 # Ringsegment function leads to strange behaviour

I’m new to three.js and I wanted to create a Shape (special ringsegment with some space) manually in a function. see code below. My attempt is the following:

1. my function takes 4 arguments (inner radius, outer radius, the segment angle, the space)
2. i create a shape (circle segment)
3. i create a hole-path, that will be removed.

My function worked well when I set the angle to 25°. But when I raise the angle up to 30° -> the Hole is not removed anymore? The size of the space has the same effect… if i set the space too small , the hole is not removed as well. Has someone an idea, why this happens? Or another approach to get to my goal? ``````    Toolbox.prototype.createCircleElement = function(inner, outer, angle, space) {
var spaceAngleO = Math.asin( space / outer ); //difference of
var spaceAngleI = Math.asin( space / inner );
var xCenter = space / Math.tan( radians / 2 );
var radiusDiff = Math.sqrt(Math.pow(space, 2) + Math.pow(xCenter, 2));
var yspaceO = 0.5*Math.sqrt( Math.pow(2*outer*Math.sin(spaceAngleO), 2) - Math.pow(space, 2) );
var yspaceI = 0.5*Math.sqrt( Math.pow(2*inner*Math.sin(spaceAngleI), 2) - Math.pow(space, 2) );

//fill shape
var buttonShape = new THREE.Shape();
buttonShape.moveTo(xCenter, space);
buttonShape.lineTo(outer - yspaceO, space);
buttonShape.arc(-outer + xCenter + yspaceO, 0, outer - radiusDiff, 0, radians, false);
buttonShape.lineTo(xCenter, space);

//inner ring to remove
var buttonHolePath = new THREE.Path();
buttonHolePath.moveTo(xCenter, space);
buttonHolePath.lineTo(inner - yspaceI, space);
buttonHolePath.arc(-inner + xCenter + yspaceI, 0, inner - radiusDiff, 0, radians, false);
buttonHolePath.lineTo(xCenter, space);

buttonShape.holes.push(buttonHolePath);

var geometry = new THREE.ShapeGeometry(buttonShape);
var material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
var mesh = new THREE.Mesh(geometry, material);
return mesh;
``````

Here are three examples (two of them fail):

``````    var butn = vrToolbox.createCircleElement(1.5, 4, 45, 0.14); //did not work... space too small
var butn2 = vrToolbox.createCircleElement(1.5, 4, 45, 0.15); //ok
var butn3 = vrToolbox.createCircleElement(1.5, 4, 30, 0.15); //did not work angle to large``````

Why not just using RingBufferGeometry?

In any event, I suggest you have a look at the code of the class and maybe use it for your own implementation. The analytical approach of `RingBufferGeometry` is more efficient than using `Shape`s since they need to be triangulated first before rendering.

Thank you! I will try to create the mesh with a BufferGeometry analytical. But I still want to find out, why the Shape-attempt did not work in some particular cases. Can this emerge from math-rounding-errors?

I don’t think so. There might be a problem in the way you calculate the parameter for the method calls of `Shape`. Have you written this code based on a reference or documentation you can share?

Ok, It’s not based on a particular reference… I calculated the paramters using trigonometric functions.
I found it just strange, that in some cases the holeShape isn’t removed. Did somebody know under which conditions a hole won’t be removed? Can that occur, when some points of the hole lay outside of the Shape?
@Mugen87: Thanks for your advice to take the analytical way, this is very good to learn some additional Stuff:)

Hi!
Just out of curiousity, why do you want to use a hole instead of creating a shape of segment with two arcs?

My first attempt was exactly as you say. But I had no clue, how to create a path.arc that was koncave. It filled always the hole circle-segment.

Just an option:

``````var angle = 45;

var shape = new THREE.Shape();
shape.absarc(-5, 0, 10, 0, THREE.Math.degToRad(angle), false); // CCW - outer
shape.absarc(-5, 0, 5, THREE.Math.degToRad(angle), 0, true); // CW - inner

var shapeGeom = new THREE.ShapeGeometry(shape);
var shapeMat = new THREE.MeshBasicMaterial({color: 0x00ff00});
var shapeMesh = new THREE.Mesh(shapeGeom, shapeMat);
``````

But, if got your scheme correctly, you’re looking for something like this:

``````var angle = 45;
var Ro = 10;
var Ri = 5;
var center = new THREE.Vector2(0, 0);
var shift = 0.5; // "space" on your scheme, though, it would be better to call it "offset" here instead of "shift"

