How to create a curved arrow

HI !

I would like to draw curved arrow with only the norm and the position of the start of the arc.
I found how to draw an arc but I can’t make the end.

I don’t know if I am very clear but I would like to make an arc with an arrow at the end. (to symbolize an angle for example)

Someone would have any idea ?
thanks

You can take a look at the debug arrow shape.

Thanks for your answer.
I saw it. I tried to add a quaternion but nothing appeared… :confused:

Here is my class if you want… :slight_smile:

public class Arc extends Mesh {
	private Quaternion tempQuat = new Quaternion();
    private Vector3f tempVec = new Vector3f();
    
    private static final float[] positions = new float[]{
            0, 0, 0,
            0, 0, 1, // tip
            0.05f, 0, 0.9f, // tip right
            -0.05f, 0, 0.9f, // tip left
            0, 0.05f, 0.9f, // tip top
            0, -0.05f, 0.9f, // tip bottom
    };


	public Arc(int samples, float radius, float start, float end) {

		FloatBuffer vertexBuf = createBuffer(samples, radius, start, end);

		setBuffer(Type.Position, 3, vertexBuf);

		setMode(Mode.LineStrip);

		updateBound();

		updateCounts();
		
		

	}

	private FloatBuffer createBuffer(int sample, float radius, float start, float end) {

		FloatBuffer buf = BufferUtils.createVector3Buffer(sample);
		float len = 0;
		buf.rewind();

		float arc = end - start;

		for (int i = 0; i < sample; i++) {

			float theta = start + arc / (sample - 1) * i;

			float x = (float) (Math.cos(theta) * radius);

			float y = (float) (Math.sin(theta) * radius);

			buf.put(x).put(y).put(0);
			
			if (i == sample-1)
			{	
				Vector3f extent = new Vector3f(x,y,1);
			    Vector3f dir = extent.normalize();
			    len = extent.length();
			    tempQuat.lookAt(dir, Vector3f.UNIT_Y);
			    tempQuat.normalizeLocal();
			}

		}
		float[] newPositions = new float[positions.length];
        for (int i = 0; i < positions.length; i += 3) {
            Vector3f vec = tempVec.set(positions[i],
                    positions[i + 1],
                    positions[i + 2]);
            vec.multLocal(len);
            tempQuat.mult(vec, vec);

            newPositions[i] = vec.getX();
            newPositions[i + 1] = vec.getY();
            newPositions[i + 2] = vec.getZ();
        }

		return buf;

	}
	 
}

It might be difficult to create the arrow “barbs” in the same manner as the Arrow class does, the Arc mesh is created with Mode.LineStrip where the Arrow class uses Mode.Lines.
The Arrow class also defines the index buffer of the verts specifically, so that each line segment can have separately defined (but possibly co-located) endpoints.

Edit:
Here is an example I adjusted from your provided code to illustrate how the arrow “barbs” could be rotated from their base positions and added to the buffer by tracing the line out from the tip, then back to the tip before moving to the next. I adjusted the resulting scale of the arrowhead by the radius parameter, although other methods could be substituted.
(also adjusted the loops to use samples parameter to define the number of line segments, instead of number of total verts)

It’s not pretty, but it seems to work:

public class Arc extends Mesh {
    private Quaternion tempQuat = new Quaternion();
    private Vector3f tempVec = new Vector3f();
    private Vector3f endVec = new Vector3f();
    private Vector3f dir = new Vector3f();

    public Arc(int samples, float radius, float start, float end) {
        FloatBuffer vertexBuf = createBuffer(samples, radius, start, end);
        setBuffer(Type.Position, 3, vertexBuf);
        setMode(Mode.LineStrip);
        updateBound();
        updateCounts();
    }

    private FloatBuffer createBuffer(int sample, float radius, float start, float end) {
        int totalVerts = sample + 1 + 8; // + 8 verts for the 4 arrowhead lines
        FloatBuffer buf = BufferUtils.createVector3Buffer(totalVerts);
        buf.rewind();

        // base positions for the arrowhead lines, to be rotated to match the last sample segment
        Vector3f tipRight   = new Vector3f(  0.05f,      0, -0.1f ).multLocal(radius);
        Vector3f tipLeft    = new Vector3f( -0.05f,      0, -0.1f ).multLocal(radius);
        Vector3f tipTop     = new Vector3f(      0,  0.05f, -0.1f ).multLocal(radius);
        Vector3f tipBottom  = new Vector3f(      0, -0.05f, -0.1f ).multLocal(radius);
        
        float arc = end - start;
        for (int i = 0; i <= sample; i++) {
            float theta = start + arc / sample * i;
            float x = (float) (Math.cos(theta) * radius);
            float y = (float) (Math.sin(theta) * radius);
            buf.put(x).put(y).put(0);
            
            // record the two verts of the final line segment
            if (i == sample-1) {
                tempVec.set(x, y, 0f);
            }
            if (i == sample) {
                endVec.set(new Vector3f(x, y, 0f));
            }
        }
        
        // copy rotation from direction of last curve segment
        dir.set(endVec.subtract(tempVec));
        tempQuat.lookAt(dir.normalize(), Vector3f.UNIT_Y);
        tempQuat.normalizeLocal();
        // rotate the vectors representing the arrowhead lines' end positions
        tempQuat.mult(tipRight, tipRight);
        tempQuat.mult(tipLeft, tipLeft);
        tempQuat.mult(tipTop, tipTop);
        tempQuat.mult(tipBottom, tipBottom);
        // move points into the local space of the tip
        tipRight.addLocal(endVec);
        tipLeft.addLocal(endVec);
        tipTop.addLocal(endVec);
        tipBottom.addLocal(endVec);
        
        // add each point to the buffer, then add the tip again (traceback over the same line)
        float localX = endVec.getX();
        float localY = endVec.getY();
        float localZ = endVec.getZ();
        buf.put(tipRight.getX()).put(tipRight.getY()).put(tipRight.getZ());
         buf.put(localX).put(localY).put(localZ);
        buf.put(tipLeft.getX()).put(tipLeft.getY()).put(tipLeft.getZ());
         buf.put(localX).put(localY).put(localZ);
        buf.put(tipTop.getX()).put(tipTop.getY()).put(tipTop.getZ());
         buf.put(localX).put(localY).put(localZ);
        buf.put(tipBottom.getX()).put(tipBottom.getY()).put(tipBottom.getZ());
         buf.put(localX).put(localY).put(localZ); // unneeded -> just padding the buffer

        return buf;
    }
	 
}