Search code examples
reactjstypescriptthree.jsreact-three-fiber

How should I implement Three.Line2 using QuadraticBezierCurve3 as geometry with Typescript and r3f?


I am attempting to render curved arcs between two points on a 3D sphere representing the Globe. I have successfully implemented arcs using Three.Line like this:

const calcPosFromLatLonRad = (lat: number, lon: number, radius: number): Vector3 => {
  return new Vector3(
    -((radius) * Math.sin((90 - lat) * (Math.PI / 180))
    * Math.cos((lon + 180) * (Math.PI / 180))),
    ((radius) * Math.cos((90 - lat) * (Math.PI / 180))),
    ((radius) * Math.sin((90 - lat) * (Math.PI / 180))
    * Math.sin((lon + 180) * (Math.PI / 180))),
  );
}

import { ReactThreeFiber, extend } from '@react-three/fiber'

extend({ Line_: Line })

declare global {
  namespace JSX {
    interface IntrinsicElements {
      line_: ReactThreeFiber.Object3DNode<Line, typeof Line>
    }
  }
}

export const Arc = (
  {
    start,
    control,
    end,
  }:{
    start: number[],
    control: number[],
    end: number[],
  }) => {
  const curve = new QuadraticBezierCurve3(
    calcPosFromLatLonRad( start[0], start[1], 1.01 ),
    calcPosFromLatLonRad( control[0], control[1], 1.13 ),
    calcPosFromLatLonRad( end[0], end[1], 1.01 ),
  );

  const points = curve.getPoints( 50 );
  const geometry = new BufferGeometry().setFromPoints( points );
  const material = new LineBasicMaterial( {transparent:true, opacity:0.2, color: 0x119cec } );
  
  let uniforms = {
    time: {value: 0}
  }
  let m = new LineDashedMaterial({
    color: "red",
    dashSize: 0.001,
    gapSize: 0.007,
    })
  m.onBeforeCompile = shader => {
    shader.uniforms.time = uniforms.time;
    shader.fragmentShader = `
      uniform float time;
      ${shader.fragmentShader}
    `.replace(
      `vLineDistance,`,
      `vLineDistance - time,`
    );
  };

  return (
    <>
      <line_
      geometry={geometry}
      material={material}
      />
      <line_
      onUpdate={(line) => line.computeLineDistances()}
      geometry={geometry}
      material={m}
      />
    </>
  )
}

The lines are too thin and are barely visible at the scale I require, After some research I came across examples such as this and attempted to implement them, but ran into a couple of issues.

  1. Typescript seems to be causing problems when attempting to use 'line2', 'lineMaterial', and 'lineGeometry' as JSX elements, presumably in the same way that the default Three.Line does and requires manually extending them as I have in the above snippet, however I cannot work out how.

  2. When I attempted to create LineGeometry and give it the points from the bezier curve, it is not in the correct type and seems to want 'number[]', even on a conceptual level I don't understand how i'd convert a Vector3(which i believe are 3D space coordinates) into an array of numbers.

After looking through docs, other questions on stack overflow and attempting to understand examples like the one I posted I think I lack the foundational understanding of how React, Three and Typescript function on a lower level, I am struggling to get things to compile due to type issues so any guidance would be much appreciated.

As a bonus which might be relevant, my next step is to animate the dashed line to move along it's path ideally similar to this. I thought that any approach or solution to my main question might change with the requirement to animate it after the fact.


Solution

  • for anyone who comes across this, I solved here: https://codesandbox.io/s/intelligent-wind-pfqpmg

    The issue was resolved by extending the Line2 classes in the same way as Three.Line is in my original question, then creating those Lines in the Arc component and attaching them via hooks to a group JSX element.

    The Line2 class has a useful function for getting geometry from an existing Line so although probably not ideal, you can get it to work by creating a new Line with the curve points and then calling the fromLine function.

    I posted the question to reddit so more useful information and further explanation can be found there: https://www.reddit.com/r/threejs/comments/15kjaoe/implementing_threeline2_in_typescriptr3f/