theblang
theblang

Reputation: 10425

Draw a circle (not shaded) with Three.js

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

Answers (13)

dcromley
dcromley

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

Andrej
Andrej

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

Andrej
Andrej

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

Jammo
Jammo

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

Stephan
Stephan

Reputation: 86

Well, I dunno when they added it - but TorusGeometry should do the job... THREE TorusGeometry 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

endofactor
endofactor

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

Andrew
Andrew

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

mrienstra
mrienstra

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

Jack
Jack

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

Cristian Garcia
Cristian Garcia

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

Drew Noakes
Drew Noakes

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

theblang
theblang

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

bjorke
bjorke

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

Related Questions