I use PrecisionModel in NTS(C#) to specify a fixed decimal place (i.e. 0.1, 0.01, 0.001 ... ) for Coordinates in a Geometry.
I tried three fixed decimal place : 0.1, 0.01, 0.001 for the same LineString. But the precision of 0.1 always got error when I do some union operations, while 0.01 and 0.001 works well.
The source code are as follows:
//Obviously, the precision of the line and the point is is 0.1
var lineStr = "LINESTRING (1667.8 1368.6, 1741.1 4274.5, 1902.5 578, 1902.5 578.1, 1867.6 1377.3, 1667.8 1368.6)";
var ptStr = "POINT (1741.1 4274.5)";
//scale is 1000d, which means 0.001 precision
var f3 = new GeometryFactory(new PrecisionModel(1000d));
var wktReader3 = new WKTReader(f3);
//scale is 100d, which means 0.01
var f2 = new GeometryFactory(new PrecisionModel(100d));
var wktReader2 = new WKTReader(f2);
//scale is 10d, which means 0.1
var f1 = new GeometryFactory(new PrecisionModel(10d));
var wktReader1 = new WKTReader(f1);
//wkt strings to geometry objects
var line3 = wktReader3.Read(lineStr);
var pt3 = wktReader3.Read(ptStr);
var line2 = wktReader2.Read(lineStr);
var pt2 = wktReader2.Read(ptStr);
var line1 = wktReader1.Read(lineStr);
var pt1 = wktReader1.Read(ptStr);
var lines3 = line3.Union(pt3);
//result: MULTILINESTRING ((1667.8 1368.6, 1741.1 4274.5, 1869.307 1338.211),
//(1869.307 1338.211, 1902.5 578, 1902.5 578.1, 1869.307 1338.211),
//(1869.307 1338.211, 1867.6 1377.3, 1667.8 1368.6))
var lines2 = line2.Union(pt2);
//result: MULTILINESTRING ((1667.8 1368.6, 1741.1 4274.5, 1869.31 1338.21),
//(1869.31 1338.21, 1902.5 578, 1902.5 578.1, 1869.31 1338.21),
//(1869.31 1338.21, 1867.6 1377.3, 1667.8 1368.6))
var lines1 = line1.Union(pt1);
//result: NetTopologySuite.Geometries.TopologyException:
//“found non-noded intersection between LINESTRING(1741.1 4274.5, 1869.3 1338.2)
//and LINESTRING(1867.6 1377.3, 1667.8 1368.6) [ (1867.59289230568, 1377.2996905058, NaN) ]”
Obviously, the decimal place of the initial lineStr is 0.1, so I think specifying 0.1 for the PrecisionModel is enough, and I really hope the accuracy of the result is also in 0.1, otherwise I have to do some rounding operations.
Is it a correct way to use PrecisionModel?
Thanks for your help.
Related
I'm play around with the CAKeyframeAnimation in order to better understanding how this type of animation work.
I want to move my SCNnode in a square shape and at every corner rotate his eulerangle Y of 90 degrees to make it follow the orientation of the track
here my code
func animatePlaneKey(nodeToAnimate: SCNNode){
// move forward
let pos = nodeToAnimate.position
let animation = CAKeyframeAnimation(keyPath: "position")
let pos1 = SCNVector3(pos.x, pos.y, pos.z)
let pos2 = SCNVector3(pos.x + 1 , pos.y, pos.z)
let pos3 = SCNVector3(pos.x + 1 , pos.y, pos.z + 1) // 1
let pos4 = SCNVector3(pos.x - 1 , pos.y, pos.z + 1)
let pos5 = SCNVector3(pos.x, pos.y, pos.z)
let easeIn = CAMediaTimingFunction(controlPoints: 0.35, 0.0, 1.0, 1.0)
animation.values = [pos1,pos2, pos3,pos4,pos5]
animation.keyTimes = [0,0.25,0.5,0.75,1]
animation.timingFunctions = [easeIn]
animation.calculationMode = .cubic
animation.duration = 12
animation.repeatCount = 1
animation.isAdditive = false
animation.autoreverses = false
// Heading 1st
let firstTurnAnim = CAKeyframeAnimation(keyPath: "eulerAngles.y")
let heading = nodeToAnimate.eulerAngles.y
let rot0heading = heading
let rot2heading = heading - Float(deg2rad(90))
firstTurnAnim.values = [rot0heading,rot2heading]
firstTurnAnim.keyTimes = [0.2,0.3]
firstTurnAnim.duration = 3
firstTurnAnim.repeatCount = 1
firstTurnAnim.isAdditive = true
firstTurnAnim.autoreverses = false
// // Heading 2st
let secondTurnAnim = CAKeyframeAnimation(keyPath: "eulerAngles.y")
let heading2 = nodeToAnimate.eulerAngles.y
let rot1head0 = heading2
let rot1head1 = heading2 - Float(deg2rad(180))
secondTurnAnim.values = [rot1head0,rot1head1]
secondTurnAnim.keyTimes = [0.45,0.55]
secondTurnAnim.duration = 6
secondTurnAnim.repeatCount = 1
secondTurnAnim.isAdditive = true
secondTurnAnim.autoreverses = false
nodeToAnimate.addAnimation(animation, forKey: "movement")
nodeToAnimate.addAnimation(firstTurnAnim, forKey: "turn")
nodeToAnimate.addAnimation(secondTurnAnim, forKey: "turn2")
}
I'm struggling to combine the animation of the y axis at the correct time.
when I add the "turn2" the animation start to mess up everything's, the node appear already rotate in the wrong direction.
For my understanding turn2 animation should start at keyframe 0.45 and finish at 0.55, why it start immediately ?
any idea what should be the correct way to combine this animation?
I read the excelent question about random populate the surface of a sphere with particles: How to make a sphere made out of random particles in three.js. How can I populate the total volume of an sphere with random generated particles? I try it:
var particleCount = 1800,
particles = new THREE.Geometry(),
pMaterial = new THREE.PointCloudMaterial({
color: 0xFFFFFF,
size: 20,
map: THREE.ImageUtils.loadTexture(
"images/particle.png"
),
blending: THREE.AdditiveBlending,
transparent: true
});
for (var t = 0; t < particleCount; t++) {
var angle3 = Math.random() * Math.PI * 2;
var radius3 = Math.random() * 350 + 1;
var pX1 = Math.cos(angle3) * radius3,
pY1 = Math.random() * 70 - 35,
pZ1 = Math.sin(angle3) * radius3,
skparticle11 = new THREE.Vector3(pX1, pY1, pZ1);
particles.vertices.push(skparticle11);
}
var particleSystem = new THREE.PointCloud(
particles,
pMaterial);
// add it to the scene
scene.add(particleSystem);
But I'm only get an disk. How to make an sphere filled with particles?
one angle is not enough, that is why you get a disk
create a random normal vector
var randomDirection = new THREE.Vector3(Math.random()-0.5,Math.random()-0.5,Math.random()-0.5).normalize();
create random distance as you did before
var radius3 = Math.random() * 350 + 1;
a random point in a sphere around origin will be
var randomParticle = randomDirection.multiplyScalar(radius3);
for a better distribution use some better generator than Math.random
I have a position vector:
var startPos = new THREE.Vector3(1.2, -2.34, 0.5);
A direction vector:
var direction = new THREE.Vector3(0.657873735, -0.2497291683, 0.71051916);
And a distance:
var distance = 1;
How to calculate a new position vector, starting from startPos that is moved the distance along the direction?
var startPos = new THREE.Vector3(1.2, -2.34, 0.5);
var direction = new THREE.Vector3(0.6578737359955765, -0.24972916834682138, 0.710519166466616);
var distance = 1;
var newPos = new THREE.Vector3();
newPos.addVectors ( startPos, direction.multiplyScalar( distance ) );
I understand that I can use body.position.set(x, y, z) to instantaneously move a body, but how can I move it smoothly in an animated manner where it's movement will adhere to the physics and collide with any other bodies on its journey? Using body.velocity.set(x, y, z) will set its velocity, and using body.linearDamping = v, will provide some friction/resistance... but it's still not good enough to allow me to specify exactly where I want the body to stop.
It sounds like you're looking for a kinematic body. With kinematic bodies, you have full control over the movement, and it will push away other bodies in its path. However, the body has infinite mass and is not affected by other bodies colliding with it.
Start off by defining the start and end positions of your body.
var startPosition = new CANNON.Vec3(5, 0, 2);
var endPosition = new CANNON.Vec3(-5, 0, 2);
var tweenTime = 3; // seconds
Then create your kinematic body. In this example we'll add a Box shape to it.
var body = new CANNON.Body({
mass: 0,
type: CANNON.Body.KINEMATIC,
position: startPosition
});
body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
world.add(body);
Compute the direction vector and get total length of the tween path.
var direction = new CANNON.Vec3();
endPosition.vsub(startPosition, direction);
var totalLength = direction.length();
direction.normalize();
The speed and velocity can be calculated using the formula v = s / t.
var speed = totalLength / tweenTime;
direction.scale(speed, body.velocity);
For each update, compute the tween progress: a number between 0 and 1 where 0 i start position and 1 is end position. Using this number you can calculate the current body position.
var progress = (world.time - startTime) / tweenTime;
if(progress < 1){
// Calculate current position
direction.scale(progress * totalLength, offset);
startPosition.vadd(offset, body.position);
} else {
// We passed the end position! Stop.
body.velocity.set(0,0,0);
body.position.copy(endPosition);
}
See full code below. You can duplicate one of the cannon.js demos and just paste this code.
var demo = new CANNON.Demo();
var postStepHandler;
demo.addScene("Tween box",function(){
var world = demo.getWorld();
// Inputs
var startPosition = new CANNON.Vec3(5, 0, 2);
var endPosition = new CANNON.Vec3(-5, 0, 2);
var tweenTime = 3; // seconds
var body = new CANNON.Body({
mass: 0,
type: CANNON.Body.KINEMATIC,
position: startPosition
});
body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
world.add(body);
demo.addVisual(body);
if(postStepHandler){
world.removeEventListener('postStep', postStepHandler);
}
// Compute direction vector and get total length of the path
var direction = new CANNON.Vec3();
endPosition.vsub(startPosition, direction);
var totalLength = direction.length();
direction.normalize();
var speed = totalLength / tweenTime;
direction.scale(speed, body.velocity);
// Save the start time
var startTime = world.time;
var offset = new CANNON.Vec3();
postStepHandler = function(){
// Progress is a number where 0 is at start position and 1 is at end position
var progress = (world.time - startTime) / tweenTime;
if(progress < 1){
direction.scale(progress * totalLength, offset);
startPosition.vadd(offset, body.position);
} else {
body.velocity.set(0,0,0);
body.position.copy(endPosition);
world.removeEventListener('postStep', postStepHandler);
postStepHandler = null;
}
}
world.addEventListener('postStep', postStepHandler);
});
demo.start();
You need to use a physics library for this, such as Physijs. It works easily with Three.js. Googling for "Physijs Three.js" will provide examples.
(new to stackoverflow, new to webgl/three.js, ...)
I'm using three.js r54 to plot a force-directed graph. the edges between the nodes are THREE.Lines, which is fine, but lines are not selectable with a raycaster. so my aim is to take cylinders instead(/along with) of lines(also because I can do some further stuff: using textures,...)
this is what I'm doing to place the cylinders:
// init reference vector
var upVec = new THREE.Vector3(0,1,0);
//---withhin a loop---
// get direction
var direction = startPoint.subSelf(endPoint).clone();
// half length for cylinder height
var halfLength = direction.length() * 0.5;
// get offset
var offset = endPoint.clone().addSelf(direction.clone().multiplyScalar(0.5));
// normalize direc
direction.normalize();
//newUpVec = upVec - (upVec *(dot) direction) * direction - projection of direction
var newUpVec = upVec.clone().subSelf(direction.clone().multiplyScalar(upVec.dot(direction.clone()))).normalize();
var right = newUpVec.clone().crossSelf(direction.clone());
//build rotation matrix
var rot = new THREE.Matrix4(right.x, right.y, right.z, 0,
newUpVec.x, newUpVec.y, newUpVec.z, 0,
direction.x, direction.y, direction.z,0,
0,0,0,1);
//build translation matrix
var transla = new THREE.Matrix4(1, 0, 0, offset.x,
0, 1, 0, offset.y,
0, 0, 1, offset.z,
0, 0, 0, 1);
//build transformation matrix
var transfo = new THREE.Matrix4().multiply(transla, rot);
// create geometry
var cylgeo = new THREE.CylinderGeometry(2, 2, halfLength * 2, 12, 1, false);
cylgeo.applyMatrix(transfo);
var cylMesh = new THREE.Mesh(cylgeo, new THREE.MeshLambertMaterial({color:0x000000,
wireframe: true, shading: THREE.FlatShading}));
(descripted in: http://www.fastgraph.com/makegames/3drotation/ )
So the cylinders are placed at the right offset and align in some kind of way, but not to the two points (start, end) of the edges.
any suggestion would be appreciated!
using that :
object3d-rotation-to-align-to-a-vector
given 2 Vector3 and a scene:
function drawCylinder(vstart, vend,scene){
var HALF_PI = +Math.PI * .5;
var distance = vstart.distanceTo(vend);
var position = vend.clone().addSelf(vstart).divideScalar(2);
var material = new THREE.MeshLambertMaterial({color:0x0000ff});
var cylinder = new THREE.CylinderGeometry(10,10,distance,10,10,false);
var orientation = new THREE.Matrix4();//a new orientation matrix to offset pivot
var offsetRotation = new THREE.Matrix4();//a matrix to fix pivot rotation
var offsetPosition = new THREE.Matrix4();//a matrix to fix pivot position
orientation.lookAt(vstart,vend,new THREE.Vector3(0,1,0));//look at destination
offsetRotation.rotateX(HALF_PI);//rotate 90 degs on X
orientation.multiplySelf(offsetRotation);//combine orientation with rotation transformations
cylinder.applyMatrix(orientation)
var mesh = new THREE.Mesh(cylinder,material);
mesh.position=position;
scene.add(mesh);
}
r58+ code :
function drawCylinder(vstart, vend,scene){
var HALF_PI = Math.PI * .5;
var distance = vstart.distanceTo(vend);
var position = vend.clone().add(vstart).divideScalar(2);
var material = new THREE.MeshLambertMaterial({color:0x0000ff});
var cylinder = new THREE.CylinderGeometry(10,10,distance,10,10,false);
var orientation = new THREE.Matrix4();//a new orientation matrix to offset pivot
var offsetRotation = new THREE.Matrix4();//a matrix to fix pivot rotation
var offsetPosition = new THREE.Matrix4();//a matrix to fix pivot position
orientation.lookAt(vstart,vend,new THREE.Vector3(0,1,0));//look at destination
offsetRotation.makeRotationX(HALF_PI);//rotate 90 degs on X
orientation.multiply(offsetRotation);//combine orientation with rotation transformations
cylinder.applyMatrix(orientation)
var mesh = new THREE.Mesh(cylinder,material);
mesh.position=position;
scene.add(mesh);
}
#jdregister's answer didn't quite work for me in R77, since the cylinder ended up with its center at vstart (rotation and lookAt were otherwise fine).
This modification to the second last line of the R58+ answer did the trick:
mesh.position.set(position.x, position.y, position.z);
There's a very succinct answer here: https://stackoverflow.com/a/44346439/1556416
I paraphrased it here:
function drawCylinder(vstart, vend, radius){
var cylLength = new THREE.Vector3().subVectors(vend, vstart).length();
var cylGeom = new THREE.CylinderGeometry(radius, radius, cylLength, 16);
cylGeom.translate(0, cylLength / 2, 0);
cylGeom.rotateX(Math.PI / 2);
var material = new THREE.MeshLambertMaterial({color: "blue"})
var cyl = new THREE.Mesh(cylGeom, material);
cyl.position.copy(vstart);
cyl.lookAt(vend); // and do the trick with orienation
return cyl
}
In R87 the "vend.clone().add(vstart).divideScalar(2);" is not working
You can position the item like this
mesh.position.copy(start);
mesh.position.lerp(end, 0.5);
All the others from R58 are fine :)