var Xo = getX(Ro, shift); // x-value for a point on the outer radius
var Po1 = new THREE.Vector2( Xo,  shift);  // find the first point on the outer radius
var Po2 = new THREE.Vector2( Xo, -shift).rotateAround(center, THREE.Math.degToRad(angle)); // find the second point on the outer radius
var Ao1 = Po1.angle(); // find the angle for the first point
var Ao2 = Po2.angle(); // find the angle for the second point

// the same actions for the point on the inner radius
var Xi = getX(Ri, shift);
var Pi1 = new THREE.Vector2( Xi,  shift);
var Pi2 = new THREE.Vector2( Xi, -shift).rotateAround(center, THREE.Math.degToRad(angle));
var Ai1 = Pi1.angle();
var Ai2 = Pi2.angle();

// gather all the things we did before to create a shape
var shape2 = new THREE.Shape();
shape2.absarc(center.x, center.y, Ro, Ao1, Ao2, false); // CCW - outer
shape2.absarc(center.x, center.y, Ri, Ai2, Ai1, true); // CW - inner

var shapeGeom2 = new THREE.ShapeGeometry(shape2);
var shapeMat2 = new THREE.MeshBasicMaterial({color: 0xffff00});
var shapeMesh2 = new THREE.Mesh(shapeGeom2, shapeMat2);

// helper function
}

``````
2 Likes

Wow, this solution works perfect and does exactly what I want it to do:) Thank you @prisoner849!!!
I will add some checks to assure, that the shift doesn’t exceed the “width” of the circlesegment.

1 Like

You’re welcome  I created an analytical method to accomplish the spacedRingSegment. It’s based on the RingBufferGeometry.js (ringBufferGeometry.js). My approach calculates the startAngle, endAngle, Anglestep (thetaStartRel, thetaEndRel, segmentStep) anew for each phiSegment. I leaved out the possibility to set a startAngle, simply because I did not need it fo my objective. Improvements are always welcome:)

``````   spacedRingSegmentBufferGeometry = function(innerRadius, outerRadius, thetaSegments, phiSegments, thetaLength, space) {

if((Math.sin((thetaLength) / 2) * innerRadius) < space) {
var err = "Cannot create spacedRingSegment due to geometrical inconsitency: inner Radius or angle too small OR Space too large";
throw new Error(err);
}

var thetaSegments = thetaSegments;
var phiSegments = phiSegments;
var thetaLength = thetaLength;
var space = space;

this.type = 'RingBufferGeometry';

this.parameters = {
thetaSegments: thetaSegments,
phiSegments: phiSegments,
thetaLength: thetaLength,
space: space
};

thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;

thetaSegments = thetaSegments !== undefined ? Math.max(3, thetaSegments) : 8;
phiSegments = phiSegments !== undefined ? Math.max(1, phiSegments) : 1;

space = space || 0;

// buffers
var indices = [];
var vertices = [];
var normals = [];
var uvs = [];

// some helper variables
var segment;
var segmentStep;
var thetaStartRel;
var thetaEndRel;
var vertex = new THREE.Vector3();
var uv = new THREE.Vector2();
var j, i;

// generate vertices, normals and uvs
for (j = 0; j <= phiSegments; j++) {

thetaStartRel = Math.atan( space / (radius));
thetaEndRel = thetaLength - thetaStartRel;

segmentStep = (thetaEndRel - thetaStartRel) / thetaSegments;
segment = thetaStartRel;

for (i = 0; i <= thetaSegments; i++) {

vertices.push(vertex.x, vertex.y, vertex.z);

// normal
normals.push(0, 0, 1);

// uv
uv.x = (vertex.x / outerRadius + 1) / 2;
uv.y = (vertex.y / outerRadius + 1) / 2;

uvs.push(uv.x, uv.y);

segment += segmentStep;
}

// increase the radius for next row of vertices

}

// indices

for (j = 0; j < phiSegments; j++) {

var thetaSegmentLevel = j * (thetaSegments + 1);

for (i = 0; i < thetaSegments; i++) {

segment = i + thetaSegmentLevel;

var a = segment;
var b = segment + thetaSegments + 1;
var c = segment + thetaSegments + 2;
var d = segment + 1;

// faces

indices.push(a, b, d);
indices.push(b, c, d);

}

}

// build geometry
this.setIndex(indices);
Would be great to have a live code example 