distributing 2d vector points around a sphere - three.js

I'm using d3.js to create a bubble chart, which I'm then trying to wrap partially around a sphere in three.js. I'd like the end result to look like a dandelion, as pictured here:
The bubble chart on a 2d plane looks like this. I'm trying to wrap it half-way around a sphere to create that dandelion effect.
I have three semi-working solutions, yet none exactly follow the curve of a sphere when viewed from the side
Example A - zCoord = new THREE.Vector2(xCoord, yCoord).length();
This gives a linear looking cone effect, not a curved effect. I think I somehow need to calculate a quadratic curves instead of a linear line but I'm stuck trying to figure it out.
Example B - zCoord = (diameter / 2 ) * Math.cos(phi);
This uses code from the periodic table of elements and spirals the data along the z axis.
Example C - Close to what I want, but it doesn't wrap around sphere enough, and everything seems to bunch up together. I'd like to preserve the padding or space around the mini-spheres
zCoord = (diameter / 2 );
var vector = new THREE.Vector3(xCoord, yCoord, zCoord).normalize().multiplyScalar(diameter / 2);
jsfiddle link to try out the methods

May not be the most efficient solution, but I did get pretty close using Mercator Projection. It's almost like I'm UV wrapping, but with Vector2 points.
My solution involved mapping X,Y coords to latitude and longitude, then projecting them onto a sphere using mercator projection.
var latitude = data[i].position.x / R;
var longitude = (2 * Math.atan(Math.exp(data[i].position.y / R))) - (Math.PI / 2);
xCoord = R * Math.cos(latitude) * Math.cos(longitude);
yCoord = R * Math.cos(latitude) * Math.sin(longitude);
zCoord = R * Math.sin(latitude);
link to jsfiddle showing tweening from 2d > 3d

Related

Create a ring on the surface of a sphere in threejs

I have a sphere in threejs, and I'd like a ring to animate over the top of it.
I have the following progress:
https://codepen.io/EightArmsHQ/pen/zYRdQOw/2919f1a1bdcd2643390efc33bd4b73c9?editors=0010
In the animate function, I call:
const scale = Math.cos((circlePos / this.globeRadius) * Math.PI * 0.5);
console.log(scale);
this.ring.scale.set(scale, scale, 1);
My understanding is that the sin and cos functions are exactly what I need to work out how far around the circle the ring has gotten to. However, the animation actually shows the ring fall inside the sphere, before eventually hitting the 0 scale at the outside of the sphere.
Ideally, I'd also like to just be changing the radius of the sphere but I cannot work out how to do that either, so I think it may be an issue of using the scale function.
How can I keep the ring on the surface of the sphere?
Not quite. Consider this:
You have a right triangle whose bases are your x and y, with a hypotenuse of r = globeRadius. So by Pythagoras' theorem, we have:
x2 + y2 = r2.
So if we solve for the height, y, we get:
y = √(r2 - x2).
Thus, in your code, you could write it e.g. like this:
const scale = Math.sqrt(this.globeRadius * this.globeRadius - circlePos * circlePos);
However, this is the scale in terms of world units, not relative to the objects. So for this to work, you need to either divide by your radius again, or just initialise your ring with radius 1:
this.ringGeometry = new THREE.RingGeometry(1, 1.03, 32);
Here I gave it an arbitrary ring width of 0.03 - you may of course adjust it to your own needs.

d3 geo projection transitions from orthographic to X

I'm working on an educational map project in which different map projections are displayed. I'd like to implement a morph transition between choosing different projections.
I've found a great example how to implement it, and I've had not much troubles to recreate it. Unfortunately, I also need the capability to clip the projections. This works flawlessly with the target states, but not when morphing the projections.
You can see it in this example when choosing "orthographic" as first projection and for example "equirectangular" as second one:
https://bl.ocks.org/alexmacy/082cb12c8f4d5c0d5c4445c16a3db383
The clipping path follows the darker line instead of the current map extent. Is there a way to implement it correctly?
This is a lot trickier to implement than appears, I remember looking at this a few years back. The cleanest solution is to create a new preclipping function that determines which portions of the projected earth should be behind/covered by portions closer to the origin. But it turns out this is relatively hard to define - at least my me - and also hard to use in a new preclipping function.
Instead we could cheat. There are a couple ways, I'll propose one that nearly does the trick - you can still see some overlap though. We'll use d3's antimeridian preclipping to make sure no features stretch over the antimeridian, then we'll use a clip angle to remove portions of the earth that need to be removed.
Setting Clip Angle
When the hybrid projection is purely orthographic, clipping angle is great: the clip angle is the same in all directions. Here it should be 90 degrees.
When the equirectangular is dominant in the hybrid projection, the clipping angle is not needed (I use an angle of 180 degrees, which doesn't clip anything below). This is because the entire earth should still be visible.
But otherwise, the hybrid clip angle is not the same in all directions - this is why this is not a perfect solution. However, it does remove nearly all the overlap. So as we go from the projection being mostly equirectangular to wholly orthogrpahic, we slowly reduce the clip angle.
Example
Starting with an equirectangular projection and transitioning to an orthographic, we'll start transitioning the clipAngle from 180 degrees to 90 degrees only once we get 40 percent of the way trough the transition:
function getProjection(d) {
var clip = Math.PI; // Starting with 180 degrees: don't clip anything.
var projection = d3.geoProjection(project)
.rotate([posX, posY])
.fitExtent([[10, 10], [width - 10, height - 10]], {
type: "Sphere"
})
// Apply the two pre clipping functions:
.preclip( function(stream){
stream = d3.geoClipAntimeridian(stream) // cut antimeridian
return d3.geoClipCircle(clip)(stream) // apply clip angle
})
var path = d3.geoPath(projection);
function project(λ, φ) {
λ *= 180 / Math.PI,
φ *= 180 / Math.PI;
var p0 = projections[0]([λ, φ]),
p1 = projections[1]([λ, φ]);
// Don't actually clip anything until t == 0.4
if(t > 0.4) {
clip = Math.PI/2 + (0.60-(t-0.4)) * Math.PI/2
}
return [
(1 - t) * p0[0] + t * p1[0],
(1 - t) * -p0[1] + t * -p1[1]
];
}
return path(d)
}
Here's an example.
Great answer Andrew Reid! I just made one small change. I removed the t > 0.4 if statement and used this clip for transitioning into an orthogrpahic projection:
clip = Math.PI/2 + (1 - t) * Math.PI/2
.. and this clip for transitioning out of an orthogrpahic projection:
clip = Math.PI/2 + t * Math.PI/2
I like this because it's slightly cleaner, is a 'catch-all' for any t value and having the reverse is also useful.

Rotating an Object properly around a pivot point given axis and angle

In Three.js there seems to be quite a few ways of rotation which i personally do not find very intuitive. See e.g. the example
http://cloud.engineering-bear.com/apps/robot/robot.html
I get very strange unexpected effects when I apply rotation to multiple objects. E.g. when I rotate objects that have been added to each other and start rotating the parent the individual objects will all over sudden by placed differently in respect to each other then they originally where. I am now experimenting with grouping and would like to avoid the same effect.
See http://pi-q-robot.bitplan.com/example/robot?robot=/models/thing3088064.json for the current state of affairs and https://github.com/BITPlan/PI-Q-Robot for the source code.
So i searched for proper examples following the different API options:
rotation
function renderScene() {
stats.update();
//side1.rotation.z += 0.02;
pivot.rotation.z += 0.02;
https://jsfiddle.net/of1vfhzz/1/
https://github.com/mrdoob/three.js/issues/1958
rotateOnAxis
three.js rotate Object3d around Y axis at it center
How to rotate a 3D object on axis three.js?
ThreeJS - rotation around object's own axis
rotateAroundWorldAxis
object.rotateAroundWorldAxis(p, ax, r * Math.PI * 2 / frames);
How to rotate a object on axis world three.js?
https://stackoverflow.com/a/32038265/1497139
https://jsfiddle.net/b4wqxkjn/7/
THREE.js Update rotation property of object after rotateOnWorldAxis
rotateOnWorldAxis
object.rotateOnWorldAxis( axis, angle );
Rotate around World Axis
rotateAboutPoint
Three JS Pivot point
Rotation anchor point in Three.js
setRotationFromAxisAngle
https://threejs.org/docs/#api/en/core/Object3D.setRotationFromAxisAngle
setEulerFromQuaternion
quaternion = new THREE.Quaternion().setFromAxisAngle( axisOfRotation, angleOfRotation );
object.rotation.setEulerFromQuaternion( quaternion );
Three.js - Rotating a sphere around a certain axis
applyMatrix
this.mesh.updateMatrixWorld(); // important !
childPart.mesh.applyMatrix(new THREE.Matrix4().getInverse(this.mesh.matrixWorld))
Applying a matrix in Three.js does not what I expect
I like the jsFiddle for https://stackoverflow.com/a/56427636/1497139
var pivot = new THREE.Object3D();
pivot.add( cube );
scene.add( pivot );
I also found the following discussions
pivot issue in discourcee.three.js.org
https://discourse.threejs.org/t/rotate-group-around-pivot/3656
https://discourse.threejs.org/t/how-to-rotate-an-object-around-a-pivot-point/6838
https://discourse.threejs.org/t/set-dynamically-generated-groups-pivot-position-to-the-center-of-its-children-objects-position/6349
https://discourse.threejs.org/t/my-3d-model-is-not-rotating-around-its-origin/3339/3
https://jsfiddle.net/blackstrings/c0o3Lm45/
https://discourse.threejs.org/t/rotate-object-at-end-point/2190
https://jsfiddle.net/f2Lommf5/3594/
Questions
None of the above information is clear enough to get to the point of the problem to be solved. The graphics above are much clearer stating the problem than the proposals are stating a solution.
a)
I'd like to use the cylinder as the axis even when the cylinder is moved.I'd expect the easiest way to go would be to use rotateAroundWorldAxis - is that available in the latest revision from three.js or do i have to add it from e.g. https://stackoverflow.com/a/32038265/1497139?
b) I'd like to get a chain of objects to be rotated to later apply inverse kinematics as in
https://github.com/jsantell/THREE.IK
https://jsantell.github.io/THREE.IK/
Although i looked at the source code of that solutions I can't really find the place where the parent-child positioning and rotating is happening. What are the relevant lines of code / API functions that would make proper rotation around a chain of joints happen?
I already looked in the Bone/Skeleton API of Three.js but had the same problem there - lots of lines of code but no clear point where the rotation/positioning between child and parent happens.
Question a)
Basically it works as expected:
cylinder.position.set( options.x, 15, options.z );
pivot.position.x=options.x;
pivot.position.z=options.z;
see
https://jsfiddle.net/wf_bitplan_com/4f6ebs90/13/
Question b)
see
https://codepen.io/seppl2019/pen/zgJVKM
The key is to set the positions correctly. Instead of the proposal at https://stackoverflow.com/a/43837053/1497139 the size is computed in this case.
// create the pivot to rotate around/about
this.pivot = new THREE.Group();
this.pivot.add(this.mesh);
// shift the pivot position to fit my size + the size of the joint
this.pivot.position.set(
x,
y + this.size.y / 2 + this.pivotr,
z + this.size.z / 2
);
// reposition the mesh accordingly
this.mesh.position.set(0, this.size.y / 2, 0);

