splines arc on three js globe not working - javascript

I've been following a couple of examples on mapping arcs around a three.js globe. I've nearly got this to work although I'm having trouble getting the math correct and the resulting projection is incorrect. I'd appreciate anyone taking a look at the code and telling me what I'm doing wrong. Thanks
import oHoverable from 'o-hoverable';
import d3 from 'd3';
import oHeader from 'o-header';
import THREE from 'three.js';
// var OrbitControls = require('three-orbit-controls')(THREE);
// console.log("orbitControls=",OrbitControls);
oHoverable.init(); // makes hover effects work on touch devices
document.addEventListener('DOMContentLoaded', function () {
oHoverable.init(); // makes hover effects work on touch devices
var dataset=spreadsheet.data
console.log(dataset)
// This let's you inspect the resulting image in the console.
//console.log(canvas.node().toDataURL()); //F
var startLon=45
var startLat=75
var endLon=0
var endLat=0
var posX = 200;
var posY = 600;
var posZ = 1800;
var width = document.getElementById('main').getBoundingClientRect().width;
var height = 600;
var FOV = 45;
var NEAR = 2;
var FAR = 4000;
var controls;
// some global variables and initialization code
// simple basic renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor( 0x000000, 1);
// add it to the target element
var globeDiv = document.getElementById("globeDiv");
globeDiv.appendChild(renderer.domElement);
// setup a camera that points to the center
var camera = new THREE.PerspectiveCamera(FOV,width/height,NEAR,FAR);
camera.position.set(posX,posY, posZ);
camera.lookAt(new THREE.Vector3(0,0,0));
// create a basic scene and add the camera
var scene = new THREE.Scene();
scene.add(camera);
//spotlight set up in the same location as the camera
var light = new THREE.DirectionalLight(0xffffff, 1.0, 200 );
light.position.set(posX,posY,posZ);
scene.add(light);
//Add Earth
var radious=650
var line
// var earthGeo=new THREE.SphereGeometry(radious,100,100);
// var earthMat=new THREE.MeshPhongMaterial();
// earthMat.map=THREE.ImageUtils.loadTexture("images/world.jpg");
// earthMat.bumpMap=THREE.ImageUtils.loadTexture("images/bumpmap.jpg");
// earthMat.bumpScale=8;
// earthMat.shininess=10;
// var earthObject = new THREE.Mesh(earthGeo,earthMat);
// scene.add(earthObject);
// //Add clouds
// var cloudGeo=new THREE.SphereGeometry(radious,60,60);
// var cloudsMat=new THREE.MeshPhongMaterial({
// opacity: 0.5,
// transparent: true,
// color: 0xffffff
// });
// cloudsMat.map=THREE.ImageUtils.loadTexture("images/clouds.png");
// var meshClouds = new THREE.Mesh( cloudGeo, cloudsMat );
// meshClouds.scale.set(1.015, 1.015, 1.015 );
// scene.add(meshClouds);
//Add lines
var root = new THREE.Object3D();
for (var i = 0; i < dataset.length; i++) {
endLon=dataset[i].lng;
endLat=dataset[i].lat;
makeCurve(startLat,startLon,endLat,endLon);
root.add(line);
};
scene.add(root)
function makeCurve(startLon,startLat,endLon,endLat){
console.log("makeCurve",startLon,startLat,endLon,endLat);
var phiFrom = startLon * Math.PI / 180;
var thetaFrom = (startLat+90) * Math.PI / 180;
//calculates "from" point
var xF = radious * Math.cos(phiFrom) * Math.sin(thetaFrom);
var yF = radious * Math.sin(phiFrom);
var zF = radious * Math.cos(phiFrom) * Math.cos(thetaFrom);
phiFrom = endLon * Math.PI / 180;
thetaFrom = (endLat+90) * Math.PI / 180;
//calculates "from" point
var xT = radious * Math.cos(phiFrom) * Math.sin(thetaFrom);
var yT = radious * Math.sin(phiFrom);
var zT = radious * Math.cos(phiFrom) * Math.cos(thetaFrom);
//Sets up vectors
var vF = new THREE.Vector3(xF, yF, zF);
var vT = new THREE.Vector3(xT, yT, zT);
var dist = vF.distanceTo(vT);
// here we are creating the control points for the first ones.
// the 'c' in front stands for control.
var cvT = vT.clone();
var cvF = vF.clone();
// get the half point of the vectors points.
var xC = ( 0.5 * (vF.x + vT.x) );
var yC = ( 0.5 * (vF.y + vT.y) );
var zC = ( 0.5 * (vF.z + vT.z) );
// then we create a vector for the midpoints.
var subdivisions = 100;
var geometry = new THREE.Geometry();
var curve = new THREE.QuadraticBezierCurve3();
curve.v0 = new THREE.Vector3(xF, yF, zF);
curve.v1 = new THREE.Vector3(xT, yT, zT);
curve.v2 = new THREE.Vector3(xC, yC, zC);
for (var i = 0; i < subdivisions; i++) {
geometry.vertices.push( curve.getPoint(i / subdivisions) )
}
var material = new THREE.LineBasicMaterial( { color: 0xf2c0a4, linewidth: 2 } );
line = new THREE.Line(geometry, material);
}
// controls = new THREE.OrbitControls( camera );
render();
function render() {
var timer = Date.now() * 0.0001;
// camera.position.x=(Math.cos(timer)*1800);
// camera.position.z=(Math.sin(timer)*1800);
camera.lookAt( scene.position );
// light.position.x = (Math.cos(timer)*2000);
// light.position.z = (Math.sin(timer)*2000);
light.lookAt(scene.position);
//earthObject.rotation.y += 0.0014;
root.rotation.y += 0.0014;
//meshClouds.rotation.y += 0.001;
renderer.render(scene,camera);
requestAnimationFrame(render );
}
});
data sample is as follows and is loaded elsewhere
ftlabel,imfcode,lat,lng
Afghanistan,512,33,66
Albania,914,41,20
Algeria,612,28,3
Angola,614,-12.5,18.5
Argentina,213,-34,-64
Armenia,911,40,45
Aruba,314,12.5,-69.97
Australia,193,-25,135
Austria,122,47.33,13.33
Azerbaijan,912,40.5,47.5
Bahamas,313,24,-76
Bahrain,419,26,50.5
Bangladesh,513,24,90
Barbados,316,13.17,-59.53
Belarus,913,53,28
Belgium,124,50.83,4
Belize,339,17.25,-88.75
Benin,638,9.5,2.25
Bermuda,319,32.33,-64.75
Bolivia,218,-17,-65
Bosnia and Herzegovina,963,44.25,17.83
Brazil,223,-10,-55
Brunei,516,4.5,114.67
Bulgaria,918,43,25
Burkina Faso,748,13,-2
Burundi,618,-3.5,30
Cambodia,522,13,105
Cameroon,622,6,12
Canada,156,60,-96
Cape Verde,624,16,-24
Central African Republic,626,7,21
Chad,628,15,19
Chile,228,-30,-71
China,924,35,105
Colombia,233,4,-72
Comoros,632,-12.17,44.25
Costa Rica,238,10,-84
Cote d'Ivoire,662,8,-5
Croatia,960,45.17,15.5
Cuba,928,22,-79.5

