fixed wall mitering
This commit is contained in:
@@ -28,6 +28,7 @@ interface Junction {
|
|||||||
export interface MiterData {
|
export interface MiterData {
|
||||||
left: Point2D
|
left: Point2D
|
||||||
right: Point2D
|
right: Point2D
|
||||||
|
center: Point2D // The junction meeting point
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map of wallId -> { start?: MiterData, end?: MiterData }
|
// Map of wallId -> { start?: MiterData, end?: MiterData }
|
||||||
@@ -44,13 +45,6 @@ function pointToKey(p: Point2D, tolerance = TOLERANCE): string {
|
|||||||
return `${Math.round(p.x * snap)},${Math.round(p.y * snap)}`
|
return `${Math.round(p.x * snap)},${Math.round(p.y * snap)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function getOutgoingVector(wall: WallNode, endType: 'start' | 'end'): Point2D {
|
|
||||||
if (endType === 'start') {
|
|
||||||
return { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
|
|
||||||
}
|
|
||||||
return { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] }
|
|
||||||
}
|
|
||||||
|
|
||||||
function createLineFromPointAndVector(p: Point2D, v: Point2D): LineEquation {
|
function createLineFromPointAndVector(p: Point2D, v: Point2D): LineEquation {
|
||||||
const a = -v.y
|
const a = -v.y
|
||||||
const b = v.x
|
const b = v.x
|
||||||
@@ -66,19 +60,10 @@ function intersectLines(l1: LineEquation, l2: LineEquation): Point2D | null {
|
|||||||
return { x, y }
|
return { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalize(v: Point2D): Point2D {
|
|
||||||
const len = Math.sqrt(v.x * v.x + v.y * v.y)
|
|
||||||
if (len < 1e-9) return { x: 0, y: 0 }
|
|
||||||
return { x: v.x / len, y: v.y / len }
|
|
||||||
}
|
|
||||||
|
|
||||||
function dot(a: Point2D, b: Point2D): number {
|
function dot(a: Point2D, b: Point2D): number {
|
||||||
return a.x * b.x + a.y * b.y
|
return a.x * b.x + a.y * b.y
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a point lies on a wall segment (excluding endpoints)
|
|
||||||
*/
|
|
||||||
function pointOnWallSegment(
|
function pointOnWallSegment(
|
||||||
point: Point2D,
|
point: Point2D,
|
||||||
wallStart: Point2D,
|
wallStart: Point2D,
|
||||||
@@ -90,14 +75,10 @@ function pointOnWallSegment(
|
|||||||
if (wallLen < 1e-9) return false
|
if (wallLen < 1e-9) return false
|
||||||
|
|
||||||
const toPoint = { x: point.x - wallStart.x, y: point.y - wallStart.y }
|
const toPoint = { x: point.x - wallStart.x, y: point.y - wallStart.y }
|
||||||
|
|
||||||
// Project point onto wall line
|
|
||||||
const t = dot(toPoint, wallVec) / (wallLen * wallLen)
|
const t = dot(toPoint, wallVec) / (wallLen * wallLen)
|
||||||
|
|
||||||
// Check if within segment (with margin to exclude endpoints)
|
|
||||||
if (t <= tolerance / wallLen || t >= 1 - tolerance / wallLen) return false
|
if (t <= tolerance / wallLen || t >= 1 - tolerance / wallLen) return false
|
||||||
|
|
||||||
// Check perpendicular distance
|
|
||||||
const projX = wallStart.x + t * wallVec.x
|
const projX = wallStart.x + t * wallVec.x
|
||||||
const projY = wallStart.y + t * wallVec.y
|
const projY = wallStart.y + t * wallVec.y
|
||||||
const dist = Math.sqrt((point.x - projX) ** 2 + (point.y - projY) ** 2)
|
const dist = Math.sqrt((point.x - projX) ** 2 + (point.y - projY) ** 2)
|
||||||
@@ -109,15 +90,10 @@ function pointOnWallSegment(
|
|||||||
// JUNCTION DETECTION
|
// JUNCTION DETECTION
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
interface JunctionResult {
|
|
||||||
junctions: Map<string, Junction>
|
|
||||||
throughWalls: Map<string, WallNode> // junctionKey -> host wall that the junction lies on
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds all junctions (where wall endpoints meet, including T-junctions on wall segments)
|
* Finds all junctions where wall endpoints meet
|
||||||
*/
|
*/
|
||||||
function findCornerJunctions(walls: WallNode[]): JunctionResult {
|
function findJunctions(walls: WallNode[]): Map<string, Junction> {
|
||||||
const junctionMap = new Map<string, Junction>()
|
const junctionMap = new Map<string, Junction>()
|
||||||
|
|
||||||
for (const wall of walls) {
|
for (const wall of walls) {
|
||||||
@@ -138,29 +114,6 @@ function findCornerJunctions(walls: WallNode[]): JunctionResult {
|
|||||||
junctionMap.get(endKey)!.walls.push({ wall, endType: 'end' })
|
junctionMap.get(endKey)!.walls.push({ wall, endType: 'end' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each junction point, check if it lies on any wall's segment (T-junction)
|
|
||||||
// Store this info separately - the host wall should NOT be modified
|
|
||||||
const throughWallsAtJunction = new Map<string, WallNode>() // junctionKey -> host wall
|
|
||||||
|
|
||||||
for (const [key, junction] of junctionMap) {
|
|
||||||
const wallIdsInJunction = new Set(junction.walls.map((w) => w.wall.id))
|
|
||||||
|
|
||||||
for (const wall of walls) {
|
|
||||||
if (wallIdsInJunction.has(wall.id)) continue
|
|
||||||
|
|
||||||
const wallStart: Point2D = { x: wall.start[0], y: wall.start[1] }
|
|
||||||
const wallEnd: Point2D = { x: wall.end[0], y: wall.end[1] }
|
|
||||||
|
|
||||||
// Check if junction point lies on this wall's segment
|
|
||||||
if (pointOnWallSegment(junction.point, wallStart, wallEnd)) {
|
|
||||||
// Store the through wall separately - don't add to junction.walls
|
|
||||||
// The host wall should NOT get miter data
|
|
||||||
throughWallsAtJunction.set(key, wall)
|
|
||||||
break // Only need one through wall per junction
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only keep junctions with 2+ walls
|
// Only keep junctions with 2+ walls
|
||||||
const actualJunctions = new Map<string, Junction>()
|
const actualJunctions = new Map<string, Junction>()
|
||||||
for (const [key, junction] of junctionMap) {
|
for (const [key, junction] of junctionMap) {
|
||||||
@@ -169,14 +122,112 @@ function findCornerJunctions(walls: WallNode[]): JunctionResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { junctions: actualJunctions, throughWalls: throughWallsAtJunction }
|
return actualJunctions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// MITER CALCULATION (Simple approach from prototype)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
interface ProcessedWall {
|
||||||
|
wallId: string
|
||||||
|
endType: 'start' | 'end'
|
||||||
|
angle: number
|
||||||
|
edgeA: LineEquation // Left edge (CCW from outgoing direction)
|
||||||
|
edgeB: LineEquation // Right edge (CW from outgoing direction)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates miter intersections for a junction
|
||||||
|
* Simple algorithm from prototype:
|
||||||
|
* 1. Get outgoing vector for each wall (pointing away from junction)
|
||||||
|
* 2. Calculate left/right edge lines offset by halfThickness
|
||||||
|
* 3. Sort walls by outgoing angle
|
||||||
|
* 4. Intersect adjacent edges: wall[i].edgeA ∩ wall[i+1].edgeB
|
||||||
|
* 5. Assign: wall[k].left = intersection[k], wall[k].right = intersection[k-1]
|
||||||
|
*/
|
||||||
|
function calculateJunctionMiters(
|
||||||
|
junction: Junction,
|
||||||
|
getThickness: (wall: WallNode) => number,
|
||||||
|
): Map<string, MiterData> {
|
||||||
|
const { point, walls } = junction
|
||||||
|
const result = new Map<string, MiterData>()
|
||||||
|
const processedWalls: ProcessedWall[] = []
|
||||||
|
|
||||||
|
// Process each wall at this junction
|
||||||
|
for (const { wall, endType } of walls) {
|
||||||
|
const halfT = getThickness(wall) / 2
|
||||||
|
|
||||||
|
// Outgoing vector (pointing away from junction)
|
||||||
|
const v =
|
||||||
|
endType === 'start'
|
||||||
|
? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
|
||||||
|
: { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] }
|
||||||
|
|
||||||
|
const L = Math.sqrt(v.x * v.x + v.y * v.y)
|
||||||
|
if (L < 1e-9) continue
|
||||||
|
|
||||||
|
// Perpendicular unit vector (90° CCW = "left" of outgoing direction)
|
||||||
|
const nUnit = { x: -v.y / L, y: v.x / L }
|
||||||
|
|
||||||
|
// Points on left (A) and right (B) edges at the junction
|
||||||
|
const pA = { x: point.x + nUnit.x * halfT, y: point.y + nUnit.y * halfT }
|
||||||
|
const pB = { x: point.x - nUnit.x * halfT, y: point.y - nUnit.y * halfT }
|
||||||
|
|
||||||
|
// Edge lines
|
||||||
|
const edgeA = createLineFromPointAndVector(pA, v)
|
||||||
|
const edgeB = createLineFromPointAndVector(pB, v)
|
||||||
|
|
||||||
|
// Angle for sorting
|
||||||
|
const angle = Math.atan2(v.y, v.x)
|
||||||
|
|
||||||
|
processedWalls.push({ wallId: wall.id, endType, angle, edgeA, edgeB })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by outgoing angle
|
||||||
|
processedWalls.sort((a, b) => a.angle - b.angle)
|
||||||
|
|
||||||
|
const n = processedWalls.length
|
||||||
|
if (n < 2) return result
|
||||||
|
|
||||||
|
// Calculate intersections between adjacent walls
|
||||||
|
const intersections: Point2D[] = []
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
const wall1 = processedWalls[i]!
|
||||||
|
const wall2 = processedWalls[(i + 1) % n]!
|
||||||
|
|
||||||
|
// Intersect left edge of wall1 with right edge of wall2
|
||||||
|
const intersection = intersectLines(wall1.edgeA, wall2.edgeB)
|
||||||
|
|
||||||
|
// If parallel, use junction center
|
||||||
|
intersections.push(intersection ?? point)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign miter data to each wall
|
||||||
|
// wall[k].left = intersection[k], wall[k].right = intersection[k-1]
|
||||||
|
for (let k = 0; k < n; k++) {
|
||||||
|
const wall = processedWalls[k]!
|
||||||
|
const prevIdx = (k - 1 + n) % n
|
||||||
|
|
||||||
|
result.set(wall.wallId, {
|
||||||
|
left: intersections[k]!,
|
||||||
|
right: intersections[prevIdx]!,
|
||||||
|
center: point, // Junction center point
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// T-JUNCTION HANDLING
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds T-junctions where a wall endpoint meets another wall's side
|
* Finds T-junctions where a wall endpoint meets another wall's side
|
||||||
*/
|
*/
|
||||||
function findTJunctions(walls: WallNode[]): Map<string, Junction> {
|
function findTJunctions(walls: WallNode[]): Map<string, { junction: Junction; hostWall: WallNode }> {
|
||||||
const tJunctions = new Map<string, Junction>()
|
const tJunctions = new Map<string, { junction: Junction; hostWall: WallNode }>()
|
||||||
|
|
||||||
for (const wall of walls) {
|
for (const wall of walls) {
|
||||||
const endpoints: { pt: Point2D; endType: 'start' | 'end' }[] = [
|
const endpoints: { pt: Point2D; endType: 'start' | 'end' }[] = [
|
||||||
@@ -187,36 +238,28 @@ function findTJunctions(walls: WallNode[]): Map<string, Junction> {
|
|||||||
for (const { pt, endType } of endpoints) {
|
for (const { pt, endType } of endpoints) {
|
||||||
const key = pointToKey(pt)
|
const key = pointToKey(pt)
|
||||||
|
|
||||||
// Skip if this is already a corner junction
|
|
||||||
// (will be handled by findCornerJunctions)
|
|
||||||
|
|
||||||
for (const otherWall of walls) {
|
for (const otherWall of walls) {
|
||||||
if (otherWall.id === wall.id) continue
|
if (otherWall.id === wall.id) continue
|
||||||
|
|
||||||
const otherStart: Point2D = { x: otherWall.start[0], y: otherWall.start[1] }
|
const otherStart: Point2D = { x: otherWall.start[0], y: otherWall.start[1] }
|
||||||
const otherEnd: Point2D = { x: otherWall.end[0], y: otherWall.end[1] }
|
const otherEnd: Point2D = { x: otherWall.end[0], y: otherWall.end[1] }
|
||||||
|
|
||||||
// Check if endpoint touches the other wall's endpoints
|
// Skip if touching endpoints (handled by regular junctions)
|
||||||
const touchesStart = pointToKey(pt) === pointToKey(otherStart)
|
if (pointToKey(pt) === pointToKey(otherStart)) continue
|
||||||
const touchesEnd = pointToKey(pt) === pointToKey(otherEnd)
|
if (pointToKey(pt) === pointToKey(otherEnd)) continue
|
||||||
if (touchesStart || touchesEnd) continue
|
|
||||||
|
|
||||||
// Check if endpoint lies on the other wall's segment
|
// Check if endpoint lies on the other wall's segment
|
||||||
if (pointOnWallSegment(pt, otherStart, otherEnd)) {
|
if (pointOnWallSegment(pt, otherStart, otherEnd)) {
|
||||||
if (!tJunctions.has(key)) {
|
if (!tJunctions.has(key)) {
|
||||||
tJunctions.set(key, { point: pt, walls: [] })
|
tJunctions.set(key, {
|
||||||
}
|
junction: { point: pt, walls: [] },
|
||||||
const junction = tJunctions.get(key)!
|
hostWall: otherWall,
|
||||||
|
})
|
||||||
// Add the incoming wall if not already present
|
|
||||||
if (!junction.walls.some((w) => w.wall.id === wall.id && w.endType === endType)) {
|
|
||||||
junction.walls.push({ wall, endType })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the host wall as a "through" wall (we'll handle it specially)
|
const entry = tJunctions.get(key)!
|
||||||
// Use 'start' as a convention for through walls
|
if (!entry.junction.walls.some((w) => w.wall.id === wall.id && w.endType === endType)) {
|
||||||
if (!junction.walls.some((w) => w.wall.id === otherWall.id)) {
|
entry.junction.walls.push({ wall, endType })
|
||||||
junction.walls.push({ wall: otherWall, endType: 'start' })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -226,253 +269,75 @@ function findTJunctions(walls: WallNode[]): Map<string, Junction> {
|
|||||||
return tJunctions
|
return tJunctions
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// MITER CALCULATION
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates mitered corners for a junction (including T-junctions with through walls)
|
* Calculates miter for T-junction (wall endpoint meeting another wall's side)
|
||||||
* @param throughWall - Optional wall that the junction lies on (for T-junctions)
|
|
||||||
*/
|
|
||||||
function calculateCornerMiters(
|
|
||||||
junction: Junction,
|
|
||||||
getThickness: (wall: WallNode) => number,
|
|
||||||
throughWall?: WallNode,
|
|
||||||
): Map<string, MiterData> {
|
|
||||||
const { point, walls } = junction
|
|
||||||
const result = new Map<string, MiterData>()
|
|
||||||
|
|
||||||
// If there's a through wall, handle as combined corner + T-junction
|
|
||||||
// The through wall is NOT modified - only incoming walls get miter data
|
|
||||||
if (throughWall) {
|
|
||||||
const hostHalfT = getThickness(throughWall) / 2
|
|
||||||
const hostDir = normalize({
|
|
||||||
x: throughWall.end[0] - throughWall.start[0],
|
|
||||||
y: throughWall.end[1] - throughWall.start[1],
|
|
||||||
})
|
|
||||||
const hostNormal = { x: -hostDir.y, y: hostDir.x }
|
|
||||||
|
|
||||||
// Host wall edge points at junction
|
|
||||||
const hostLeft = { x: point.x + hostNormal.x * hostHalfT, y: point.y + hostNormal.y * hostHalfT }
|
|
||||||
const hostRight = { x: point.x - hostNormal.x * hostHalfT, y: point.y - hostNormal.y * hostHalfT }
|
|
||||||
const hostEdgeLeft = createLineFromPointAndVector(hostLeft, hostDir)
|
|
||||||
const hostEdgeRight = createLineFromPointAndVector(hostRight, hostDir)
|
|
||||||
|
|
||||||
// Build processed list for incoming walls
|
|
||||||
const incomingProcessed: {
|
|
||||||
wallId: string
|
|
||||||
angle: number
|
|
||||||
edgeLeft: LineEquation
|
|
||||||
edgeRight: LineEquation
|
|
||||||
defaultLeft: Point2D
|
|
||||||
defaultRight: Point2D
|
|
||||||
approachDot: number
|
|
||||||
}[] = []
|
|
||||||
|
|
||||||
for (const { wall, endType } of walls) {
|
|
||||||
const halfT = getThickness(wall) / 2
|
|
||||||
const v = getOutgoingVector(wall, endType)
|
|
||||||
const vNorm = normalize(v)
|
|
||||||
|
|
||||||
if (Math.abs(vNorm.x) < 1e-9 && Math.abs(vNorm.y) < 1e-9) continue
|
|
||||||
|
|
||||||
const normal = { x: -vNorm.y, y: vNorm.x }
|
|
||||||
const leftPt = { x: point.x + normal.x * halfT, y: point.y + normal.y * halfT }
|
|
||||||
const rightPt = { x: point.x - normal.x * halfT, y: point.y - normal.y * halfT }
|
|
||||||
|
|
||||||
const incomingDir = { x: -vNorm.x, y: -vNorm.y }
|
|
||||||
const approachDot = dot(incomingDir, hostNormal)
|
|
||||||
|
|
||||||
incomingProcessed.push({
|
|
||||||
wallId: wall.id,
|
|
||||||
angle: Math.atan2(v.y, v.x),
|
|
||||||
edgeLeft: createLineFromPointAndVector(leftPt, v),
|
|
||||||
edgeRight: createLineFromPointAndVector(rightPt, v),
|
|
||||||
defaultLeft: leftPt,
|
|
||||||
defaultRight: rightPt,
|
|
||||||
approachDot,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize all walls with default values
|
|
||||||
for (const w of incomingProcessed) {
|
|
||||||
result.set(w.wallId, { left: w.defaultLeft, right: w.defaultRight })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Group walls by side, then process each side separately
|
|
||||||
// Walls at a T-junction don't form a closed loop - they all face toward the host
|
|
||||||
const leftSideWalls = incomingProcessed.filter((w) => w.approachDot > 0)
|
|
||||||
const rightSideWalls = incomingProcessed.filter((w) => w.approachDot <= 0)
|
|
||||||
|
|
||||||
for (const sideWalls of [leftSideWalls, rightSideWalls]) {
|
|
||||||
if (sideWalls.length === 0) continue
|
|
||||||
|
|
||||||
// Determine which host edge this side approaches
|
|
||||||
const targetHostEdge = sideWalls[0]!.approachDot > 0 ? hostEdgeRight : hostEdgeLeft
|
|
||||||
|
|
||||||
// For T-junctions: ALL edges of ALL walls on this side meet the host surface
|
|
||||||
// This ensures walls stop at the host and don't go through it
|
|
||||||
for (const w of sideWalls) {
|
|
||||||
const leftInt = intersectLines(w.edgeLeft, targetHostEdge)
|
|
||||||
const rightInt = intersectLines(w.edgeRight, targetHostEdge)
|
|
||||||
if (leftInt) result.get(w.wallId)!.left = leftInt
|
|
||||||
if (rightInt) result.get(w.wallId)!.right = rightInt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard corner junction processing (no through wall)
|
|
||||||
const processed: {
|
|
||||||
wallId: string
|
|
||||||
angle: number
|
|
||||||
edgeLeft: LineEquation
|
|
||||||
edgeRight: LineEquation
|
|
||||||
defaultLeft: Point2D
|
|
||||||
defaultRight: Point2D
|
|
||||||
}[] = []
|
|
||||||
|
|
||||||
for (const { wall, endType } of walls) {
|
|
||||||
const halfT = getThickness(wall) / 2
|
|
||||||
const v = getOutgoingVector(wall, endType)
|
|
||||||
const vNorm = normalize(v)
|
|
||||||
|
|
||||||
if (Math.abs(vNorm.x) < 1e-9 && Math.abs(vNorm.y) < 1e-9) continue
|
|
||||||
|
|
||||||
const normal = { x: -vNorm.y, y: vNorm.x }
|
|
||||||
const leftPt = { x: point.x + normal.x * halfT, y: point.y + normal.y * halfT }
|
|
||||||
const rightPt = { x: point.x - normal.x * halfT, y: point.y - normal.y * halfT }
|
|
||||||
|
|
||||||
processed.push({
|
|
||||||
wallId: wall.id,
|
|
||||||
angle: Math.atan2(v.y, v.x),
|
|
||||||
edgeLeft: createLineFromPointAndVector(leftPt, v),
|
|
||||||
edgeRight: createLineFromPointAndVector(rightPt, v),
|
|
||||||
defaultLeft: leftPt,
|
|
||||||
defaultRight: rightPt,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by angle for proper adjacency
|
|
||||||
processed.sort((a, b) => a.angle - b.angle)
|
|
||||||
|
|
||||||
const n = processed.length
|
|
||||||
if (n < 2) return result
|
|
||||||
|
|
||||||
// Initialize with defaults
|
|
||||||
for (const p of processed) {
|
|
||||||
result.set(p.wallId, { left: p.defaultLeft, right: p.defaultRight })
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate intersections between adjacent walls
|
|
||||||
for (let i = 0; i < n; i++) {
|
|
||||||
const curr = processed[i]!
|
|
||||||
const next = processed[(i + 1) % n]!
|
|
||||||
|
|
||||||
const intersection = intersectLines(curr.edgeLeft, next.edgeRight)
|
|
||||||
|
|
||||||
if (intersection) {
|
|
||||||
result.get(curr.wallId)!.left = intersection
|
|
||||||
result.get(next.wallId)!.right = intersection
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates miter for a T-junction (wall endpoint meeting another wall's side)
|
|
||||||
*/
|
*/
|
||||||
function calculateTJunctionMiters(
|
function calculateTJunctionMiters(
|
||||||
junction: Junction,
|
junction: Junction,
|
||||||
|
hostWall: WallNode,
|
||||||
getThickness: (wall: WallNode) => number,
|
getThickness: (wall: WallNode) => number,
|
||||||
): Map<string, MiterData> {
|
): Map<string, MiterData> {
|
||||||
const { point, walls } = junction
|
const { point, walls } = junction
|
||||||
const result = new Map<string, MiterData>()
|
const result = new Map<string, MiterData>()
|
||||||
|
|
||||||
// Separate incoming walls (those with endpoint at junction) from host wall
|
// Host wall direction and normal
|
||||||
const incomingWalls: WallEndpoint[] = []
|
const hostDir = {
|
||||||
let hostWall: WallNode | null = null
|
|
||||||
|
|
||||||
for (const { wall, endType } of walls) {
|
|
||||||
const wallStart: Point2D = { x: wall.start[0], y: wall.start[1] }
|
|
||||||
const wallEnd: Point2D = { x: wall.end[0], y: wall.end[1] }
|
|
||||||
|
|
||||||
const startKey = pointToKey(wallStart)
|
|
||||||
const endKey = pointToKey(wallEnd)
|
|
||||||
const junctionKey = pointToKey(point)
|
|
||||||
|
|
||||||
if (startKey === junctionKey || endKey === junctionKey) {
|
|
||||||
incomingWalls.push({ wall, endType })
|
|
||||||
} else {
|
|
||||||
hostWall = wall
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hostWall || incomingWalls.length === 0) return result
|
|
||||||
|
|
||||||
// If there are multiple incoming walls, use corner miter logic with throughWall
|
|
||||||
// This handles cases where walls meet at a T-junction point but weren't grouped as a corner junction
|
|
||||||
if (incomingWalls.length >= 2) {
|
|
||||||
const cornerJunction: Junction = { point, walls: incomingWalls }
|
|
||||||
return calculateCornerMiters(cornerJunction, getThickness, hostWall)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single incoming wall: handle as simple T-junction
|
|
||||||
// Get host wall direction and normal
|
|
||||||
const hostDir = normalize({
|
|
||||||
x: hostWall.end[0] - hostWall.start[0],
|
x: hostWall.end[0] - hostWall.start[0],
|
||||||
y: hostWall.end[1] - hostWall.start[1],
|
y: hostWall.end[1] - hostWall.start[1],
|
||||||
})
|
}
|
||||||
const hostNormal = { x: -hostDir.y, y: hostDir.x }
|
const hostLen = Math.sqrt(hostDir.x * hostDir.x + hostDir.y * hostDir.y)
|
||||||
|
if (hostLen < 1e-9) return result
|
||||||
|
|
||||||
|
const hostDirNorm = { x: hostDir.x / hostLen, y: hostDir.y / hostLen }
|
||||||
|
const hostNormal = { x: -hostDirNorm.y, y: hostDirNorm.x }
|
||||||
const hostHalfT = getThickness(hostWall) / 2
|
const hostHalfT = getThickness(hostWall) / 2
|
||||||
|
|
||||||
// Host wall edge points at junction
|
// Host wall edge lines at the junction point
|
||||||
const hostLeft = { x: point.x + hostNormal.x * hostHalfT, y: point.y + hostNormal.y * hostHalfT }
|
const hostLeft = { x: point.x + hostNormal.x * hostHalfT, y: point.y + hostNormal.y * hostHalfT }
|
||||||
const hostRight = {
|
const hostRight = { x: point.x - hostNormal.x * hostHalfT, y: point.y - hostNormal.y * hostHalfT }
|
||||||
x: point.x - hostNormal.x * hostHalfT,
|
const hostEdgeLeft = createLineFromPointAndVector(hostLeft, hostDirNorm)
|
||||||
y: point.y - hostNormal.y * hostHalfT,
|
const hostEdgeRight = createLineFromPointAndVector(hostRight, hostDirNorm)
|
||||||
}
|
|
||||||
|
|
||||||
// For each incoming wall, extend to meet the host wall's edges
|
// For each incoming wall, extend to meet host wall's edge
|
||||||
for (const { wall, endType } of incomingWalls) {
|
for (const { wall, endType } of walls) {
|
||||||
const halfT = getThickness(wall) / 2
|
const halfT = getThickness(wall) / 2
|
||||||
const v = getOutgoingVector(wall, endType)
|
|
||||||
const vNorm = normalize(v)
|
|
||||||
|
|
||||||
if (Math.abs(vNorm.x) < 1e-9 && Math.abs(vNorm.y) < 1e-9) continue
|
// Outgoing vector
|
||||||
|
const v =
|
||||||
|
endType === 'start'
|
||||||
|
? { x: wall.end[0] - wall.start[0], y: wall.end[1] - wall.start[1] }
|
||||||
|
: { x: wall.start[0] - wall.end[0], y: wall.start[1] - wall.end[1] }
|
||||||
|
|
||||||
|
const L = Math.sqrt(v.x * v.x + v.y * v.y)
|
||||||
|
if (L < 1e-9) continue
|
||||||
|
|
||||||
|
const vNorm = { x: v.x / L, y: v.y / L }
|
||||||
const normal = { x: -vNorm.y, y: vNorm.x }
|
const normal = { x: -vNorm.y, y: vNorm.x }
|
||||||
|
|
||||||
// Default corner points
|
// Edge points
|
||||||
const leftPt = { x: point.x + normal.x * halfT, y: point.y + normal.y * halfT }
|
const leftPt = { x: point.x + normal.x * halfT, y: point.y + normal.y * halfT }
|
||||||
const rightPt = { x: point.x - normal.x * halfT, y: point.y - normal.y * halfT }
|
const rightPt = { x: point.x - normal.x * halfT, y: point.y - normal.y * halfT }
|
||||||
|
|
||||||
// Create edge lines for incoming wall
|
// Edge lines
|
||||||
const edgeLeft = createLineFromPointAndVector(leftPt, v)
|
const edgeLeft = createLineFromPointAndVector(leftPt, v)
|
||||||
const edgeRight = createLineFromPointAndVector(rightPt, v)
|
const edgeRight = createLineFromPointAndVector(rightPt, v)
|
||||||
|
|
||||||
// Determine which side of the host wall the incoming wall approaches from
|
// Determine which host edge to intersect with
|
||||||
// Use the OPPOSITE of outgoing direction (incoming direction) dotted with host normal
|
// Use incoming direction (opposite of outgoing) dotted with host normal
|
||||||
const incomingDir = { x: -vNorm.x, y: -vNorm.y }
|
const incomingDir = { x: -vNorm.x, y: -vNorm.y }
|
||||||
const approachDot = dot(incomingDir, hostNormal)
|
const approachDot = dot(incomingDir, hostNormal)
|
||||||
|
|
||||||
// Pick the host edge facing the incoming wall
|
// Pick host edge facing the incoming wall
|
||||||
// If dot > 0, wall approaches from the opposite side of hostNormal, use hostRight (near surface)
|
const targetHostEdge = approachDot > 0 ? hostEdgeRight : hostEdgeLeft
|
||||||
// If dot < 0, wall approaches from the hostNormal side, use hostLeft (near surface)
|
|
||||||
const targetHostEdge =
|
|
||||||
approachDot > 0
|
|
||||||
? createLineFromPointAndVector(hostRight, hostDir)
|
|
||||||
: createLineFromPointAndVector(hostLeft, hostDir)
|
|
||||||
|
|
||||||
// Both edges of incoming wall meet the same host edge
|
// Both edges meet the same host edge
|
||||||
const leftIntersection = intersectLines(edgeLeft, targetHostEdge)
|
const leftInt = intersectLines(edgeLeft, targetHostEdge)
|
||||||
const rightIntersection = intersectLines(edgeRight, targetHostEdge)
|
const rightInt = intersectLines(edgeRight, targetHostEdge)
|
||||||
|
|
||||||
result.set(wall.id, {
|
result.set(wall.id, {
|
||||||
left: leftIntersection || leftPt,
|
left: leftInt ?? leftPt,
|
||||||
right: rightIntersection || rightPt,
|
right: rightInt ?? rightPt,
|
||||||
|
center: point,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,12 +355,10 @@ export function calculateLevelMiters(walls: WallNode[]): WallMiterMap {
|
|||||||
const miterMap: WallMiterMap = new Map()
|
const miterMap: WallMiterMap = new Map()
|
||||||
const getThickness = (wall: WallNode) => wall.thickness ?? 0.1
|
const getThickness = (wall: WallNode) => wall.thickness ?? 0.1
|
||||||
|
|
||||||
// Process corner junctions
|
// Process regular junctions (2+ walls meeting at endpoints)
|
||||||
const { junctions: cornerJunctions, throughWalls } = findCornerJunctions(walls)
|
const junctions = findJunctions(walls)
|
||||||
for (const [key, junction] of cornerJunctions) {
|
for (const [, junction] of junctions) {
|
||||||
// Pass the through wall (if any) for T-junction handling
|
const miters = calculateJunctionMiters(junction, getThickness)
|
||||||
const throughWall = throughWalls.get(key)
|
|
||||||
const miters = calculateCornerMiters(junction, getThickness, throughWall)
|
|
||||||
|
|
||||||
for (const { wall, endType } of junction.walls) {
|
for (const { wall, endType } of junction.walls) {
|
||||||
const miterData = miters.get(wall.id)
|
const miterData = miters.get(wall.id)
|
||||||
@@ -508,16 +371,16 @@ export function calculateLevelMiters(walls: WallNode[]): WallMiterMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process T-junctions
|
// Process T-junctions (wall endpoint on another wall's side)
|
||||||
const tJunctions = findTJunctions(walls)
|
const tJunctions = findTJunctions(walls)
|
||||||
for (const [, junction] of tJunctions) {
|
for (const [, { junction, hostWall }] of tJunctions) {
|
||||||
const miters = calculateTJunctionMiters(junction, getThickness)
|
const miters = calculateTJunctionMiters(junction, hostWall, getThickness)
|
||||||
|
|
||||||
for (const { wall, endType } of junction.walls) {
|
for (const { wall, endType } of junction.walls) {
|
||||||
const miterData = miters.get(wall.id)
|
const miterData = miters.get(wall.id)
|
||||||
if (!miterData) continue
|
if (!miterData) continue
|
||||||
|
|
||||||
// Don't overwrite corner junction miters
|
// Don't overwrite existing miter data
|
||||||
if (miterMap.get(wall.id)?.[endType]) continue
|
if (miterMap.get(wall.id)?.[endType]) continue
|
||||||
|
|
||||||
if (!miterMap.has(wall.id)) {
|
if (!miterMap.has(wall.id)) {
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ function updateWallGeometry(wallId: string, miterMap: WallMiterMap) {
|
|||||||
*/
|
*/
|
||||||
export function generateExtrudedWall(
|
export function generateExtrudedWall(
|
||||||
wallNode: WallNode,
|
wallNode: WallNode,
|
||||||
childrenNodes: AnyNode[],
|
_childrenNodes: AnyNode[], // TODO: Use for hole cutting (doors/windows)
|
||||||
miters?: { start?: MiterData; end?: MiterData },
|
miters?: { start?: MiterData; end?: MiterData },
|
||||||
) {
|
) {
|
||||||
const start = new THREE.Vector2(wallNode.start[0], wallNode.start[1])
|
const start = new THREE.Vector2(wallNode.start[0], wallNode.start[1])
|
||||||
@@ -142,6 +142,10 @@ export function generateExtrudedWall(
|
|||||||
const thickness = wallNode.thickness ?? 0.1
|
const thickness = wallNode.thickness ?? 0.1
|
||||||
const halfT = thickness / 2
|
const halfT = thickness / 2
|
||||||
|
|
||||||
|
console.log(`\n=== generateExtrudedWall: ${wallNode.id} ===`)
|
||||||
|
console.log('Wall:', { start: wallNode.start, end: wallNode.end, length, thickness })
|
||||||
|
console.log('Miters received:', miters)
|
||||||
|
|
||||||
// Wall angle for coordinate transforms
|
// Wall angle for coordinate transforms
|
||||||
const wallAngle = Math.atan2(end.y - start.y, end.x - start.x)
|
const wallAngle = Math.atan2(end.y - start.y, end.x - start.x)
|
||||||
const cosA = Math.cos(-wallAngle)
|
const cosA = Math.cos(-wallAngle)
|
||||||
@@ -157,155 +161,103 @@ export function generateExtrudedWall(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate miter offsets at start and end
|
// Default miter points (no junction - simple rectangle)
|
||||||
// These determine how far the wall extends/retracts at each end for proper joints
|
const defaultStart = {
|
||||||
let startLeftZ = halfT
|
left: { x: 0, z: halfT },
|
||||||
let startRightZ = -halfT
|
right: { x: 0, z: -halfT },
|
||||||
let endLeftZ = halfT
|
center: { x: 0, z: 0 },
|
||||||
let endRightZ = -halfT
|
hasJunction: false,
|
||||||
|
}
|
||||||
|
const defaultEnd = {
|
||||||
|
left: { x: length, z: halfT },
|
||||||
|
right: { x: length, z: -halfT },
|
||||||
|
center: { x: length, z: 0 },
|
||||||
|
hasJunction: false,
|
||||||
|
}
|
||||||
|
|
||||||
// Miter offset along the wall's X axis (for angled cuts)
|
// Apply miter data if available
|
||||||
let startLeftX = 0
|
let startMiter = defaultStart
|
||||||
let startRightX = 0
|
let endMiter = defaultEnd
|
||||||
let endLeftX = length
|
|
||||||
let endRightX = length
|
|
||||||
|
|
||||||
if (miters?.start) {
|
if (miters?.start) {
|
||||||
const left = worldToLocal(miters.start.left)
|
const left = worldToLocal(miters.start.left)
|
||||||
const right = worldToLocal(miters.start.right)
|
const right = worldToLocal(miters.start.right)
|
||||||
startLeftZ = left.z
|
const center = worldToLocal(miters.start.center)
|
||||||
startRightZ = right.z
|
startMiter = { left, right, center, hasJunction: true }
|
||||||
startLeftX = left.x
|
|
||||||
startRightX = right.x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (miters?.end) {
|
if (miters?.end) {
|
||||||
// At end, left/right are relative to outgoing direction (reversed)
|
// At end, left/right are swapped because outgoing direction is reversed
|
||||||
const left = worldToLocal(miters.end.right)
|
const left = worldToLocal(miters.end.right)
|
||||||
const right = worldToLocal(miters.end.left)
|
const right = worldToLocal(miters.end.left)
|
||||||
endLeftZ = left.z
|
const center = worldToLocal(miters.end.center)
|
||||||
endRightZ = right.z
|
endMiter = { left, right, center, hasJunction: true }
|
||||||
endLeftX = left.x
|
|
||||||
endRightX = right.x
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the main wall shape (XY plane: X = along wall, Y = height)
|
// Create geometry
|
||||||
const shape = new THREE.Shape()
|
const geometry = createMiteredExtrudeGeometry(height, startMiter, endMiter)
|
||||||
shape.moveTo(0, 0)
|
|
||||||
shape.lineTo(length, 0)
|
|
||||||
shape.lineTo(length, height)
|
|
||||||
shape.lineTo(0, height)
|
|
||||||
shape.closePath()
|
|
||||||
|
|
||||||
// Process holes (doors/windows)
|
|
||||||
const wallStart: [number, number] = [wallNode.start[0], wallNode.start[1]]
|
|
||||||
const wallMesh = sceneRegistry.nodes.get(wallNode.id) as THREE.Mesh
|
|
||||||
const wallWorldY = wallMesh?.getWorldPosition(new THREE.Vector3()).y ?? 0
|
|
||||||
|
|
||||||
childrenNodes.forEach((child) => {
|
|
||||||
if (child.type !== 'item') return
|
|
||||||
|
|
||||||
const childMesh = sceneRegistry.nodes.get(child.id)
|
|
||||||
if (!childMesh) return
|
|
||||||
|
|
||||||
const cutoutMesh = childMesh.getObjectByName('cutout') as THREE.Mesh
|
|
||||||
if (!cutoutMesh) return
|
|
||||||
|
|
||||||
const holePath = createPathFromCutout(cutoutMesh, wallStart, wallAngle, wallWorldY)
|
|
||||||
if (holePath) {
|
|
||||||
shape.holes.push(holePath)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create custom extrude geometry with mitered ends
|
|
||||||
const geometry = createMiteredExtrudeGeometry(
|
|
||||||
shape,
|
|
||||||
height,
|
|
||||||
{
|
|
||||||
leftZ: startLeftZ,
|
|
||||||
rightZ: startRightZ,
|
|
||||||
leftX: startLeftX,
|
|
||||||
rightX: startRightX,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
leftZ: endLeftZ,
|
|
||||||
rightZ: endRightZ,
|
|
||||||
leftX: endLeftX,
|
|
||||||
rightX: endRightX,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
return geometry
|
return geometry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MiterPoint {
|
||||||
|
x: number
|
||||||
|
z: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MiterEnd {
|
||||||
|
left: MiterPoint
|
||||||
|
right: MiterPoint
|
||||||
|
center: MiterPoint
|
||||||
|
hasJunction: boolean
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an extruded geometry with mitered (angled) ends
|
* Creates wall geometry using footprint polygon approach
|
||||||
|
*
|
||||||
|
* Footprint has 6 vertices - 3 on each thickness edge (start/end):
|
||||||
|
* - start-right, start-center (if junction), start-left
|
||||||
|
* - end-left, end-center (if junction), end-right
|
||||||
|
*
|
||||||
|
* Based on the prototype: center vertices are only added when there's a junction
|
||||||
*/
|
*/
|
||||||
function createMiteredExtrudeGeometry(
|
function createMiteredExtrudeGeometry(
|
||||||
shape: THREE.Shape,
|
|
||||||
height: number,
|
height: number,
|
||||||
startMiter: { leftZ: number; rightZ: number; leftX: number; rightX: number },
|
startMiter: MiterEnd,
|
||||||
endMiter: { leftZ: number; rightZ: number; leftX: number; rightX: number },
|
endMiter: MiterEnd,
|
||||||
): THREE.BufferGeometry {
|
): THREE.BufferGeometry {
|
||||||
// First, create standard extrude geometry
|
// Build footprint polygon (CCW winding, viewed from above)
|
||||||
const thickness = Math.max(
|
// Following prototype: start-right -> end-right -> [end-center] -> end-left -> start-left -> [start-center]
|
||||||
Math.abs(startMiter.leftZ - startMiter.rightZ),
|
const footprint = new THREE.Shape()
|
||||||
Math.abs(endMiter.leftZ - endMiter.rightZ),
|
|
||||||
0.1,
|
|
||||||
)
|
|
||||||
|
|
||||||
const geometry = new THREE.ExtrudeGeometry(shape, {
|
// Start from start-right, go to end-right
|
||||||
depth: thickness,
|
footprint.moveTo(startMiter.right.x, -startMiter.right.z)
|
||||||
|
footprint.lineTo(endMiter.right.x, -endMiter.right.z)
|
||||||
|
|
||||||
|
// Add end-center if there's a junction at end
|
||||||
|
if (endMiter.hasJunction) {
|
||||||
|
footprint.lineTo(endMiter.center.x, -endMiter.center.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continue to end-left, then start-left
|
||||||
|
footprint.lineTo(endMiter.left.x, -endMiter.left.z)
|
||||||
|
footprint.lineTo(startMiter.left.x, -startMiter.left.z)
|
||||||
|
|
||||||
|
// Add start-center if there's a junction at start
|
||||||
|
if (startMiter.hasJunction) {
|
||||||
|
footprint.lineTo(startMiter.center.x, -startMiter.center.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
footprint.closePath()
|
||||||
|
|
||||||
|
// Extrude along Z by height
|
||||||
|
const geometry = new THREE.ExtrudeGeometry(footprint, {
|
||||||
|
depth: height,
|
||||||
bevelEnabled: false,
|
bevelEnabled: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Translate so center is at Z=0
|
// Rotate so extrusion direction (Z) becomes height direction (Y)
|
||||||
geometry.translate(0, 0, -thickness / 2)
|
geometry.rotateX(-Math.PI / 2)
|
||||||
|
|
||||||
// Get position attribute for modification
|
|
||||||
const positions = geometry.attributes.position
|
|
||||||
const vertices = positions.array as Float32Array
|
|
||||||
|
|
||||||
// Modify vertex positions for mitering
|
|
||||||
for (let i = 0; i < positions.count; i++) {
|
|
||||||
const x = vertices[i * 3]!
|
|
||||||
const y = vertices[i * 3 + 1]!
|
|
||||||
const z = vertices[i * 3 + 2]!
|
|
||||||
|
|
||||||
// Get shape bounds to determine which end we're at
|
|
||||||
const shapePoints = shape.getPoints()
|
|
||||||
const minX = Math.min(...shapePoints.map((p: THREE.Vector2) => p.x))
|
|
||||||
const maxX = Math.max(...shapePoints.map((p: THREE.Vector2) => p.x))
|
|
||||||
const wallLength = maxX - minX
|
|
||||||
|
|
||||||
// Determine position along wall (0 to 1)
|
|
||||||
const t = wallLength > 0 ? (x - minX) / wallLength : 0
|
|
||||||
|
|
||||||
// Interpolate Z offset based on position along wall and which side (left/right)
|
|
||||||
const isLeftSide = z > 0
|
|
||||||
const startZ = isLeftSide ? startMiter.leftZ : startMiter.rightZ
|
|
||||||
const endZ = isLeftSide ? endMiter.leftZ : endMiter.rightZ
|
|
||||||
|
|
||||||
// Linear interpolation of Z offset
|
|
||||||
const newZ = startZ + t * (endZ - startZ)
|
|
||||||
|
|
||||||
// Also adjust X for angled cuts at ends
|
|
||||||
let newX = x
|
|
||||||
if (t < 0.01) {
|
|
||||||
// Near start
|
|
||||||
const startX = isLeftSide ? startMiter.leftX : startMiter.rightX
|
|
||||||
newX = startX
|
|
||||||
} else if (t > 0.99) {
|
|
||||||
// Near end
|
|
||||||
const endX = isLeftSide ? endMiter.leftX : endMiter.rightX
|
|
||||||
newX = endX
|
|
||||||
}
|
|
||||||
|
|
||||||
vertices[i * 3] = newX
|
|
||||||
vertices[i * 3 + 2] = newZ
|
|
||||||
}
|
|
||||||
|
|
||||||
positions.needsUpdate = true
|
|
||||||
geometry.computeVertexNormals()
|
geometry.computeVertexNormals()
|
||||||
|
|
||||||
return geometry
|
return geometry
|
||||||
@@ -313,8 +265,9 @@ function createMiteredExtrudeGeometry(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Path from a cutout mesh for door/window holes
|
* Creates a Path from a cutout mesh for door/window holes
|
||||||
|
* TODO: Integrate with mitered wall geometry
|
||||||
*/
|
*/
|
||||||
function createPathFromCutout(
|
function _createPathFromCutout(
|
||||||
cutoutMesh: THREE.Mesh,
|
cutoutMesh: THREE.Mesh,
|
||||||
wallStart: [number, number],
|
wallStart: [number, number],
|
||||||
wallAngle: number,
|
wallAngle: number,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const WallRenderer = ({ node }: { node: WallNode }) => {
|
|||||||
<mesh ref={ref} castShadow receiveShadow>
|
<mesh ref={ref} castShadow receiveShadow>
|
||||||
{/* WallSystem will replace this geometry in the next frame */}
|
{/* WallSystem will replace this geometry in the next frame */}
|
||||||
<boxGeometry args={[0, 0, 0]} />
|
<boxGeometry args={[0, 0, 0]} />
|
||||||
<meshStandardMaterial color="lightgray" />
|
<meshStandardMaterial color="lightgray" />
|
||||||
<mesh name="collision-mesh" {...handlers} visible={false}>
|
<mesh name="collision-mesh" {...handlers} visible={false}>
|
||||||
<boxGeometry args={[0, 0, 0]} />
|
<boxGeometry args={[0, 0, 0]} />
|
||||||
</mesh>
|
</mesh>
|
||||||
|
|||||||
Reference in New Issue
Block a user