Takes vector
params as vectors=0,0|0,1|1,1|1,0
in an order=01,2,3
and draws them in three dimensions.
A Pen by HARUN PEHLİVAN on CodePen.
console.clear(); | |
const COLORS = [ | |
'hsl(4, 90%, 58%)', 'hsl(291, 64%, 42%)', 'hsl(207, 90%, 54%)', | |
'hsl(122, 39%, 49%)', 'hsl(54, 100%, 62%)', 'hsl(36, 100%, 50%)', | |
'hsl(16, 25%, 38%)' | |
]; | |
const PI = Math.PI; | |
const PI2 = PI * 2; | |
class Visualization { | |
constructor({ plots, tour }) { | |
this.setMaxes(plots); | |
this.scale = 0.25; | |
this.scene = new THREE.Scene(); | |
this.w = window.innerWidth; | |
this.h = window.innerHeight; | |
this.buildCamera(); | |
this.buildRenderer(); | |
this.buildLighting(); | |
this.tour = tour; | |
this.createPlots(plots); | |
this.createLines(); | |
this.createCone(); | |
this.bindResize(); | |
this.rotation = 0; | |
this.animate(); | |
} | |
setMaxes(plots) { | |
this.xmax = 0; | |
this.xmin = Infinity; | |
this.ymax = 0; | |
this.ymin = Infinity; | |
plots.forEach((plot) => { | |
if (plot[0] > this.xmax) this.xmax = plot[0]; | |
if (plot[0] < this.xmin) this.xmin = plot[0]; | |
if (plot[1] > this.ymax) this.ymax = plot[1]; | |
if (plot[1] < this.ymin) this.ymin = plot[1]; | |
}); | |
this.fieldw = this.xmax - this.xmin; | |
this.fieldh = this.ymax - this.ymin; | |
} | |
buildCamera() { | |
this.camera = new THREE.PerspectiveCamera(40, this.w/this.h, 0.1, 1000); | |
this.camera.position.y = this.scale * 2; | |
} | |
buildRenderer() { | |
this.renderer = new THREE.WebGLRenderer(); | |
this.renderer.setSize(this.w, this.h); | |
document.body.appendChild(this.renderer.domElement); | |
this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement); | |
} | |
buildLighting() { | |
var lights = []; | |
lights[0] = new THREE.PointLight( '#999', 1, 0 ); | |
lights[1] = new THREE.PointLight( '#999', 1, 0 ); | |
lights[2] = new THREE.PointLight( '#999', 1, 0 ); | |
lights[0].position.set( 0, 200, 0 ); | |
lights[1].position.set( 100, 200, 100 ); | |
lights[2].position.set( - 100, - 200, - 100 ); | |
this.scene.add(lights[0]); | |
this.scene.add(lights[1]); | |
this.scene.add(lights[2]); | |
} | |
createCone() { | |
for (let i = 0; i <= this.scale; i += 0.01) { | |
let r = (i - 0.01) * 1.1; | |
var geometry = new THREE.CylinderGeometry(r, r, 0.005, 32); | |
var material = new THREE.MeshPhongMaterial({ color: '#444', specular: '#333', transparent: true, opacity: 0.4 }); | |
var cylinder = new THREE.Mesh(geometry, material); | |
cylinder.position.x = this.centerx; | |
cylinder.position.y = this.scale - i; | |
cylinder.position.z = this.centerz; | |
this.scene.add(cylinder); | |
} | |
} | |
createPlots(points) { | |
this.plots = [] | |
let tour = this.tour; | |
let cx = 0, cy = 0; | |
let maxDist = 0; | |
let tourPoints = tour.map((idx) => { | |
let point = points[idx]; | |
cx += point[0], cy += point[1]; | |
return point; | |
}); | |
cx /= tour.length, cy /= tour.length; | |
tour.forEach((idx) => { | |
let point = points[idx]; | |
let dist = Math.hypot(cx - point[0], cy - point[1]); | |
if (dist > maxDist) maxDist = dist; | |
}) | |
let maxSize = Math.max(this.fieldw, this.fieldh); | |
let offx = (maxSize - this.fieldw) / 2; | |
let offy = (maxSize - this.fieldh) / 2; | |
this.maxDist = maxDist; | |
let px = cx - this.xmin; | |
let py = cy - this.ymin; | |
let x = (px + offx) / maxSize; | |
let z = (py + offy) / maxSize; | |
let y = (1 - Math.hypot(cx - px, cy - py) / maxDist) * 1; | |
x = (x * 2 - 1) * this.scale; | |
z = (z * 2 - 1) * this.scale; | |
y = this.scale; | |
this.centerx = x; | |
this.centery = y; | |
this.centerz = z; | |
this.createPlot({ x, y, z, i: null, size: 0.005 }); | |
tourPoints.forEach((p, i) => { | |
let px = p[0] - this.xmin; | |
let py = p[1] - this.ymin; | |
let x = (px + offx) / maxSize; | |
let z = (py + offy) / maxSize; | |
let y = (1 - Math.hypot(cx - px, cy - py) / this.maxDist); | |
x = (x * 2 - 1) * this.scale; | |
z = (z * 2 - 1) * this.scale; | |
y = y * this.scale; | |
let touri = tour[i] | |
this.createPlot({ x, y, z, i: touri}); | |
}); | |
} | |
createPlot({ x, y, z, i, size = 0.01 }) { | |
var color = i === null ? 'white' : COLORS[i % COLORS.length]; | |
var geometry = new THREE.SphereGeometry(size); | |
var material = new THREE.MeshPhongMaterial({ | |
color, | |
specular: '#333' | |
}); | |
var sphere = new THREE.Mesh(geometry, material); | |
sphere.position.x = x; | |
sphere.position.y = y; | |
sphere.position.z = z; | |
if (i !== null) this.plots.push(sphere); | |
this.scene.add(sphere); | |
} | |
createLines() { | |
this.plots.forEach((plot, i) => { | |
let touri = this.tour[i]; | |
var color = COLORS[touri % COLORS.length]; | |
var lineMaterial = new THREE.LineBasicMaterial({ color }); | |
var lineGeometry = new THREE.Geometry(); | |
let v1 = new THREE.Vector3(plot.position.x, plot.position.y, plot.position.z); | |
let plot2 = this.plots[(i + 1) % this.plots.length] | |
let v2 = new THREE.Vector3(plot2.position.x, plot2.position.y, plot2.position.z); | |
lineGeometry.vertices.push(v1); | |
lineGeometry.vertices.push(v2); | |
var line = new THREE.Line(lineGeometry, lineMaterial); | |
this.scene.add(line); | |
}); | |
} | |
animate() { | |
requestAnimationFrame(this.animate.bind(this)); | |
this.rotation += 0.0125; | |
this.camera.position.x = Math.cos(this.rotation); | |
this.camera.position.z = Math.sin(this.rotation); | |
this.camera.lookAt({ x: this.centerx, y: this.centery * 0.25, z: this.centerz}); | |
// this.camera.lookAt(this.scene.position); | |
this.renderer.render(this.scene, this.camera); | |
} | |
bindResize() { | |
window.addEventListener('resize', () => { | |
this.w = window.innerWidth; | |
this.h = window.innerHeight; | |
this.camera.aspect = this.w / this.h; | |
this.camera.updateProjectionMatrix(); | |
this.renderer.setSize(this.w, this.h); | |
}, false); | |
} | |
} | |
function tour1() { | |
return [ | |
0,1,2,3 | |
]; | |
} | |
function plots1() { | |
return [ | |
[0,0],[1,0],[1,1],[0,1] | |
]; | |
} | |
let vectorString = window.location.search.match(/vectors=([\d\.,\|]+)/); | |
vectorString = vectorString ? vectorString[1] : '0,0|1,0|1,1|0,1'; | |
// vectorString = vectorString ? vectorString[1] : '0,0|0.4,0.125|0.6,0.125|0.5,0.45||0,1|1,0'; | |
let orderString = window.location.search.match(/order=([\d,]+)/); | |
orderString = orderString ? orderString[1] : null; | |
// orderString = orderString ? orderString[1] : '0,1,2,6,3,5'; | |
let vectors = parseVectors(vectorString); | |
let ordering = parseOrdering(orderString, vectors); | |
let app = new Visualization({ plots: vectors || plots1(), tour: ordering || tour1() }); | |
function parseVectors(string) { | |
return string.split('|').map(s => s.split(',').map((v, i) => { | |
return parseFloat(v) | |
})); | |
} | |
function parseOrdering(string, vectors) { | |
if (string) { | |
return string.split(',').map((o => parseInt(o))); | |
} else { | |
return vectors.map((v, i) => i); | |
} | |
} |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/84/three.min.js"></script> | |
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/111863/THREE-OrbitControls.js"></script> |
html, body { | |
height: 100%; | |
} | |
body { margin: 0; } | |
canvas { | |
width: 100%; | |
height: 100%; | |
} |
Takes vector
params as vectors=0,0|0,1|1,1|1,0
in an order=01,2,3
and draws them in three dimensions.
A Pen by HARUN PEHLİVAN on CodePen.