Cleaned up the maths a bit but basically I was passing the lon and lat to the function which was receiving them the wrong way round. Code now working and looks like this
import oHoverable from 'o-hoverable';
import d3 from 'd3';
import oHeader from 'o-header';
import THREE from 'three.js';
oHoverable.init(); // makes hover effects work on touch devices
document.addEventListener('DOMContentLoaded', function () {
oHoverable.init(); // makes hover effects work on touch devices
var dataset=spreadsheet.data
//console.log(dataset)
var startLat=38
var startLon=-100
var endLon=0
var endLat=0
var posX = 200;
var posY = 600;
var posZ = 1800;
var width = document.getElementById('main').getBoundingClientRect().width;
var height = 600;
var FOV = 45;
var NEAR = 2;
var FAR = 4000;
var controls;
// some global variables and initialization code
// simple basic renderer
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width,height);
renderer.setClearColor( 0x000000, 1);
// add it to the target element
var globeDiv = document.getElementById("globeDiv");
globeDiv.appendChild(renderer.domElement);
// setup a camera that points to the center
var camera = new THREE.PerspectiveCamera(FOV,width/height,NEAR,FAR);
camera.position.set(posX,posY, posZ);
camera.lookAt(new THREE.Vector3(0,0,0));
// create a basic scene and add the camera
var scene = new THREE.Scene();
scene.add(camera);
//spotlight set up in the same location as the camera
var light = new THREE.DirectionalLight(0xffffff, 1.0, 200 );
light.position.set(posX,posY,posZ);
scene.add(light);
//Add Earth
var radius=600
var curveObject
var earthGeo=new THREE.SphereGeometry(radius,100,100);
var earthMat=new THREE.MeshPhongMaterial();
earthMat.map=THREE.ImageUtils.loadTexture("images/world.jpg");
earthMat.bumpMap=THREE.ImageUtils.loadTexture("images/bumpmap.jpg");
earthMat.bumpScale=8;
earthMat.shininess=10;
var earthObject = new THREE.Mesh(earthGeo,earthMat);
scene.add(earthObject);
//Add clouds
var cloudGeo=new THREE.SphereGeometry(radius,60,60);
var cloudsMat=new THREE.MeshPhongMaterial({
opacity: 0.5,
transparent: true,
color: 0xffffff
});
cloudsMat.map=THREE.ImageUtils.loadTexture("images/clouds.png");
var meshClouds = new THREE.Mesh( cloudGeo, cloudsMat );
meshClouds.scale.set(1.015, 1.015, 1.015 );
scene.add(meshClouds);
//Add lines
var lineObject = new THREE.Object3D();
for (var i = 0; i < dataset.length; i++) {
endLon=dataset[i].lng;
endLat=dataset[i].lat;
makeCurve(startLon,startLat,endLon,endLat,i);
lineObject.add(curveObject);
};
scene.add(lineObject)
// takes lon lat and a radius and turns that into vector
function to3DVector(lon, lat, radius) {
var phi = lat * Math.PI / 180;
var theta = (lon + 90) * Math.PI / 180;
var xF = radius * Math.cos(phi) * Math.sin(theta);
var yF = radius * Math.sin(phi);
var zF = radius * Math.cos(phi) * Math.cos(theta);
return new THREE.Vector3(xF, yF, zF);
}
function makeCurve(startLon,startLat,endLon,endLat,i){
var widthScale = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) { return d.imfcode; })])
.range([1, 12]);
var vF = to3DVector(startLon, startLat, radius);
var vT = to3DVector(endLon, endLat, radius);
// then you get the half point of the vectors points.
var xC = ( 0.5 * (vF.x + vT.x) );
var yC = ( 0.5 * (vF.y + vT.y) );
var zC = ( 0.5 * (vF.z + vT.z) );
// then we create a vector for the midpoints.
var mid = new THREE.Vector3(xC, yC, zC);
var dist = vF.distanceTo(vT);
// here we are creating the control points for the first ones.
// the 'c' in front stands for control.
var cvT = vT.clone();
var cvF = vF.clone();
var smoothDist = map(dist, 0, 10, 0, 15/dist );
console.log(smoothDist);
mid.setLength( radius * smoothDist );
cvT.add(mid);
cvF.add(mid);
cvT.setLength( radius * smoothDist );
cvF.setLength( radius * smoothDist );
var curve = new THREE.CubicBezierCurve3( vF, cvF, cvT, vT );
var geometry2 = new THREE.Geometry();
geometry2.vertices = curve.getPoints( 50 );
var material2 = new THREE.LineBasicMaterial( { transparent: true,opacity :0.6, color : 0xff0000,linewidth:widthScale(dataset[i].imfcode)} );
// Create the final Object3d to add to the scene
curveObject = new THREE.Line( geometry2, material2 );
function map(value, low1, high1, low2, high2) {
return low2 + (high2 - low2) * (value - low1) / (high1 - low1);
}
}
render();
function render() {
var timer = Date.now() * 0.0001;
// camera.position.x=(Math.cos(timer)*1800);
// camera.position.z=(Math.sin(timer)*1800);
camera.lookAt( scene.position );
// light.position.x = (Math.cos(timer)*2000);
// light.position.z = (Math.sin(timer)*2000);
light.lookAt(scene.position);
earthObject.rotation.y += 0.0005;
lineObject.rotation.y += 0.0005;
meshClouds.rotation.y += 0.0012;
renderer.render(scene,camera);
requestAnimationFrame(render );
}
});

