Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 1 addition & 25 deletions packages/editor/src/components/tools/wall/wall-drafting.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,32 @@
import { useScene, type WallNode, WallNode as WallSchema } from '@pascal-app/core'
import { useViewer } from '@pascal-app/viewer'
import { sfxEmitter } from '../../../lib/sfx-bus'

export type WallPlanPoint = [number, number]

export const WALL_GRID_STEP = 0.5
export const WALL_JOIN_SNAP_RADIUS = 0.35
export const WALL_MIN_LENGTH = 0.01

export const WALL_MIN_LENGTH = 0.5
function distanceSquared(a: WallPlanPoint, b: WallPlanPoint): number {
const dx = a[0] - b[0]
const dz = a[1] - b[1]
return dx * dx + dz * dz
}

function snapScalarToGrid(value: number, step = WALL_GRID_STEP): number {
return Math.round(value / step) * step
}

export function snapPointToGrid(point: WallPlanPoint, step = WALL_GRID_STEP): WallPlanPoint {
return [snapScalarToGrid(point[0], step), snapScalarToGrid(point[1], step)]
}

export function snapPointTo45Degrees(start: WallPlanPoint, cursor: WallPlanPoint): WallPlanPoint {
const dx = cursor[0] - start[0]
const dz = cursor[1] - start[1]
const angle = Math.atan2(dz, dx)
const snappedAngle = Math.round(angle / (Math.PI / 4)) * (Math.PI / 4)
const distance = Math.sqrt(dx * dx + dz * dz)

return snapPointToGrid([
start[0] + Math.cos(snappedAngle) * distance,
start[1] + Math.sin(snappedAngle) * distance,
])
}

function projectPointOntoWall(point: WallPlanPoint, wall: WallNode): WallPlanPoint | null {
const [x1, z1] = wall.start
const [x2, z2] = wall.end
Expand All @@ -44,15 +36,12 @@ function projectPointOntoWall(point: WallPlanPoint, wall: WallNode): WallPlanPoi
if (lengthSquared < 1e-9) {
return null
}

const t = ((point[0] - x1) * dx + (point[1] - z1) * dz) / lengthSquared
if (t <= 0 || t >= 1) {
return null
}

return [x1 + dx * t, z1 + dz * t]
}

export function findWallSnapTarget(
point: WallPlanPoint,
walls: WallNode[],
Expand All @@ -62,12 +51,10 @@ export function findWallSnapTarget(
const radiusSquared = (options?.radius ?? WALL_JOIN_SNAP_RADIUS) ** 2
let bestTarget: WallPlanPoint | null = null
let bestDistanceSquared = Number.POSITIVE_INFINITY

for (const wall of walls) {
if (ignoreWallIds.has(wall.id)) {
continue
}

const candidates: Array<WallPlanPoint | null> = [
wall.start,
wall.end,
Expand All @@ -77,23 +64,19 @@ export function findWallSnapTarget(
if (!candidate) {
continue
}

const candidateDistanceSquared = distanceSquared(point, candidate)
if (
candidateDistanceSquared > radiusSquared ||
candidateDistanceSquared >= bestDistanceSquared
) {
continue
}

bestTarget = candidate
bestDistanceSquared = candidateDistanceSquared
}
}

return bestTarget
}

export function snapWallDraftPoint(args: {
point: WallPlanPoint
walls: WallNode[]
Expand All @@ -103,38 +86,31 @@ export function snapWallDraftPoint(args: {
}): WallPlanPoint {
const { point, walls, start, angleSnap = false, ignoreWallIds } = args
const basePoint = start && angleSnap ? snapPointTo45Degrees(start, point) : snapPointToGrid(point)

return (
findWallSnapTarget(basePoint, walls, {
ignoreWallIds,
}) ?? basePoint
)
}

export function isWallLongEnough(start: WallPlanPoint, end: WallPlanPoint): boolean {
return distanceSquared(start, end) >= WALL_MIN_LENGTH * WALL_MIN_LENGTH
}

export function createWallOnCurrentLevel(
start: WallPlanPoint,
end: WallPlanPoint,
): WallNode | null {
const currentLevelId = useViewer.getState().selection.levelId
const { createNode, nodes } = useScene.getState()

if (!(currentLevelId && isWallLongEnough(start, end))) {
return null
}

const wallCount = Object.values(nodes).filter((node) => node.type === 'wall').length
const wall = WallSchema.parse({
name: `Wall ${wallCount + 1}`,
start,
end,
})

createNode(wall, currentLevelId)
sfxEmitter.emit('sfx:structure-build')

return wall
}
6 changes: 3 additions & 3 deletions packages/editor/src/components/tools/wall/wall-tool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DoubleSide, type Group, type Mesh, Shape, ShapeGeometry, Vector3 } from
import { EDITOR_LAYER } from '../../../lib/constants'
import { sfxEmitter } from '../../../lib/sfx-bus'
import { CursorSphere } from '../shared/cursor-sphere'
import { createWallOnCurrentLevel, snapWallDraftPoint, type WallPlanPoint } from './wall-drafting'
import { createWallOnCurrentLevel, snapWallDraftPoint, WALL_MIN_LENGTH, type WallPlanPoint } from './wall-drafting'

const WALL_HEIGHT = 2.5

Expand All @@ -17,7 +17,7 @@ const updateWallPreview = (mesh: Mesh, start: Vector3, end: Vector3) => {
const direction = new Vector3(end.x - start.x, 0, end.z - start.z)
const length = direction.length()

if (length < 0.01) {
if (length < WALL_MIN_LENGTH) {
mesh.visible = false
return
}
Expand Down Expand Up @@ -142,7 +142,7 @@ export const WallTool: React.FC = () => {
endingPoint.current.set(snappedEnd[0], event.position[1], snappedEnd[1])
const dx = endingPoint.current.x - startingPoint.current.x
const dz = endingPoint.current.z - startingPoint.current.z
if (dx * dx + dz * dz < 0.01 * 0.01) return
if (dx * dx + dz * dz < WALL_MIN_LENGTH * WALL_MIN_LENGTH) return
createWallOnCurrentLevel(
[startingPoint.current.x, startingPoint.current.z],
[endingPoint.current.x, endingPoint.current.z],
Expand Down