Skip to content

Commit

Permalink
feat: plane sphere intersection
Browse files Browse the repository at this point in the history
  • Loading branch information
plantain-00 committed Jul 24, 2024
1 parent f19db68 commit 34c9b30
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 3 deletions.
61 changes: 60 additions & 1 deletion dev/webgl-3d-put.story.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react"
import { produce } from 'immer'
import * as twgl from 'twgl.js'
import { createWebgl3DRenderer, Graphic3d, useWindowSize, angleToRadian, Vec3, useGlobalKeyDown, Nullable, useUndoRedo, metaKeyIfMacElseCtrlKey, Menu, colorNumberToVec, NumberEditor, arcToPolyline, circleToArc, MenuItem, vecToColorNumber, SphereGeometry, CubeGeometry, Vec4, WeakmapCache, useLocalStorageState, Position3D, getLineAndSphereIntersectionPoints, position3DToVec3, slice3, vec3ToPosition3D, Button, GeneralFormPlane, CylinderGeometry, getLineAndPlaneIntersectionPoint, getThreePointPlane, getLineAndCylinderIntersectionPoints, getTwoLine3DIntersectionPoint, v3, getVerticesTriangles, getLineAndTrianglesIntersectionPoint, ConeGeometry, getLineAndConeIntersectionPoints } from "../src"
import { createWebgl3DRenderer, Graphic3d, useWindowSize, angleToRadian, Vec3, useGlobalKeyDown, Nullable, useUndoRedo, metaKeyIfMacElseCtrlKey, Menu, colorNumberToVec, NumberEditor, arcToPolyline, circleToArc, MenuItem, vecToColorNumber, SphereGeometry, CubeGeometry, Vec4, WeakmapCache, useLocalStorageState, Position3D, getLineAndSphereIntersectionPoints, position3DToVec3, slice3, vec3ToPosition3D, Button, GeneralFormPlane, CylinderGeometry, getLineAndPlaneIntersectionPoint, getThreePointPlane, getLineAndCylinderIntersectionPoints, getTwoLine3DIntersectionPoint, v3, getVerticesTriangles, getLineAndTrianglesIntersectionPoint, ConeGeometry, getLineAndConeIntersectionPoints, getPlaneSphereIntersection } from "../src"

export default () => {
const ref = React.useRef<HTMLCanvasElement | null>(null)
Expand Down Expand Up @@ -115,6 +115,15 @@ export default () => {
color: s.color,
position: [0, 0, 0],
}
} else if (s.geometry.type === 'line strip') {
return {
geometry: {
type: 'line strip',
points: s.geometry.points.flat(),
},
color: s.color,
position: [0, 0, 0],
}
}
return {
geometry: s.geometry,
Expand Down Expand Up @@ -160,6 +169,15 @@ export default () => {
color: [0, 1, 0, 1],
position: [0, 0, 0.5],
})
} else if (g.geometry.type === 'line strip') {
graphics.push({
geometry: {
type: 'line strip',
points: g.geometry.points.map(p => [p[0], p[1], p[2] + 0.5]).flat(),
},
color: [0, 1, 0, 1],
position: [0, 0, 0],
})
}
if (radius) {
const points = arcToPolyline(circleToArc({ x: 0, y: 0, r: radius }), 5)
Expand Down Expand Up @@ -349,6 +367,7 @@ export default () => {
const target1 = getGraphic(state1)
const target2 = getGraphic(state2)
let points: Vec3[] | undefined
let lines: Vec3[] | undefined
if (target1.geometry.type === 'lines') {
if (target2.geometry.type === 'lines') {
const p1 = slice3(target1.geometry.points)
Expand Down Expand Up @@ -415,6 +434,18 @@ export default () => {
...(vec3ToPosition3D(target1.position || [0, 0, 0])),
}
)
} else if (state2.geometry.type === 'triangle') {
const plane = getThreePointPlane(
position3DToVec3(state2.position),
position3DToVec3(state2.geometry.p1),
position3DToVec3(state2.geometry.p2),
)
if (plane) {
lines = getPlaneSphereIntersection(plane, {
radius: target1.geometry.radius,
...(vec3ToPosition3D(target1.position || [0, 0, 0])),
})
}
}
} else if (target1.geometry.type === 'cylinder') {
if (target2.geometry.type === 'lines') {
Expand Down Expand Up @@ -455,6 +486,18 @@ export default () => {
points = [p]
}
}
} else if (target2.geometry.type === 'sphere') {
const plane = getThreePointPlane(
position3DToVec3(state1.position),
position3DToVec3(state1.geometry.p1),
position3DToVec3(state1.geometry.p2),
)
if (plane) {
lines = getPlaneSphereIntersection(plane, {
radius: target2.geometry.radius,
...(vec3ToPosition3D(target2.position || [0, 0, 0])),
})
}
}
} else if (target1.geometry.type === 'cube') {
if (target2.geometry.type === 'lines') {
Expand All @@ -475,6 +518,19 @@ export default () => {
})
setStatus(undefined)
}
if (lines && lines.length > 0) {
setState(draft => {
draft.push({
geometry: {
type: 'line strip',
points: lines,
},
color: [0, 1, 0, 1],
position: { x: 0, y: 0, z: 0 },
})
})
setStatus(undefined)
}
}
}
}}
Expand Down Expand Up @@ -820,6 +876,9 @@ interface State {
type: 'triangle'
p1: Position3D
p2: Position3D
} | {
type: 'line strip'
points: Vec3[]
}
color: Vec4
position: Position3D
Expand Down
8 changes: 8 additions & 0 deletions src/utils/plane.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,11 @@ export function rotateDirectionByRadianOnPlane(direction: Vec3, radian: number,
if (!p1) return
return v3.normalize(p1)
}