Related

ThreeJS: How to move background only in one direction?

I have a fully working model of a car mesh moving right, left, up and down using Three.JS
The input data are acceleration on the X and Z direction, we apply double integration on the acceleration to compute the displacement. So I have been able to animate the car in all directions and making the background move to keep the car in the center of the canvas. However, I only need to background to move in the right and left direction, and not for the up and down
That's my code:
<html>
<head>
<style>
canvas {
position: fixed;
top: 0;
left: 0;
}
</style>
</head>
<body>
<script src="./libs/three.min.js"></script>
<script src="./libs/OrbitControls.js"></script>
<script src="./libs/KeyboardState.js"></script>
<script src="./libs/MTLLoader.js"></script>
<script src="./libs/OBJMTLLoader.js"></script>
<script src="./data/accXaccZCOMBINEDMOTIONS.json"></script>
</body>
<script>
var data = JSON.parse(JSON.stringify(data));
var toycar;
var valid = 1;
const dispArrayX = Array.from({ length: 1 }).fill(0);
const dispArrayZ = Array.from({ length: 1 }).fill(0);
let sensorValue = 0;
var initialVelocity = 0;
var angle = 0;
var currentIndex = 0;
var initialDisplacement = 0;
var scene, renderer, camera;
var width = window.innerWidth;
var height = window.innerHeight;
var time = data[currentIndex].time
var pos = new THREE.Vector3(0, 0, 0);
init();
animate();
function init() {
var width = window.innerWidth;
var height = window.innerHeight;
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setClearColor(0x626d73, 1);
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(10, width / height, 1, 10000);
// camera.position.y = -150;
// camera.position.y = 200;
// camera.position.x = 100;
camera.lookAt(new THREE.Vector3(0, 0, 0));
var loader = new THREE.OBJMTLLoader();
loader.load('https://jyunming-chen.github.io/tutsplus/models/toycar.obj', 'https://jyunming-chen.github.io/tutsplus/models/toycar.mtl',
function (object) {
toycar = object;
toycar.rotateZ(10.8); //toycar.rotateZ(-10.99);
scene.add(toycar);
});
var gridXZ = new THREE.GridHelper(100000, 10);
gridXZ.setColors(new THREE.Color(0xff0000), new THREE.Color(0xffffff));
scene.add(gridXZ);
var pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(350, 10, 5);
scene.add(pointLight);
var ambientLight = new THREE.AmbientLight(0x111111);
scene.add(ambientLight);
}
function animate(dt) {
let time = data[currentIndex].time
dt *= 10 ** -9
time += dt
while (data[currentIndex].time < time) {
currentIndex++
if (currentIndex >= data.length) return
}
const { accX,accZ } = data[currentIndex]
var dir = new THREE.Vector3(0, 0, -1);
dir.applyAxisAngle(new THREE.Vector3(0, 0, 0), 10);
pos.add(dir);
if (toycar != undefined) {
toycar.scale.set(0.1, 0.1, 0.1);
if (currentIndex > 0) {
// compute the displacement by double integrating the acceleration for accZ (e.i. Right and left movement)
const deltaTime = ((data[currentIndex].time) / (24 * 60 * 60)) - 1; // convert time from second per day to second
const velocityInitialZ = ((data[currentIndex-3].accZ + data[currentIndex-2].accZ)/2)*deltaTime; // compute initialVelocity two step backward
const velocityCurrentZ = velocityInitialZ + ((data[currentIndex-1].accZ + data[currentIndex].accZ)/2)*deltaTime; // compute currentVelocity one step backward
const previousDispZ = dispArrayZ[0] + (deltaTime * ((velocityCurrentZ + velocityInitialZ)/2));
dispArrayZ[0] = previousDispZ;
const dispCurrentZ = previousDispZ + dispArrayZ[0];
// compute the displacement by double integrating the acceleration for accX (e.i. Up and down movement)
const velocityInitialX = ((data[currentIndex-3].accX + data[currentIndex-2].accX)/2)*deltaTime; // compute initialVelocity two step backward
const velocityCurrentX = velocityInitialX + ((data[currentIndex-1].accX + data[currentIndex].accX)/2)*deltaTime; // compute currentVelocity one step backward
const previousDispX = dispArrayX[0] + (deltaTime * ((velocityCurrentX + velocityInitialX)/2));
dispArrayX[0] = previousDispX;
const dispCurrentX = previousDispX + dispArrayX[0];
const dispCurrentXscaled = dispCurrentX/3500;
// Move the car up and down
if (dispCurrentXscaled*0.0001 < 0){
toycar.position.x = dispCurrentXscaled*0.00001;
}
else if (dispCurrentXscaled*0.0001 > 8){
toycar.position.x = dispCurrentXscaled*0.0000001;
}
else{
toycar.position.x = dispCurrentXscaled*0.0001;
}
toycar.position.y = 0;
// Move the car right and left
toycar.position.z = -(dispCurrentZ/4000)*0.0005;
// print out displacementX and displacementZ
console.log("DispX: " + (dispCurrentX*0.0000001).toFixed(5) + " DispZ: " + ((dispCurrentZ/4000)*0.0005).toFixed(5));
}
toycar.rotation.x = (angle + Math.PI);;
var relativeCameraOffset = new THREE.Vector3(-1600, 400, 0);
var cameraOffset = relativeCameraOffset.applyMatrix4(toycar.matrixWorld);
camera.position.x = cameraOffset.x*0.5;
camera.position.y = cameraOffset.y;
camera.position.z = cameraOffset.z;
camera.lookAt(toycar.position);
}
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
</script>
</html>
I am using from a JSON file as input.
That's how the movement of the car looks like:
You can see that the background is following the car motion. For Up and Down I only need the car to move (not the background) and the right and left are fine because is moving so it stays within the canvas. Can you please help with that?

How to remove jitter/shaking in my special application of OrbitControl and PerspectiveCamera?

In the live snippet below (also at https://jsfiddle.net/gpolyn/bpo7t7f6), I offer optional dynamic updating of PerspectiveCamera's lookAt parameter and fov in the three.js render cycle. (My goal is to fill as much of the viewport as possible with the subject cube, through all orbit positions.)
I suspect that lack of precision in the matrix math code used to calculate my dynamic fov and lookAt paramters (three.js uses Float32 arrays) causes the jitter/shaking I notice in the cube when orbiting with the dynamic options selected from the controls.
(The matrix operations can be found in the snippet addExtrema function.)
In my demo, my highest goal is to remove jitter/shaking in case 1, described here:
the "dynamicFovAndLookAt" control option uses dynamic fov and lookAt updating causing quite a bit of jitter in the cube, no matter the orbit position; the fov and lookAt parameters can be seen fluctuating in the demo's lower right status box;
"dynamicFov" uses dynamic fov updating causing some jitter in the cube, depending on orbiting; the fov parameter in the lower right status box will vary due to the dynamic recalculation;
the "boundingSphere" option uses no dynamic fov, lookAt updating and the cube exhibits no jitter/shake through orbiting – the fov and lookAt parameters are constant in the lower right status box.
(Orbit position is reported in the lower left corner of the demo, while one of the box corners has a green dot to aid any discussion of the jitter effect.)
var renderer, scene, camera, controls;
var object;
var vertices3;
var cloud;
var boxToBufferAlphaMapping = {
0: 0,
2: 1,
1: 2,
3: 4,
6: 7,
7: 10,
5: 8,
4: 6
}
var lastAlphas = [];
var canvasWidth, canvasHeight;
var windowMatrix;
var boundingSphere;
var figure;
var fovWidth, fovDistance, fovHeight;
var newFov, newLookAt;
var dist, height, fov, lookAt;
var aspect;
var CONSTANT_FOR_FOV_CALC = 180 / Math.PI;
var mat3;
var CORNERS = 8;
var ndc = new Array(CORNERS);
var USE_GREEN_DOTS = false;
var stats, orbitPosition, cameraFacts;
var useDynamicFov;
var datGuiData = {};
init();
render();
afterInit();
animate();
function init() {
mat3 = new THREE.Matrix4();
canvasWidth = window.innerWidth;
canvasHeight = window.innerHeight;
aspect = canvasWidth / canvasHeight;
// renderer
<!-- renderer = new THREE.WebGLRenderer({antialias:true, logarithmicDepthBuffer:true}); -->
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(canvasWidth, canvasHeight);
document.body.appendChild(renderer.domElement);
// scene
scene = new THREE.Scene();
// object
var geometry = new THREE.BoxGeometry(4, 4, 6);
// too lazy to add edges without EdgesHelper...
var material = new THREE.MeshBasicMaterial({
transparent: true,
opacity: 0
});
var cube = new THREE.Mesh(geometry, material);
object = cube;
// bounding sphere used for orbiting control in render
object.geometry.computeBoundingSphere();
boundingSphere = object.geometry.boundingSphere;
cube.position.set(2, 2, 3)
// awkward, but couldn't transfer cube position to sphere...
boundingSphere.translate(new THREE.Vector3(2, 2, 3));
// save vertices for subsequent use
vertices = cube.geometry.vertices;
var edges = new THREE.EdgesHelper(cube)
scene.add(edges);
scene.add(cube);
<!-- if (USE_GREEN_DOTS) addGreenDotsToScene(geometry); -->
addGreenDotsToScene(geometry);
// camera
<!-- camera = new THREE.PerspectiveCamera( 17, window.innerWidth / window.innerHeight, 20, 40 ); -->
camera = new THREE.PerspectiveCamera(17, window.innerWidth / window.innerHeight);
camera.position.set(20, 20, 20);
// controls
controls = new THREE.OrbitControls(camera);
controls.maxPolarAngle = 0.5 * Math.PI;
controls.minAzimuthAngle = 0;
controls.maxAzimuthAngle = 0.5 * Math.PI;
controls.enableZoom = false;
// performance monitor
stats = new Stats();
document.body.appendChild(stats.dom);
// orbitposition tracker
orbitPosition = new THREEg.OrbitReporter()
orbitPosition.domElement.style.position = 'absolute'
orbitPosition.domElement.style.left = '0px'
orbitPosition.domElement.style.bottom = '0px'
document.body.appendChild(orbitPosition.domElement)
// camera facts
cameraFacts = new THREEg.CameraReporter()
cameraFacts.domElement.style.position = 'absolute'
cameraFacts.domElement.style.right = '0px'
cameraFacts.domElement.style.bottom = '0px'
document.body.appendChild(cameraFacts.domElement)
// ambient
scene.add(new THREE.AmbientLight(0x222222));
// axes
scene.add(new THREE.AxisHelper(20));
// initial settings
dist = boundingSphere.distanceToPoint(camera.position);
height = boundingSphere.radius * 2;
fov = 2 * Math.atan(height / (2 * dist)) * (CONSTANT_FOR_FOV_CALC);
newFov = fov;
lookAt = new THREE.Vector3(2, 2, 3); // center of box
newLookAt = lookAt;
// dat.gui
window.onload = function() {
var view = datGuiData;
view.boundingSphere = true;
view.dynamicFov = false;
view.dynamicFovAndLookAt = false;
var gui = new dat.GUI();
var CB1Controller = gui.add(view, 'boundingSphere').listen();
CB1Controller.onChange(function(value) {
view.boundingSphere = true;
view.dynamicFov = false;
view.dynamicFovAndLookAt = false;
});
var CB2Controller = gui.add(view, 'dynamicFov').listen();
CB2Controller.onChange(function(value) {
view.boundingSphere = false;
view.dynamicFov = true;
view.dynamicFovAndLookAt = false;
});
var CB3Controller = gui.add(view, 'dynamicFovAndLookAt').listen();
CB3Controller.onChange(function(value) {
view.boundingSphere = false;
view.dynamicFov = true;
view.dynamicFovAndLookAt = true;
});
};
}
function addExtrema() {
// thread A
mat3.multiplyMatrices(camera.matrixWorld, mat3.getInverse(camera.projectionMatrix));
// thread B
var scratchVar;
var topIdx, bottomIdx, leftIdx, rightIdx;
var top = Number.NEGATIVE_INFINITY;
var bottom = Number.POSITIVE_INFINITY;
var right = Number.NEGATIVE_INFINITY;
var left = Number.POSITIVE_INFINITY;
var closestVertex, closestVertexDistance = Number.POSITIVE_INFINITY;
var vtx;
for (var i = 0; i < CORNERS; i++) {
scratchVar = vertices3[i].clone().applyMatrix4(camera.matrixWorldInverse);
scratchVar.applyMatrix4(camera.projectionMatrix);
scratchVar.divideScalar(scratchVar.w)
ndc[i] = scratchVar;
vtx = ndc[i];
if (vtx.x < left) {
left = vtx.x;
leftIdx = i;
} else if (vtx.x > right) {
right = vtx.x;
rightIdx = i;
}
if (vtx.y < bottom) {
bottom = vtx.y;
bottomIdx = i;
} else if (vtx.y > top) {
top = vtx.y;
topIdx = i;
}
if (vtx.z < closestVertexDistance) {
closestVertex = i;
closestVertexDistance = vtx.z;
}
}
var yNDCPercentCoverage = (Math.abs(ndc[topIdx].y) + Math.abs(ndc[bottomIdx].y)) / 2;
yNDCPercentCoverage = Math.min(1, yNDCPercentCoverage);
var xNDCPercentCoverage = (Math.abs(ndc[leftIdx].x) + Math.abs(ndc[rightIdx].x)) / 2;
xNDCPercentCoverage = Math.min(1, xNDCPercentCoverage);
var ulCoords = [ndc[leftIdx].x, ndc[topIdx].y, closestVertexDistance, 1]
var blCoords = [ndc[leftIdx].x, ndc[bottomIdx].y, closestVertexDistance, 1]
var urCoords = [ndc[rightIdx].x, ndc[topIdx].y, closestVertexDistance, 1]
var ul = new THREE.Vector4().fromArray(ulCoords);
ul.applyMatrix4(mat3).divideScalar(ul.w);
var bl = new THREE.Vector4().fromArray(blCoords);
bl.applyMatrix4(mat3).divideScalar(bl.w);
var ur = new THREE.Vector4().fromArray(urCoords);
ur.applyMatrix4(mat3).divideScalar(ur.w);
var center = new THREE.Vector3();
center.addVectors(ur, bl);
center.divideScalar(2);
var dist = camera.position.distanceTo(center);
var upperLeft = new THREE.Vector3().fromArray(ul.toArray().slice(0, 3));
var p;
if ((1 - yNDCPercentCoverage) < (1 - xNDCPercentCoverage)) { // height case
var bottomLeft = new THREE.Vector3().fromArray(bl.toArray().slice(0, 3));
var height = upperLeft.distanceTo(bottomLeft);
p = 2 * Math.atan(height / (2 * dist)) * (CONSTANT_FOR_FOV_CALC);
} else { // width case
var upperRight = new THREE.Vector3().fromArray(ur.toArray().slice(0, 3));
var width = upperRight.distanceTo(upperLeft);
p = 2 * Math.atan((width / aspect) / (2 * dist)) * (CONSTANT_FOR_FOV_CALC);
}
if (datGuiData.dynamicFovAndLookAt || datGuiData.dynamicFov) {
newFov = p;
} else {
dist = boundingSphere.distanceToPoint(camera.position);
height = boundingSphere.radius * 2;
newFov = 2 * Math.atan(height / (2 * dist)) * (CONSTANT_FOR_FOV_CALC);
}
if (datGuiData.dynamicFovAndLookAt == true) {
newLookAt = center;
} else {
newLookAt = lookAt;
}
if (USE_GREEN_DOTS) {
var alphas = cloud.geometry.attributes.alpha;
// make last points invisible
lastAlphas.forEach(function(alphaIndex) {
alphas.array[alphaIndex] = 0.0;
});
// now, make new points visible...
// (boxToBufferAlphaMapping is a BufferGeometry-Object3D geometry
// map between the object and green dots)
alphas.array[boxToBufferAlphaMapping[rightIdx]] = 1.0;
alphas.array[boxToBufferAlphaMapping[bottomIdx]] = 1.0;
alphas.array[boxToBufferAlphaMapping[topIdx]] = 1.0;
alphas.array[boxToBufferAlphaMapping[leftIdx]] = 1.0;
// store visible points for next cycle
lastAlphas = [boxToBufferAlphaMapping[rightIdx]];
lastAlphas.push(boxToBufferAlphaMapping[bottomIdx])
lastAlphas.push(boxToBufferAlphaMapping[topIdx])
lastAlphas.push(boxToBufferAlphaMapping[leftIdx])
alphas.needsUpdate = true;
}
}
function addGreenDotsToScene(geometry) {
var bg = new THREE.BufferGeometry();
bg.fromGeometry(geometry);
bg.translate(2, 2, 3); // yucky, and quick
var numVertices = bg.attributes.position.count;
var alphas = new Float32Array(numVertices * 1); // 1 values per vertex
<!-- for( var i = 0; i < numVertices; i ++ ) { -->
<!-- alphas[ i ] = 1; -->
<!-- } -->
alphas[2] = 1;
bg.addAttribute('alpha', new THREE.BufferAttribute(alphas, 1));
var uniforms = {
color: {
type: "c",
value: new THREE.Color(0x00ff00)
},
};
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexshader').textContent,
fragmentShader: document.getElementById('fragmentshader').textContent,
transparent: true
});
cloud = new THREE.Points(bg, shaderMaterial);
scene.add(cloud);
}
function afterInit() {
windowMatrix = new THREE.Matrix4();
windowMatrix.set(canvasWidth / 2, 0, 0, canvasWidth / 2, 0, canvasHeight / 2, 0, canvasHeight / 2, 0, 0, 0.5, 0.5, 0, 0, 0, 1);
var vertices2 = object.geometry.vertices.map(function(vtx) {
return (new THREE.Vector4(vtx.x, vtx.y, vtx.z));
});
// create 'world-space' geometry points, using
// model ('world') matrix
vertices3 = vertices2.map(function(vt) {
return vt.applyMatrix4(object.matrixWorld);
})
}
function render() {
<!-- console.log({far: camera.far, camera_near: camera.near}) -->
camera.lookAt(newLookAt);
camera.fov = newFov;
camera.updateProjectionMatrix();
renderer.render(scene, camera);
}
function animate() {
requestAnimationFrame(animate);
render();
addExtrema()
stats.update();
orbitPosition.update(controls);
cameraFacts.update(camera, newLookAt);
}
body {
background-color: #000;
margin: 0px;
overflow: hidden;
}
.dg .c {
width: 40%
}
.dg .property-name {
width: 60%
}
<script src="https://rawgit.com/mrdoob/three.js/dev/build/three.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/libs/stats.min.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
<script src="https://rawgit.com/gpolyn/789d63a662c1768320756f68a6099f15/raw/3a0f323bb284b09e624a11f93ff4055e23adea80/OrbitReporter.js"></script>
<script src="https://rawgit.com/gpolyn/70352cb34c7900ed2489400d4ecc45f7/raw/7b6e7e6bb3e175d4145879ef1afdeb38c31cf785/camera_reporter.js"></script>
<script src="https://rawgit.com/mrdoob/three.js/master/examples/js/libs/dat.gui.min.js"></script>
<script type="x-shader/x-vertex" id="vertexshader">
attribute float alpha; varying float vAlpha; void main() { vAlpha = alpha; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); gl_PointSize = 8.0; gl_Position = projectionMatrix * mvPosition; }
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color; varying float vAlpha; void main() { gl_FragColor = vec4( color, vAlpha ); }
</script>

