Wrong side of the arc based on the angle from three points

Hello,

I’m trying to create an angle measurement based on 3 points. Points are meant to be dynamic and editable by user by dragging them on the surface of a mesh. I already have the angle but I have some problems showing the arc of the angle. It looks ok up to the point where the angle is 180°. After that the angle value is not increasing but decreasing and the arc is placed on the wrong side of the angle. It shows proper angle size, but it’s on the wrong side of the angle.

Code that I’m using to handle arc creation is following

  const updateArc = useCallback(() => {
    if (!arcRef.current) return;

    // copy vectors, otherwise next calculations have impact on original values
    P1.current.copy(start);
    P2.current.copy(vertex);
    P3.current.copy(end);

    const radius = Math.min(P1.current.distanceTo(P2.current), P3.current.distanceTo(P2.current)) / 2;

    const P2P1 = new Vector3().subVectors(P1.current, P2.current).normalize();
    const P2P3 = new Vector3().subVectors(P3.current, P2.current).normalize();

    /**
     * calculate angle based on the dot product
     * mathematically dot(P2P1,P2P3) = |P2P3|*|P2P3|*cosϕ
     * P2P1 & P2P3 are normalized so |P2P1| = |P2P3| = 1
     * dot(P2P1,P2P3) = cosϕ
     * ϕ = arccos(dot(P2P1,P2P3))
     * */
    const angle = Math.acos(P2P1.dot(P2P3));

    const tempArcPoints: number[] = [];

    const quaternion = new Quaternion();
    const axis = vertexNormal ?? new Vector3().crossVectors(P2P1, P2P3).normalize();

    for (let i = 0; i <= N; i++) {
      const stepAngle = angle * (i / N);
      const rotatedVector = P2P1.clone()
        .applyQuaternion(quaternion.setFromAxisAngle(axis, stepAngle))
        .multiplyScalar(radius);

      const point = rotatedVector.add(P2.current);

      tempArcPoints.push(point.x, point.y, point.z);
    }

    const geom = new LineGeometry();
    geom.setPositions(tempArcPoints.flat());
    arc.geometry = geom;
    arc.computeLineDistances();

  }, [start, vertex, end, vertexNormal, arc]);

and the usage:

<primitive dispose={undefined} object={arc} ref={arcRef}>
    <primitive
      dispose={undefined}
      object={lineMaterial}
      attach="material"
      color={isActive ? COLOR_TEAL_700 : COLOR_TEAL_200}
      resolution={new Vector2(1024, 1024)}
      linewidth={1}
      depthTest={false}
    />
  </primitive>

Angle for this code looks following
arc_1

i also tried to introduce reference vector (proposed by gpt) which looks llike this :

const referenceVector = new Vector3(0, 1, 0);

const axis =
  referenceVector.dot(new Vector3().crossVectors(P2P1, P2P3).normalize()) > 0
    ? vertexNormal
    : vertexNormal?.negate();

and it sometimes works but sometimes not…

arc_2

Does someone have any idea what would help to track proper arc side/vallue here?

Reference vector works properly, I was just negating negated axis vector on every drag event.

With following code

const axis =
  referenceVector.dot(new Vector3().crossVectors(P2P1, P2P3).normalize()) > 0
    ? vertexNormal
    : new Vector3().copy(vertexNormal).negate();

everything works fine!

arc_3

I think I prematurely signed this as a solution. It only works for the Y-axis, and I still need some more generic approach for this issue :thinking:

1 Like

You may not parts of the math you’re currently doing by hand - Vector3.angleTo and MathUtils may help you simplify the code.

Thanks for the hint, doing all by hand is great for the learning purpose as I’m not great at it, but knowing proper utils is better for efficiency :slight_smile: