Reputation: 10425
I am trying to draw a circle very similar to the orbital patterns on this website. I would like to use Three.js instead of pure WebGL.
Upvotes: 17
Views: 42923
Reputation: 1410
The "brute force" methods (using for, cos, sin) now (R149) need to use BufferGeometry instead of Geometry:
But I like the "absarc" approach of Jammo (without g.rotateX()).
function docircle(r, segs, color) {
let x,y,angle,avertices=[],geometry,material,lines;
for (let i=0; i<segs; i+=1 ) {
angle = i/segs*Math.PI*2;
x = r*Math.cos(angle); y = r*Math.sin(angle);
avertices.push(x,y,0);
}
geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.Float32BufferAttribute(avertices, 3));
material = new THREE.LineBasicMaterial( { color:color } );
lines = new THREE.LineLoop(geometry, material);
scene.add(lines);
}
Upvotes: 0
Reputation: 707
The code from mrienstra's answer is deprecated for THREE.REVISION = "150". I see an error message:
Cannot read properties of undefined (reading 'shift')
for the
geometry.vertices.shift();
line.
Please see my edited code below for a solution:
const radius = 100,
segments = 64,
material = new THREE.LineBasicMaterial( { color: 0x0000ff } ),
geometry = new THREE.CircleGeometry( radius, segments );
// Remove center vertex
const itemSize = 3;
geometry.setAttribute( 'position',
new THREE.BufferAttribute(
geometry.attributes.position.array.slice( itemSize,
geometry.attributes.position.array.length - itemSize
), itemSize
)
);
geometry.index = null;
// Non closed circle with one open segment:
scene.add( new THREE.Line( geometry, material ) );
// To get a closed circle use LineLoop instead (see also @jackrugile his comment):
scene.add( new THREE.LineLoop( geometry, material ) );
Upvotes: 0
Reputation: 707
The code from Andrew's answer is deprecated for THREE.REVISION = "150". I see an error message:
Cannot read properties of undefined (reading 'splice')
for the
circleGeometry.vertices.splice(0, 1);
line.
Please see my edited code below for a solution:
function createCircle() {
const circleGeometry = new THREE.CircleGeometry(1.0, 30);
// Remove center vertex
const itemSize = 3;
circleGeometry.setAttribute( 'position',
new THREE.BufferAttribute(
circleGeometry.attributes.position.array.slice( itemSize,
circleGeometry.attributes.position.array.length - itemSize
), itemSize
)
);
circleGeometry.index = null;
return new THREE.LineLoop(circleGeometry,
new THREE.LineBasicMaterial({ color: 'blue' }));
}
const circle = createCircle();
Upvotes: 2
Reputation: 2152
Instead of drawing a 3D object with a fixed thickness (which becomes apparent when you zoom in) you can just make a fixed thickness circle that stays a thin line, which is very nice for a UI overlay, just like OP example link
absarc()
and setFromPoints()
very useful here.
//Draws an orbit at 0,0 with given distance
let pts = new THREE.Path().absarc(0, 0, orbitDist, 0, Math.PI * 2).getPoints(90);
let g = new THREE.BufferGeometry().setFromPoints(pts);
let m = new THREE.LineBasicMaterial( { color: 0x00ffff, transparent: true, opacity: 0.5 } );
g.rotateX( - Math.PI / 2);
let l = new THREE.Line(g, m);
scene.add( l );
As it makes an arc in 2D, you can move it in 3D if you wish with .position.set(x,y,z)
, or, I add it to the scene here at the 3D origin anyway (0,0,0). I rotated due to my 3D perspective camera angle.
Upvotes: 1
Reputation: 86
Well, I dunno when they added it - but TorusGeometry should do the job... THREE TorusGeometry
const geometry = new THREE.TorusGeometry( 10, 3, 16, 100 );
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
const torus = new THREE.Mesh( geometry, material );
scene.add( torus );
Dunno, but I think it shouldn't be (much) more expensive than the line thingy and it's a buffer geometry and you may adjust size and material etc...
Upvotes: 2
Reputation: 21
I had some issues getting the other answers to work here -- in particular, CircleGeometry
had an extra point at the center of the circle, and I didn't like the hack of trying to remove that vertex.
EllipseCurve
does what I wanted (verified in r135):
const curve = new THREE.EllipseCurve(
0.0, 0.0, // Center x, y
10.0, 10.0, // x radius, y radius
0.0, 2.0 * Math.PI, // Start angle, stop angle
);
const pts = curve.getSpacedPoints(256);
const geo = new THREE.BufferGeometry().setFromPoints(pts);
const mat = new THREE.LineBasicMaterial({ color: 0xFF00FF });
const circle = new THREE.LineLoop(geo, mat);
scene.add(circle);
Upvotes: 2
Reputation: 1
I had to do this lol:
function createCircle() {
let circleGeometry = new THREE.CircleGeometry(1.0, 30.0);
circleGeometry.vertices.splice(0, 1); //<= This.
return new THREE.LineLoop(circleGeometry,
new THREE.LineBasicMaterial({ color: 'blue' }));
}
let circle = createCircle();
Reason: Otherwise, it doesn't draw a "pure" circle, there's a line coming from the center to the rim of the circle, even if you use LineLoop instead of Line. Splicing (removing) the first vertex from the array is a hack but seems to do the trick. :)
(Note that apparently, according to mrienstra's answer, "Oh, and it appears to be necessary if you are going to use any material aside from LineBasicMaterial / LineDashedMaterial.")
If you want thickness, though, you're screwed ("Due to limitations of the OpenGL Core Profile with the WebGL renderer on most platforms linewidth will always be 1 regardless of the set value.")... Unless you use: https://github.com/spite/THREE.MeshLine
Code example for that is here: https://stackoverflow.com/a/61312721/1599699
Upvotes: 5
Reputation: 622
Three.js r50 added CircleGeometry
. It can be seen (albeit with a face) in the WebGL Geometries example.
The first vertex in the geometry is created at the center of the circle (in r84, see CircleGeometry.js line 71, in r65, see CircleGeometry.js line 18), which is nifty if you are going for that "full Pac-Man" or "uninformative pie chart" look. Oh, and it appears to be necessary if you are going to use any material aside from LineBasicMaterial
/ LineDashedMaterial
.
I've verified that the following code works in both r60 & r65:
var radius = 100,
segments = 64,
material = new THREE.LineBasicMaterial( { color: 0x0000ff } ),
geometry = new THREE.CircleGeometry( radius, segments );
// Remove center vertex
geometry.vertices.shift();
// Non closed circle with one open segment:
scene.add( new THREE.Line( geometry, material ) );
// To get a closed circle use LineLoop instead (see also @jackrugile his comment):
scene.add( new THREE.LineLoop( geometry, material ) );
PS: The "docs" now include a nice CircleGeometry
interactive example: https://threejs.org/docs/#api/geometries/CircleGeometry
Upvotes: 28
Reputation: 357
var getStuffDashCircle2 = function () {
var segment = 100, radius = 100;
var lineGeometry = new THREE.Geometry();
var vertArray = lineGeometry.vertices;
var angle = 2 * Math.PI / segment;
for (var i = 0; i < segment; i++) {
var x = radius * Math.cos(angle * i);
var y = radius * Math.sin(angle * i);
vertArray.push(new THREE.Vector3(x, y, 0));
}
lineGeometry.computeLineDistances();
var lineMaterial = new THREE.LineDashedMaterial({ color: 0x00cc00, dashSize: 4, gapSize: 2 });
var circle = new THREE.Line(lineGeometry, lineMaterial);
circle.rotation.x = Math.PI / 2;
circle.position.y = cylinderParam.trackHeight+20;
return circle;
}
Upvotes: 1
Reputation: 9859
This example is in the Three.js documentation:
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
var radius = 5;
var segments = 32; //<-- Increase or decrease for more resolution I guess
var circleGeometry = new THREE.CircleGeometry( radius, segments );
var circle = new THREE.Mesh( circleGeometry, material );
scene.add( circle );
Upvotes: 3
Reputation: 310792
The API changed slightly in newer versions of threejs.
var segmentCount = 32,
radius = 100,
geometry = new THREE.Geometry(),
material = new THREE.LineBasicMaterial({ color: 0xFFFFFF });
for (var i = 0; i <= segmentCount; i++) {
var theta = (i / segmentCount) * Math.PI * 2;
geometry.vertices.push(
new THREE.Vector3(
Math.cos(theta) * radius,
Math.sin(theta) * radius,
0));
}
scene.add(new THREE.Line(geometry, material));
Modify segmentCount
to make the circle smoother or more jagged as needed by your scene. 32 segments will be quite smooth for small circles. For orbits such as those on the site you link you, you may want to have a few hundred.
Modify the order of the three components within the Vector3
constructor to choose the orientation of the circle. As given here, the circle will be aligned to the x/y plane.
Upvotes: 13
Reputation: 10425
I used code that Mr.doob references in this github post.
var resolution = 100;
var amplitude = 100;
var size = 360 / resolution;
var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial( { color: 0xFFFFFF, opacity: 1.0} );
for(var i = 0; i <= resolution; i++) {
var segment = ( i * size ) * Math.PI / 180;
geometry.vertices.push( new THREE.Vertex( new THREE.Vector3( Math.cos( segment ) * amplitude, 0, Math.sin( segment ) * amplitude ) ) );
}
var line = new THREE.Line( geometry, material );
scene.add(line);
Upvotes: 4
Reputation: 3305
See the three.js sample http://mrdoob.github.com/three.js/examples/webgl_lines_colors.html to see how to draw colored lines.
A circle like the ones you cite is drawn as a large # of little straight segments. (Actually, the ones you show may be ellipses)
Upvotes: 1