Planes trimmed by mesh three.js

I have a mesh, which should represent a "building" and I want to add planes every 3 units (different floor levels) and trim them with the mesh faces, so they appear only inside the mesh.
How do I do that? I can't figure it out and the mesh is quite complex for me to define the plane to normally cover only the inside.
var program = new Program(reset, step)
function reset() {
scene.clear()
scene.add(new THREE.GridHelper(100, 1))
}
function step() {
}
program.startup()
// the points group
var spGroup;
// the mesh
var hullMesh;
generatePoints();
render();
function generatePoints() {
// add 10 random spheres
var points = [];
for (var i = 0; i < 50; i++) {
var x = Math.random() * (80 - 1) + 1 //Math.random() * (max - min) + min
var y = Math.random() * (80 - 1) + 1
var z = Math.random() * (80 - 1) + 1
points.push(new THREE.Vector3(x, y, z));
}
spGroup = new THREE.Object3D();
var material = new THREE.MeshBasicMaterial({
color: 0xff0000,
transparent: false
});
points.forEach(function(point) {
var spGeom = new THREE.SphereGeometry(0.5);
var spMesh = new THREE.Mesh(spGeom, material);
spMesh.position.copy(point);
spGroup.add(spMesh);
});
// add the points as a group to the scene
scene.add(spGroup);
// use the same points to create a convexgeometry
var hullGeometry = new THREE.ConvexGeometry(points);
hullMesh = createMesh(hullGeometry);
scene.add(hullMesh);
}
function createMesh(geom) {
// assign two materials
var meshMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
transparent: true,
opacity: 0.2
});
meshMaterial.side = THREE.DoubleSide;
var wireFrameMat = new THREE.MeshBasicMaterial();
wireFrameMat.wireframe = true;
// create a multimaterial
var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, [meshMaterial, wireFrameMat]);
return mesh;
}

