ceiling rendering + tools fix
This commit is contained in:
@@ -4,7 +4,8 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three";
|
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three";
|
||||||
import useEditor from "@/store/use-editor";
|
import useEditor from "@/store/use-editor";
|
||||||
|
|
||||||
const Y_OFFSET = 2.52; // Slightly above default ceiling height
|
const CEILING_HEIGHT = 2.52; // Slightly above default ceiling height
|
||||||
|
const GRID_OFFSET = 0.02; // Small offset above floor level
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Snaps a point to the nearest axis-aligned or 45-degree diagonal from the last point
|
* Snaps a point to the nearest axis-aligned or 45-degree diagonal from the last point
|
||||||
@@ -69,6 +70,7 @@ const commitCeilingDrawing = (
|
|||||||
type PreviewState = {
|
type PreviewState = {
|
||||||
points: Array<[number, number]>;
|
points: Array<[number, number]>;
|
||||||
cursorPoint: [number, number] | null;
|
cursorPoint: [number, number] | null;
|
||||||
|
levelY: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to validate point values (no NaN or Infinity)
|
// Helper to validate point values (no NaN or Infinity)
|
||||||
@@ -79,14 +81,13 @@ const isValidPoint = (
|
|||||||
return Number.isFinite(pt[0]) && Number.isFinite(pt[1]);
|
return Number.isFinite(pt[0]) && Number.isFinite(pt[1]);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GRID_Y = 0.02; // Grid level indicator
|
|
||||||
|
|
||||||
export const CeilingTool: React.FC = () => {
|
export const CeilingTool: React.FC = () => {
|
||||||
const cursorRef = useRef<Mesh>(null);
|
const cursorRef = useRef<Mesh>(null);
|
||||||
const gridCursorRef = useRef<Mesh>(null);
|
const gridCursorRef = useRef<Mesh>(null);
|
||||||
const mainLineRef = useRef<Line>(null!);
|
const mainLineRef = useRef<Line>(null!);
|
||||||
const closingLineRef = useRef<Line>(null!);
|
const closingLineRef = useRef<Line>(null!);
|
||||||
const pointsRef = useRef<Array<[number, number]>>([]);
|
const pointsRef = useRef<Array<[number, number]>>([]);
|
||||||
|
const levelYRef = useRef(0); // Track current level Y position
|
||||||
const currentLevelId = useViewer((state) => state.selection.levelId);
|
const currentLevelId = useViewer((state) => state.selection.levelId);
|
||||||
const setTool = useEditor((state) => state.setTool);
|
const setTool = useEditor((state) => state.setTool);
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
const [preview, setPreview] = useState<PreviewState>({
|
const [preview, setPreview] = useState<PreviewState>({
|
||||||
points: [],
|
points: [],
|
||||||
cursorPoint: null,
|
cursorPoint: null,
|
||||||
|
levelY: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -107,6 +109,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
|
|
||||||
const updateLines = () => {
|
const updateLines = () => {
|
||||||
const points = pointsRef.current;
|
const points = pointsRef.current;
|
||||||
|
const ceilingY = levelYRef.current + CEILING_HEIGHT;
|
||||||
|
|
||||||
if (points.length === 0) {
|
if (points.length === 0) {
|
||||||
mainLineRef.current.visible = false;
|
mainLineRef.current.visible = false;
|
||||||
@@ -116,7 +119,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
|
|
||||||
// Build main line points
|
// Build main line points
|
||||||
const linePoints: Vector3[] = points.map(
|
const linePoints: Vector3[] = points.map(
|
||||||
([x, z]) => new Vector3(x, Y_OFFSET, z)
|
([x, z]) => new Vector3(x, ceilingY, z)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add cursor point
|
// Add cursor point
|
||||||
@@ -124,7 +127,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
if (lastPoint) {
|
if (lastPoint) {
|
||||||
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
||||||
if (isValidPoint(snapped)) {
|
if (isValidPoint(snapped)) {
|
||||||
linePoints.push(new Vector3(snapped[0], Y_OFFSET, snapped[1]));
|
linePoints.push(new Vector3(snapped[0], ceilingY, snapped[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,8 +146,8 @@ export const CeilingTool: React.FC = () => {
|
|||||||
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
||||||
if (isValidPoint(snapped)) {
|
if (isValidPoint(snapped)) {
|
||||||
const closingPoints = [
|
const closingPoints = [
|
||||||
new Vector3(snapped[0], Y_OFFSET, snapped[1]),
|
new Vector3(snapped[0], ceilingY, snapped[1]),
|
||||||
new Vector3(firstPoint[0], Y_OFFSET, firstPoint[1]),
|
new Vector3(firstPoint[0], ceilingY, firstPoint[1]),
|
||||||
];
|
];
|
||||||
closingLineRef.current.geometry.dispose();
|
closingLineRef.current.geometry.dispose();
|
||||||
closingLineRef.current.geometry = new BufferGeometry().setFromPoints(closingPoints);
|
closingLineRef.current.geometry = new BufferGeometry().setFromPoints(closingPoints);
|
||||||
@@ -166,7 +169,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
cursorPt = cursorPosition;
|
cursorPt = cursorPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreview({ points: [...points], cursorPoint: cursorPt });
|
setPreview({ points: [...points], cursorPoint: cursorPt, levelY: levelYRef.current });
|
||||||
updateLines();
|
updateLines();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -177,20 +180,24 @@ export const CeilingTool: React.FC = () => {
|
|||||||
const gridX = Math.round(event.position[0] * 2) / 2;
|
const gridX = Math.round(event.position[0] * 2) / 2;
|
||||||
const gridZ = Math.round(event.position[2] * 2) / 2;
|
const gridZ = Math.round(event.position[2] * 2) / 2;
|
||||||
cursorPosition = [gridX, gridZ];
|
cursorPosition = [gridX, gridZ];
|
||||||
|
levelYRef.current = event.position[1];
|
||||||
|
|
||||||
|
const ceilingY = event.position[1] + CEILING_HEIGHT;
|
||||||
|
const gridY = event.position[1] + GRID_OFFSET;
|
||||||
|
|
||||||
// If we have points, snap to axis from last point
|
// If we have points, snap to axis from last point
|
||||||
const lastPoint = pointsRef.current[pointsRef.current.length - 1];
|
const lastPoint = pointsRef.current[pointsRef.current.length - 1];
|
||||||
if (lastPoint) {
|
if (lastPoint) {
|
||||||
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
||||||
cursorRef.current.position.set(snapped[0], Y_OFFSET, snapped[1]);
|
cursorRef.current.position.set(snapped[0], ceilingY, snapped[1]);
|
||||||
// Also update grid-level cursor
|
// Also update grid-level cursor
|
||||||
if (gridCursorRef.current) {
|
if (gridCursorRef.current) {
|
||||||
gridCursorRef.current.position.set(snapped[0], GRID_Y, snapped[1]);
|
gridCursorRef.current.position.set(snapped[0], gridY, snapped[1]);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cursorRef.current.position.set(gridX, Y_OFFSET, gridZ);
|
cursorRef.current.position.set(gridX, ceilingY, gridZ);
|
||||||
if (gridCursorRef.current) {
|
if (gridCursorRef.current) {
|
||||||
gridCursorRef.current.position.set(gridX, GRID_Y, gridZ);
|
gridCursorRef.current.position.set(gridX, gridY, gridZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +230,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
pointsRef.current = [];
|
pointsRef.current = [];
|
||||||
setPreview({ points: [], cursorPoint: null });
|
setPreview({ points: [], cursorPoint: null, levelY: levelYRef.current });
|
||||||
mainLineRef.current.visible = false;
|
mainLineRef.current.visible = false;
|
||||||
closingLineRef.current.visible = false;
|
closingLineRef.current.visible = false;
|
||||||
|
|
||||||
@@ -245,7 +252,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
pointsRef.current = [];
|
pointsRef.current = [];
|
||||||
setPreview({ points: [], cursorPoint: null });
|
setPreview({ points: [], cursorPoint: null, levelY: levelYRef.current });
|
||||||
mainLineRef.current.visible = false;
|
mainLineRef.current.visible = false;
|
||||||
closingLineRef.current.visible = false;
|
closingLineRef.current.visible = false;
|
||||||
|
|
||||||
@@ -269,7 +276,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [currentLevelId, setTool]);
|
}, [currentLevelId, setTool]);
|
||||||
|
|
||||||
const { points, cursorPoint } = preview;
|
const { points, cursorPoint, levelY } = preview;
|
||||||
|
|
||||||
// Create preview shape when we have 3+ points
|
// Create preview shape when we have 3+ points
|
||||||
const previewShape = useMemo(() => {
|
const previewShape = useMemo(() => {
|
||||||
@@ -327,7 +334,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
{previewShape && (
|
{previewShape && (
|
||||||
<mesh
|
<mesh
|
||||||
frustumCulled={false}
|
frustumCulled={false}
|
||||||
position={[0, Y_OFFSET, 0]}
|
position={[0, levelY + CEILING_HEIGHT, 0]}
|
||||||
rotation={[-Math.PI / 2, 0, 0]}
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
>
|
>
|
||||||
<shapeGeometry args={[previewShape]} />
|
<shapeGeometry args={[previewShape]} />
|
||||||
@@ -370,7 +377,7 @@ export const CeilingTool: React.FC = () => {
|
|||||||
{/* Point markers */}
|
{/* Point markers */}
|
||||||
{points.map(([x, z], index) =>
|
{points.map(([x, z], index) =>
|
||||||
isValidPoint([x, z]) ? (
|
isValidPoint([x, z]) ? (
|
||||||
<mesh key={index} position={[x, Y_OFFSET + 0.01, z]}>
|
<mesh key={index} position={[x, levelY + CEILING_HEIGHT + 0.01, z]}>
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
<meshBasicMaterial
|
<meshBasicMaterial
|
||||||
color={index === 0 ? "#22c55e" : "#d4d4d4"}
|
color={index === 0 ? "#22c55e" : "#d4d4d4"}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
type WallEvent,
|
type WallEvent,
|
||||||
type WallNode,
|
type WallNode,
|
||||||
} from '@pascal-app/core'
|
} from '@pascal-app/core'
|
||||||
|
|
||||||
import { useViewer } from '@pascal-app/viewer'
|
import { useViewer } from '@pascal-app/viewer'
|
||||||
import { useFrame } from '@react-three/fiber'
|
import { useFrame } from '@react-three/fiber'
|
||||||
import { useEffect, useRef } from 'react'
|
import { useEffect, useRef } from 'react'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three";
|
import { BufferGeometry, DoubleSide, type Line, type Mesh, Shape, Vector3 } from "three";
|
||||||
import useEditor from "@/store/use-editor";
|
import useEditor from "@/store/use-editor";
|
||||||
|
|
||||||
const Y_OFFSET = 0.02;
|
const Y_OFFSET = 0.02; // Small offset above floor level
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Snaps a point to the nearest axis-aligned or 45-degree diagonal from the last point
|
* Snaps a point to the nearest axis-aligned or 45-degree diagonal from the last point
|
||||||
@@ -69,6 +69,7 @@ const commitSlabDrawing = (
|
|||||||
type PreviewState = {
|
type PreviewState = {
|
||||||
points: Array<[number, number]>;
|
points: Array<[number, number]>;
|
||||||
cursorPoint: [number, number] | null;
|
cursorPoint: [number, number] | null;
|
||||||
|
levelY: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to validate point values (no NaN or Infinity)
|
// Helper to validate point values (no NaN or Infinity)
|
||||||
@@ -84,6 +85,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
const mainLineRef = useRef<Line>(null!);
|
const mainLineRef = useRef<Line>(null!);
|
||||||
const closingLineRef = useRef<Line>(null!);
|
const closingLineRef = useRef<Line>(null!);
|
||||||
const pointsRef = useRef<Array<[number, number]>>([]);
|
const pointsRef = useRef<Array<[number, number]>>([]);
|
||||||
|
const levelYRef = useRef(0); // Track current level Y position
|
||||||
const currentLevelId = useViewer((state) => state.selection.levelId);
|
const currentLevelId = useViewer((state) => state.selection.levelId);
|
||||||
const setTool = useEditor((state) => state.setTool);
|
const setTool = useEditor((state) => state.setTool);
|
||||||
|
|
||||||
@@ -91,6 +93,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
const [preview, setPreview] = useState<PreviewState>({
|
const [preview, setPreview] = useState<PreviewState>({
|
||||||
points: [],
|
points: [],
|
||||||
cursorPoint: null,
|
cursorPoint: null,
|
||||||
|
levelY: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -104,6 +107,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
|
|
||||||
const updateLines = () => {
|
const updateLines = () => {
|
||||||
const points = pointsRef.current;
|
const points = pointsRef.current;
|
||||||
|
const y = levelYRef.current + Y_OFFSET;
|
||||||
|
|
||||||
if (points.length === 0) {
|
if (points.length === 0) {
|
||||||
mainLineRef.current.visible = false;
|
mainLineRef.current.visible = false;
|
||||||
@@ -113,7 +117,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
|
|
||||||
// Build main line points
|
// Build main line points
|
||||||
const linePoints: Vector3[] = points.map(
|
const linePoints: Vector3[] = points.map(
|
||||||
([x, z]) => new Vector3(x, Y_OFFSET, z)
|
([x, z]) => new Vector3(x, y, z)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add cursor point
|
// Add cursor point
|
||||||
@@ -121,7 +125,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
if (lastPoint) {
|
if (lastPoint) {
|
||||||
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
||||||
if (isValidPoint(snapped)) {
|
if (isValidPoint(snapped)) {
|
||||||
linePoints.push(new Vector3(snapped[0], Y_OFFSET, snapped[1]));
|
linePoints.push(new Vector3(snapped[0], y, snapped[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,8 +144,8 @@ export const SlabTool: React.FC = () => {
|
|||||||
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
||||||
if (isValidPoint(snapped)) {
|
if (isValidPoint(snapped)) {
|
||||||
const closingPoints = [
|
const closingPoints = [
|
||||||
new Vector3(snapped[0], Y_OFFSET, snapped[1]),
|
new Vector3(snapped[0], y, snapped[1]),
|
||||||
new Vector3(firstPoint[0], Y_OFFSET, firstPoint[1]),
|
new Vector3(firstPoint[0], y, firstPoint[1]),
|
||||||
];
|
];
|
||||||
closingLineRef.current.geometry.dispose();
|
closingLineRef.current.geometry.dispose();
|
||||||
closingLineRef.current.geometry = new BufferGeometry().setFromPoints(closingPoints);
|
closingLineRef.current.geometry = new BufferGeometry().setFromPoints(closingPoints);
|
||||||
@@ -163,7 +167,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
cursorPt = cursorPosition;
|
cursorPt = cursorPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreview({ points: [...points], cursorPoint: cursorPt });
|
setPreview({ points: [...points], cursorPoint: cursorPt, levelY: levelYRef.current });
|
||||||
updateLines();
|
updateLines();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -174,6 +178,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
const gridX = Math.round(event.position[0] * 2) / 2;
|
const gridX = Math.round(event.position[0] * 2) / 2;
|
||||||
const gridZ = Math.round(event.position[2] * 2) / 2;
|
const gridZ = Math.round(event.position[2] * 2) / 2;
|
||||||
cursorPosition = [gridX, gridZ];
|
cursorPosition = [gridX, gridZ];
|
||||||
|
levelYRef.current = event.position[1];
|
||||||
|
|
||||||
// If we have points, snap to axis from last point
|
// If we have points, snap to axis from last point
|
||||||
const lastPoint = pointsRef.current[pointsRef.current.length - 1];
|
const lastPoint = pointsRef.current[pointsRef.current.length - 1];
|
||||||
@@ -259,7 +264,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [currentLevelId, setTool]);
|
}, [currentLevelId, setTool]);
|
||||||
|
|
||||||
const { points, cursorPoint } = preview;
|
const { points, cursorPoint, levelY } = preview;
|
||||||
|
|
||||||
// Create preview shape when we have 3+ points
|
// Create preview shape when we have 3+ points
|
||||||
const previewShape = useMemo(() => {
|
const previewShape = useMemo(() => {
|
||||||
@@ -306,7 +311,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
{previewShape && (
|
{previewShape && (
|
||||||
<mesh
|
<mesh
|
||||||
frustumCulled={false}
|
frustumCulled={false}
|
||||||
position={[0, Y_OFFSET, 0]}
|
position={[0, levelY + Y_OFFSET, 0]}
|
||||||
rotation={[-Math.PI / 2, 0, 0]}
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
>
|
>
|
||||||
<shapeGeometry args={[previewShape]} />
|
<shapeGeometry args={[previewShape]} />
|
||||||
@@ -349,7 +354,7 @@ export const SlabTool: React.FC = () => {
|
|||||||
{/* Point markers */}
|
{/* Point markers */}
|
||||||
{points.map(([x, z], index) =>
|
{points.map(([x, z], index) =>
|
||||||
isValidPoint([x, z]) ? (
|
isValidPoint([x, z]) ? (
|
||||||
<mesh key={index} position={[x, Y_OFFSET + 0.01, z]}>
|
<mesh key={index} position={[x, levelY + Y_OFFSET + 0.01, z]}>
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
<meshBasicMaterial
|
<meshBasicMaterial
|
||||||
color={index === 0 ? "#22c55e" : "#a3a3a3"}
|
color={index === 0 ? "#22c55e" : "#a3a3a3"}
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ const commitZoneDrawing = (
|
|||||||
type PreviewState = {
|
type PreviewState = {
|
||||||
points: Array<[number, number]>;
|
points: Array<[number, number]>;
|
||||||
cursorPoint: [number, number] | null;
|
cursorPoint: [number, number] | null;
|
||||||
|
levelY: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to validate point values (no NaN or Infinity)
|
// Helper to validate point values (no NaN or Infinity)
|
||||||
@@ -103,6 +104,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
const mainLineRef = useRef<Line>(null!);
|
const mainLineRef = useRef<Line>(null!);
|
||||||
const closingLineRef = useRef<Line>(null!);
|
const closingLineRef = useRef<Line>(null!);
|
||||||
const pointsRef = useRef<Array<[number, number]>>([]);
|
const pointsRef = useRef<Array<[number, number]>>([]);
|
||||||
|
const levelYRef = useRef(0); // Track current level Y position
|
||||||
const currentLevelId = useViewer((state) => state.selection.levelId);
|
const currentLevelId = useViewer((state) => state.selection.levelId);
|
||||||
const setTool = useEditor((state) => state.setTool);
|
const setTool = useEditor((state) => state.setTool);
|
||||||
|
|
||||||
@@ -110,6 +112,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
const [preview, setPreview] = useState<PreviewState>({
|
const [preview, setPreview] = useState<PreviewState>({
|
||||||
points: [],
|
points: [],
|
||||||
cursorPoint: null,
|
cursorPoint: null,
|
||||||
|
levelY: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -123,6 +126,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
|
|
||||||
const updateLines = () => {
|
const updateLines = () => {
|
||||||
const points = pointsRef.current;
|
const points = pointsRef.current;
|
||||||
|
const y = levelYRef.current + Y_OFFSET;
|
||||||
|
|
||||||
if (points.length === 0) {
|
if (points.length === 0) {
|
||||||
mainLineRef.current.visible = false;
|
mainLineRef.current.visible = false;
|
||||||
@@ -132,7 +136,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
|
|
||||||
// Build main line points
|
// Build main line points
|
||||||
const linePoints: Vector3[] = points.map(
|
const linePoints: Vector3[] = points.map(
|
||||||
([x, z]) => new Vector3(x, Y_OFFSET, z)
|
([x, z]) => new Vector3(x, y, z)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Add cursor point
|
// Add cursor point
|
||||||
@@ -140,7 +144,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
if (lastPoint) {
|
if (lastPoint) {
|
||||||
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
||||||
if (isValidPoint(snapped)) {
|
if (isValidPoint(snapped)) {
|
||||||
linePoints.push(new Vector3(snapped[0], Y_OFFSET, snapped[1]));
|
linePoints.push(new Vector3(snapped[0], y, snapped[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,8 +163,8 @@ export const ZoneTool: React.FC = () => {
|
|||||||
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
const snapped = calculateSnapPoint(lastPoint, cursorPosition);
|
||||||
if (isValidPoint(snapped)) {
|
if (isValidPoint(snapped)) {
|
||||||
const closingPoints = [
|
const closingPoints = [
|
||||||
new Vector3(snapped[0], Y_OFFSET, snapped[1]),
|
new Vector3(snapped[0], y, snapped[1]),
|
||||||
new Vector3(firstPoint[0], Y_OFFSET, firstPoint[1]),
|
new Vector3(firstPoint[0], y, firstPoint[1]),
|
||||||
];
|
];
|
||||||
closingLineRef.current.geometry.dispose();
|
closingLineRef.current.geometry.dispose();
|
||||||
closingLineRef.current.geometry = new BufferGeometry().setFromPoints(closingPoints);
|
closingLineRef.current.geometry = new BufferGeometry().setFromPoints(closingPoints);
|
||||||
@@ -182,7 +186,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
cursorPt = cursorPosition;
|
cursorPt = cursorPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
setPreview({ points: [...points], cursorPoint: cursorPt });
|
setPreview({ points: [...points], cursorPoint: cursorPt, levelY: levelYRef.current });
|
||||||
updateLines();
|
updateLines();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -193,6 +197,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
const gridX = Math.round(event.position[0] * 2) / 2;
|
const gridX = Math.round(event.position[0] * 2) / 2;
|
||||||
const gridZ = Math.round(event.position[2] * 2) / 2;
|
const gridZ = Math.round(event.position[2] * 2) / 2;
|
||||||
cursorPosition = [gridX, gridZ];
|
cursorPosition = [gridX, gridZ];
|
||||||
|
levelYRef.current = event.position[1];
|
||||||
|
|
||||||
// If we have points, snap to axis from last point
|
// If we have points, snap to axis from last point
|
||||||
const lastPoint = pointsRef.current[pointsRef.current.length - 1];
|
const lastPoint = pointsRef.current[pointsRef.current.length - 1];
|
||||||
@@ -232,7 +237,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
pointsRef.current = [];
|
pointsRef.current = [];
|
||||||
setPreview({ points: [], cursorPoint: null });
|
setPreview({ points: [], cursorPoint: null, levelY: levelYRef.current });
|
||||||
mainLineRef.current.visible = false;
|
mainLineRef.current.visible = false;
|
||||||
closingLineRef.current.visible = false;
|
closingLineRef.current.visible = false;
|
||||||
|
|
||||||
@@ -254,7 +259,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
|
|
||||||
// Reset state
|
// Reset state
|
||||||
pointsRef.current = [];
|
pointsRef.current = [];
|
||||||
setPreview({ points: [], cursorPoint: null });
|
setPreview({ points: [], cursorPoint: null, levelY: levelYRef.current });
|
||||||
mainLineRef.current.visible = false;
|
mainLineRef.current.visible = false;
|
||||||
closingLineRef.current.visible = false;
|
closingLineRef.current.visible = false;
|
||||||
|
|
||||||
@@ -278,7 +283,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
};
|
};
|
||||||
}, [currentLevelId, setTool]);
|
}, [currentLevelId, setTool]);
|
||||||
|
|
||||||
const { points, cursorPoint } = preview;
|
const { points, cursorPoint, levelY } = preview;
|
||||||
|
|
||||||
// Create preview shape when we have 3+ points
|
// Create preview shape when we have 3+ points
|
||||||
const previewShape = useMemo(() => {
|
const previewShape = useMemo(() => {
|
||||||
@@ -325,7 +330,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
{previewShape && (
|
{previewShape && (
|
||||||
<mesh
|
<mesh
|
||||||
frustumCulled={false}
|
frustumCulled={false}
|
||||||
position={[0, Y_OFFSET, 0]}
|
position={[0, levelY + Y_OFFSET, 0]}
|
||||||
rotation={[-Math.PI / 2, 0, 0]}
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
>
|
>
|
||||||
<shapeGeometry args={[previewShape]} />
|
<shapeGeometry args={[previewShape]} />
|
||||||
@@ -368,7 +373,7 @@ export const ZoneTool: React.FC = () => {
|
|||||||
{/* Point markers */}
|
{/* Point markers */}
|
||||||
{points.map(([x, z], index) =>
|
{points.map(([x, z], index) =>
|
||||||
isValidPoint([x, z]) ? (
|
isValidPoint([x, z]) ? (
|
||||||
<mesh key={index} position={[x, Y_OFFSET + 0.01, z]}>
|
<mesh key={index} position={[x, levelY + Y_OFFSET + 0.01, z]}>
|
||||||
<sphereGeometry args={[0.1, 16, 16]} />
|
<sphereGeometry args={[0.1, 16, 16]} />
|
||||||
<meshBasicMaterial
|
<meshBasicMaterial
|
||||||
color={index === 0 ? "#22c55e" : "#3b82f6"}
|
color={index === 0 ? "#22c55e" : "#3b82f6"}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ function updateCeilingGeometry(node: CeilingNode, mesh: THREE.Mesh) {
|
|||||||
mesh.geometry = newGeo
|
mesh.geometry = newGeo
|
||||||
|
|
||||||
// Position at the ceiling height
|
// Position at the ceiling height
|
||||||
mesh.position.y = node.height ?? 2.5
|
mesh.position.y = (node.height ?? 2.5) - 0.01 // Slight offset to avoid z-fighting with upper-level slabs
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user