export function getCoordinate(direction: Vec3): Tuple3<Vec3> {
direction = v3.normalize(direction)
const d1: Vec3 = isZero(direction[0]) && isZero(direction[1]) ? [1, 0, 0] : [0, 0, 1]
const d2 = v3.normalize(v3.cross(direction, d1))
const d3 = v3.cross(direction, d2)
return [d2, d3, direction]
}
22 changes: 20 additions & 2 deletions src/utils/sphere.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { arcToPolyline, circleToArc } from "./circle"
import { calculateEquation2 } from "./equation-calculater"
import { equals, isZero } from "./math"
import { v3 } from "./matrix"
import { matrix, v3 } from "./matrix"
import { GeneralFormPlane, getCoordinate, getPerpendicularPointToPlane } from "./plane"
import { Position3D, getPointByLengthAndDirection3D, getTwoPointsDistance3D, position3DToVec3, vec3ToPosition3D } from "./position"
import { Vec3 } from "./types"
import { getCoordinateMatrix, getCoordinateVec } from "./transform"
import { slice3, Tuple4, Vec3 } from "./types"
import { and, number } from "./validators"

export interface Sphere extends Position3D {
Expand Down Expand Up @@ -69,3 +72,18 @@ export function getParallelSpheresByDistance(sphere: Sphere, distance: number):
export function getSphereNormalAtPoint(sphere: Sphere, point: Vec3): Vec3 {
return v3.substract(point, position3DToVec3(sphere))
}

export function getPlaneSphereIntersection(plane: GeneralFormPlane, sphere: Sphere) {
const center = position3DToVec3(sphere)
const p = getPerpendicularPointToPlane(center, plane)
const direction = v3.substract(p, center)
const r = Math.sqrt(sphere.radius ** 2 - v3.lengthSquare(direction))
if (isNaN(r)) return
// x = r cos(t)
// y = r sin(t)
// z = 0
const coordinate: Tuple4<Vec3> = [p, ...getCoordinate(direction)]
const m = getCoordinateMatrix(coordinate)
if (!m) return
return arcToPolyline(circleToArc({ x: 0, y: 0, r }), 5).map(n => slice3(matrix.multiplyVec(m, getCoordinateVec([n.x, n.y, 0], coordinate))))
}

0 comments on commit 34c9b30

Please sign in to comment.