Three.js render white part of plain geometry

I'm trying to turn this plainGeometry, into this (sort of mask).
This code works: (to sum-up, create two materials, divide the plane to segments and decide for each one which material is it with MeshFaceMaterial)
Button.onClick(function () {
var obj = editor.selected;
var material = obj.material;
var tex = material.map;
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, objWidth, objHeight);
var facesNum = objHeight * objWidth * 2;
var facesX = objWidth * 2;
var facesInX = texWidth * 2;
var materials = [];
materials.push(material);
materials.push(new THREE.MeshBasicMaterial({ }));
for (var i = 0; i < facesNum; i++) {
if ((i % facesX >= objWidth - texWidth) &&
(i % facesX <= (facesInX + objWidth - texWidth - 1)) &&
(i <= (texHeight * objWidth * 2) - 1)) {
geometry.faces[i].materialIndex = 0;
}
else {
geometry.faces[i].materialIndex = 1;
}
}
obj.geometry = geometry;
obj.material = new THREE.MeshFaceMaterial(materials);
editor.signals.materialChanged.dispatch(obj);
});
But I'm wondering if there is a simpler way to go. Any suggestions?
Another way to do this is to use an alpha channel on your texture. You can do this with Gimp or Photoshop.
You then duplicate the mesh and push it just a tad out the axis with polygonOffsetFactor on the material. Apply the background material to the first mesh, and the foreground material with the texture with alpha to the second.
See this fiddle alphaTest. (you may need to disable cross-domain access security so the texture can load in this fiddle, chrome will allow this if you run it with the --disable-web-security flag)
The advantage to this method is that the image can be of any shape and location, and doesn't need to fit into a geometry face.
Another way, if the geometry you were using is complicated, is to use Three.DecalGeometry to cut a mesh piece out and use it shifted a bit with polygonOffsetFactor on the material. See the three.js decals example for this.
Source for the example fiddle is below:
var renderer;
var pointLight;
var scene;
var plane1;
var plane2;
function addPlane()
{
var material1 = new THREE.MeshPhongMaterial({ color: 0xFFFFFF });
var material2;
var loader = new THREE.TextureLoader();
loader.load('http://i.imgur.com/ETdl4De.png',
function ( texture ) {
var material2 = new THREE.MeshPhongMaterial({
color: 0xFFFFFF,
map: texture,
alphaTest: 0.7,
polygonOffset: true,
polygonOffsetFactor: - 4,
});
var geometry1 = new THREE.PlaneGeometry(12, 12, 12, 12);
var geometry2 = geometry1.clone();
plane1 = new THREE.Mesh(geometry1,material1);
plane2 = new THREE.Mesh(geometry2,material2);
scene.add(plane2);
scene.add(plane1);
}
);
}
(function() {
'use strict';
scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.01, 10000);
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
addPlane();
camera.position.z = 52;
pointLight = new THREE.DirectionalLight(0xffffff,1);
pointLight.position.x = 11;
pointLight.position.y = 5;
pointLight.position.z = 25;
scene.add(pointLight);
var reqAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame;
var render = function() {
reqAnimFrame(render);
renderer.render(scene, camera);
};
render();
}());
this did it, eventually:
var obj = editor.selected;
var tex = obj.material.map.;
var materials = [];
materials.push(new THREE.MeshBasicMaterial({ map: tex }));
materials.push(new THREE.MeshBasicMaterial({}));
var material = new THREE.MeshFaceMaterial(materials);
var objHeight = obj.geometry.parameters.height;
var objWidth = obj.geometry.parameters.width;
var texHeight = tex.image.height;
var texWidth = tex.image.width;
tex.repeat = new THREE.Vector2(3, 3);
tex.offset = new THREE.Vector2(0, 0);
var geometry = new THREE.PlaneGeometry(objWidth, objHeight, 3, 3);
var v = geometry.vertices;
var facesNum = geometry.faces.length;
v[1] = new THREE.Vector3(-texWidth / 2, objHeight / 2, 0);
v[2] = new THREE.Vector3(texWidth / 2, objHeight / 2, 0);
v[5] = new THREE.Vector3(-texWidth / 2, (objHeight / 2) - texHeight, 0);
v[6] = new THREE.Vector3(texWidth / 2, (objHeight / 2) - texHeight, 0);
v[9] = v[13];
v[10] = v[14];
v[4] = v[8] = v[12];
v[7] = v[11] = v[15];
for (var i = 0; i < facesNum; i++) {
if (i !== 2 && i !== 3) geometry.faces[i].materialIndex = 1;
}
obj.material = material;
obj.geometry = geometry;
editor.signals.materialChanged.dispatch(obj);