How to rotate a Curve in Three.js?

I'm trying to rotate a Curve (especially a EllipseCurve) in Three.js. My app looks basically like this: http://jsfiddle.net/w9914420/krw8nwLn/14/
But EllipseCurve can't be rotated at the Y-Axis. Is there a way to "transform" such 2D-curves to 3D-curves in Three.js? Three.js provides CatmullRomCurve3, CubicBezierCurve3 and LineCurve3 for 3D-curves, but I don't get a nice circle (or ellipse) with these methods – and I need something that is based on Three.js' Curve because I need the getPoint and getTangent methods.
Edit
OK, the problem isn't the curve path itself, it's the calculation of getPoint.
I've created an own version, but don't get the math the calculate the z axis right. Something like this works for the first few values of this.rotation (around -0.5 and 0.5 – higher values doesn't have the same effect in distance):
getPointOnCurve(t) {
const radians = 2 * Math.PI * t
return new THREE.Vector3(
this.radius * Math.cos(radians),
this.radius * Math.sin(radians),
this.radius * Math.cos(radians) * this.rotation
)
}
I need to know better about the math that's needed for this, definitely.

Openlayers 3 Rotate Multipoint geometry

I'm looking to use a multipoint geometry to hold different points of features I have on my map, but I need to rotate them around a point.
Does Openlayers 3 have any functionality that would allow me to take a Multipoint and rotate it around one of those points?
ol.coordinates.rotate() exists but does not perform the action I need.
Is this part of the library or a trigonometry exercise for the implementor?
I ended up creating multiplie ol.geom.Point objects and using the following function to rotate them around a given point:-
rotateGeometry = function(pointLongitude, pointLatitude, originLongitude, originLatitude, angle) {
angle = angle * Math.PI / 180.0;
return [Math.cos(angle) * (pointX - originX) - Math.sin(angle) * (pointY - originY) + originX, Math.sin(angle) * (pointX - originX) + Math.cos(angle) * (pointY - originY) + originY];
}
Feeding in my coordinates that worked. An extension of this could be feeding it a multipoint geometry, with a nominated axis point, and rotate all other geometries around it using this function in one go.
Oh, and I had to invert the angle sent in to rotate correctly, but that may be my implementation.
Comments welcomed.

Resources