Search code examples
three.jsrotationequationreact-three-fiber

How to align object with drop shape function (Piriform Curve)


I am trying to place the objects based on an equation, so let us say that I want to draw a circle.

Please check my codesandbox

so I use this function to get the coordinates of x and y..

const getCircleCoordinates = (angle, distance = 25) => {
    angle *= Math.PI / 180
    let x = distance * Math.cos(angle),
        y = distance * Math.sin(angle)

    return { x, y, angle, distance }
}

and this is my loop to place the objects.

let i = -1
const theta = 360 / (36)
return <>
    <axesHelper args={[5, 5, 5]} />
    <Center>
        <group scale={scale}>
            {[...Array(36)].map((e, index) => {
                i++
                let { x, y, angle } = getCircleCoordinates(i * theta, hight, width)
                return <mesh
                    key={index}
                    scale-z={z}
                    position-x={x}
                    position-y={y}
                    rotation-x={Math.PI / 2}
                    rotation-y={angle}
                    lookAt={[0, 0, 0]}
                >
                    <torusGeometry args={[radius, tube, radialSeg, tubSeg]} />
                    <meshNormalMaterial
                        color="red"
                    // wireframe
                    />
                </mesh>
            })}
        </group>
    </Center>
</>

Now my issue is with the rotation, in the circle the rotation was simply the angle in radian, but what If I want to draw a square or triangle?

In my case, I want to draw a drop..

and this is the function...

const getDropCoordinates = (angle, hight = 15, width = 5) => {
    angle *= Math.PI / 180
    let x = width * Math.cos(angle) * (1 + Math.sin(angle)),
        y = hight * (1 + Math.sin(angle))

    return { x, y, angle }
}

Solution

  • According to

    You've got an array of points. Find the angle between two vectors, formed by (next - current) and (previous - current), multiply it with 0.5, rotate you mesh with this angle around Z-axis: i.imgur.com/2YFzU5W.png – prisoner849

    this function will get the right rotation value for the drop shape - it seems to work with all shapes.

      const getAngle = (nextCoordinates, previousCoordinates, x, y) => {
        let nextAngle = Math.atan2(nextCoordinates.y - y, nextCoordinates.x - x)
        let previousAngle = Math.atan2(previousCoordinates.y - y, previousCoordinates.x - x)
    
        return (nextAngle + previousAngle) / 2
      }
    

    I edited my loop to be

    {[...Array(36)].map((e, index) => {
                i++
                // let getShapeCoordinates = getCircleCoordinates
                let getShapeCoordinates = getDropCoordinates
                let { x, y } = getShapeCoordinates(i * theta, hight, width)
                let nextCoordinates = getShapeCoordinates((i + 1) * theta, hight, width)
                let previousCoordinates = getShapeCoordinates((i - 1) * theta, hight, width)
                let angle = getAngle(nextCoordinates, previousCoordinates, x, y)
                // console.log(a)
    
                return (
                  <mesh
                    key={index}
                    scale-z={z}
                    position-x={x}
                    position-y={y}
                    rotation-x={Math.PI / 2}
                    // Here where I pass the rotation
                    rotation-y={angle}>
                    <torusGeometry args={[radius, tube, radialSeg, tubSeg]} />
                    <meshNormalMaterial
                      color="red"
                      // wireframe
                    />
                  </mesh>
                )
              })}