Draw dimension lines along with 3D cube using Three.js

Can we draw "lines" with Cube to show "Dimensions" at run time?
Here is how I have created the cube and getting dimensions from user and changing the cube at run time: http://jsfiddle.net/9Lvk61j3/
But now I want to show the Dimension, so the user knows what the length, width, and height is, which they will be changing.
This is what I am trying to make as end result:
Here is my code:
HTML:
<script src="http://www.html5canvastutorials.com/libraries/three.min.js"></script>
<div id="container"></div>
<div class="inputRow clear" id="dimensionsNotRound" data-role="tooltip">
<label class="grid-8">Dimensions (pixels):</label>
<br/>
<br/>
<div> <span>Length</span>
<input class="numeric-textbox" id="inp-length" type="text" value="100">
<br/>
<br/>
</div>
<div> <span>Width</span>
<input class="numeric-textbox" id="inp-width" type="text" value="50">
<br/>
<br/>
</div>
<div> <span>Height</span>
<input class="numeric-textbox" id="inp-height" type="text" value="40">
<br/>
<br/>
</div>
<button id="btn">Click me to change the Dimensions</button>
JS
var shape = null;
//Script for 3D Box
// revolutions per second
var angularSpeed = 0.2;
var lastTime = 0;
var cube = 0;
// this function is executed on each animation frame
function animate() {
// update
var time = (new Date()).getTime();
var timeDiff = time - lastTime;
var angleChange = angularSpeed * timeDiff * 2 * Math.PI / 1000;
//cube.rotation.y += angleChange; //Starts Rotating Object
lastTime = time;
// render
renderer.render(scene, camera);
// request new frame
requestAnimationFrame(function () {
animate();
});
}
// renderer
var container = document.getElementById("container");
var renderer = new THREE.WebGLRenderer();
renderer.setSize(container.offsetWidth, container.offsetHeight - 4);
container.appendChild(renderer.domElement);
// camera
var camera = new THREE.PerspectiveCamera(60, container.offsetWidth / container.offsetHeight, 1, 1000);
camera.position.z = 800;
// scene
var scene = new THREE.Scene();
scene.remove();
// cube
cube = new THREE.Mesh(new THREE.CubeGeometry(1, 1, 1), new THREE.MeshLambertMaterial({
color: '#cccccc'
}));
cube.overdraw = true;
cube.rotation.x = Math.PI * 0.1;
cube.rotation.y = Math.PI * 0.3;
scene.add(cube);
// add subtle ambient lighting
var ambientLight = new THREE.AmbientLight(0x319ec5);
scene.add(ambientLight);
// directional lighting
var directionalLight = new THREE.DirectionalLight(0x666666);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
shape = cube;
// start animation
animate();
var $ = function(id) { return document.getElementById(id); };
$('btn').onclick = function() {
console.log("Button Clicked");
var width = parseInt(document.getElementById('inp-width').value * 3.779528),
height = parseInt(document.getElementById('inp-height').value * 3.779528),
length = parseInt(document.getElementById('inp-length').value * 3.779528);
console.log("length " + length + " height " + height + " width " + width);
shape.scale.x = length;
shape.scale.y = height;
shape.scale.z = width;
};
Here is the Fiddle for the same! http://jsfiddle.net/9Lvk61j3/
Let me know if you need any other information.
Please suggest.
There's a bit of a problem with drawing dimensions:
You may have many of them, and not all of them may be perfectly visible:
some may be hidden,
some may appear too small, if the camera is far away from the object,
some may overlay other dimensions (or even object elements),
some may be seen from inconvenient angle.
The text should retain perfectly same size, no matter how you navigate camera,
Most of these points are addressed in my solution: https://jsfiddle.net/mmalex/j35p1fw8/
var geometry = new THREE.BoxGeometry(8.15, 0.5, 12.25);
var material = new THREE.MeshPhongMaterial({
color: 0x09f9f9,
transparent: true,
opacity: 0.75
});
var cube = new THREE.Mesh(geometry, material);
cube.geometry.computeBoundingBox ();
root.add(cube);
var bbox = cube.geometry.boundingBox;
var dim = new LinearDimension(document.body, renderer, camera);
// define start and end point of dimension
var from = new THREE.Vector3(bbox.min.x, bbox.min.y, bbox.min.z);
var to = new THREE.Vector3(bbox.max.x, bbox.min.y, bbox.max.z);
// in which direction to "extrude" dimension away from object
var direction = new THREE.Vector3(0, 0, 1);
// request LinearDimension to create threejs node
var newDimension = dim.create(from, to, direction);
// make it cube child
cube.add(newDimension);
var animate = function() {
requestAnimationFrame(animate);
// we need to reposition dimension label on each camera change
dim.update(camera);
renderer.render(scene, camera);
};
Let's see into helper classes now.
✔ Dimension line is only visible when camera angle is not too sharp (more than 45°),
class FacingCamera will let you know world plane, that is best facing to the camera. Useful to hide dimensions, which are facing camera with too sharp (acute) angle.
Separate fiddle to play with class FacingCamera can be found here: https://jsfiddle.net/mmalex/56gzn8pL/
class FacingCamera {
constructor() {
// camera looking direction will be saved here
this.dirVector = new THREE.Vector3();
// all world directions
this.dirs = [
new THREE.Vector3(+1, 0, 0),
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(0, +1, 0),
new THREE.Vector3(0, -1, 0),
new THREE.Vector3(0, 0, +1),
new THREE.Vector3(0, 0, -1)
];
// index of best facing direction will be saved here
this.facingDirs = [];
this.bestFacingDir = undefined;
// TODO: add other facing directions too
// event listeners are collected here
this.cb = {
facingDirChange: []
};
}
check(camera) {
camera.getWorldDirection(this.dirVector);
this.dirVector.negate();
var maxk = 0;
var maxdot = -1e19;
var oldFacingDirs = this.facingDirs;
var facingDirsChanged = false;
this.facingDirs = [];
for (var k = 0; k < this.dirs.length; k++) {
var dot = this.dirs[k].dot(this.dirVector);
var angle = Math.acos(dot);
if (angle > -Math.PI / 2 && angle < Math.PI / 2) {
this.facingDirs.push(k);
if (oldFacingDirs.indexOf(k) === -1) {
facingDirsChanged = true;
}
if (Math.abs(dot) > maxdot) {
maxdot = dot;
maxk = k;
}
}
}
// and if facing direction changed, notify subscribers
if (maxk !== this.bestFacingDir || facingDirsChanged) {
var prevDir = this.bestFacingDir;
this.bestFacingDir = maxk;
for (var i = 0; i < this.cb.facingDirChange.length; i++) {
this.cb.facingDirChange[i]({
before: {
facing: oldFacingDirs,
best: prevDir
},
current: {
facing: this.facingDirs,
best: this.bestFacingDir
}
}, this);
}
}
}
}
✔ Dimension text is HTML element, styled with CSS and positioned with three.js raycasting logic.
class LinearDimension creates an instance of linear dimension with arrows and text label, and controls it.
LinearDimension complete implementation:
class LinearDimension {
constructor(domRoot, renderer, camera) {
this.domRoot = domRoot;
this.renderer = renderer;
this.camera = camera;
this.cb = {
onChange: []
};
this.config = {
headLength: 0.5,
headWidth: 0.35,
units: "mm",
unitsConverter: function(v) {
return v;
}
};
}
create(p0, p1, extrude) {
this.from = p0;
this.to = p1;
this.extrude = extrude;
this.node = new THREE.Object3D();
this.hidden = undefined;
let el = document.createElement("div");
el.id = this.node.id;
el.classList.add("dim");
el.style.left = "100px";
el.style.top = "100px";
el.innerHTML = "";
this.domRoot.appendChild(el);
this.domElement = el;
this.update(this.camera);
return this.node;
}
update(camera) {
this.camera = camera;
// re-create arrow
this.node.children.length = 0;
let p0 = this.from;
let p1 = this.to;
let extrude = this.extrude;
var pmin, pmax;
if (extrude.x >= 0 && extrude.y >= 0 && extrude.z >= 0) {
pmax = new THREE.Vector3(
extrude.x + Math.max(p0.x, p1.x),
extrude.y + Math.max(p0.y, p1.y),
extrude.z + Math.max(p0.z, p1.z));
pmin = new THREE.Vector3(
extrude.x < 1e-16 ? extrude.x + Math.min(p0.x, p1.x) : pmax.x,
extrude.y < 1e-16 ? extrude.y + Math.min(p0.y, p1.y) : pmax.y,
extrude.z < 1e-16 ? extrude.z + Math.min(p0.z, p1.z) : pmax.z);
} else if (extrude.x <= 0 && extrude.y <= 0 && extrude.z <= 0) {
pmax = new THREE.Vector3(
extrude.x + Math.min(p0.x, p1.x),
extrude.y + Math.min(p0.y, p1.y),
extrude.z + Math.min(p0.z, p1.z));
pmin = new THREE.Vector3(
extrude.x > -1e-16 ? extrude.x + Math.max(p0.x, p1.x) : pmax.x,
extrude.y > -1e-16 ? extrude.y + Math.max(p0.y, p1.y) : pmax.y,
extrude.z > -1e-16 ? extrude.z + Math.max(p0.z, p1.z) : pmax.z);
}
var origin = pmax.clone().add(pmin).multiplyScalar(0.5);
var dir = pmax.clone().sub(pmin);
dir.normalize();
var length = pmax.distanceTo(pmin) / 2;
var hex = 0x0;
var arrowHelper0 = new THREE.ArrowHelper(dir, origin, length, hex, this.config.headLength, this.config.headWidth);
this.node.add(arrowHelper0);
dir.negate();
var arrowHelper1 = new THREE.ArrowHelper(dir, origin, length, hex, this.config.headLength, this.config.headWidth);
this.node.add(arrowHelper1);
// reposition label
if (this.domElement !== undefined) {
let textPos = origin.project(this.camera);
let clientX = this.renderer.domElement.offsetWidth * (textPos.x + 1) / 2 - this.config.headLength + this.renderer.domElement.offsetLeft;
let clientY = -this.renderer.domElement.offsetHeight * (textPos.y - 1) / 2 - this.config.headLength + this.renderer.domElement.offsetTop;
let dimWidth = this.domElement.offsetWidth;
let dimHeight = this.domElement.offsetHeight;
this.domElement.style.left = `${clientX - dimWidth/2}px`;
this.domElement.style.top = `${clientY - dimHeight/2}px`;
this.domElement.innerHTML = `${this.config.unitsConverter(pmin.distanceTo(pmax)).toFixed(2)}${this.config.units}`;
}
}
detach() {
if (this.node && this.node.parent) {
this.node.parent.remove(this.node);
}
if (this.domElement !== undefined) {
this.domRoot.removeChild(this.domElement);
this.domElement = undefined;
}
}
}

Categories

Resources