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.