First Release of Claw3D (#11)

Co-authored-by: iamlukethedev <iamlukethedev@users.noreply.github.com>
This commit is contained in:
Luke The Dev
2026-03-19 23:14:04 -05:00
committed by GitHub
parent 5ea96b2650
commit 4fa4f13558
431 changed files with 105438 additions and 14 deletions
+163
View File
@@ -0,0 +1,163 @@
import {
CANVAS_H,
CANVAS_W,
DOOR_LENGTH,
DOOR_THICKNESS,
MIN_WALL_LENGTH,
SCALE,
SNAP_GRID,
WALL_THICKNESS,
} from "@/features/retro-office/core/constants";
import type {
CanvasPoint,
FurnitureItem,
} from "@/features/retro-office/core/types";
export const toWorld = (cx: number, cy: number): [number, number, number] => [
cx * SCALE - CANVAS_W * SCALE * 0.5,
0,
cy * SCALE - CANVAS_H * SCALE * 0.5,
];
export const snap = (value: number) =>
Math.round(value / SNAP_GRID) * SNAP_GRID;
let uidCounter = 0;
export const nextUid = () => `fi_${Date.now()}_${uidCounter++}`;
export const normalizeDegrees = (value: number) => {
const normalized = value % 360;
return normalized < 0 ? normalized + 360 : normalized;
};
export const resolveItemTypeKey = (item: FurnitureItem) =>
item.type === "couch" && item.vertical ? "couch_v" : item.type;
export const ITEM_FOOTPRINT: Record<string, [number, number]> = {
wall: [80, WALL_THICKNESS],
door: [DOOR_LENGTH, DOOR_THICKNESS],
desk_cubicle: [100, 55],
chair: [24, 24],
round_table: [120, 120],
executive_desk: [130, 65],
couch: [100, 40],
couch_v: [40, 80],
bookshelf: [80, 120],
plant: [24, 24],
beanbag: [40, 40],
pingpong: [100, 60],
table_rect: [80, 40],
coffee_machine: [32, 34],
fridge: [40, 80],
water_cooler: [20, 54],
atm: [42, 38],
sms_booth: [58, 54],
phone_booth: [78, 72],
whiteboard: [10, 60],
cabinet: [200, 40],
computer: [30, 20],
lamp: [30, 30],
printer: [40, 35],
stove: [40, 40],
microwave: [30, 20],
wall_cabinet: [80, 20],
sink: [40, 40],
vending: [40, 60],
server_rack: [45, 90],
server_terminal: [42, 34],
qa_terminal: [54, 38],
device_rack: [70, 36],
test_bench: [90, 42],
treadmill: [70, 35],
weight_bench: [90, 45],
dumbbell_rack: [80, 28],
exercise_bike: [45, 65],
punching_bag: [28, 28],
rowing_machine: [90, 34],
kettlebell_rack: [70, 26],
yoga_mat: [70, 30],
keyboard: [30, 14],
mouse: [16, 10],
trash: [20, 20],
mug: [14, 14],
clock: [20, 20],
};
export const getItemBaseSize = (item: FurnitureItem) => {
if (item.r !== undefined) {
return { width: item.r * 2, height: item.r * 2 };
}
const [defaultWidth, defaultHeight] = ITEM_FOOTPRINT[
resolveItemTypeKey(item)
] ?? [item.w ?? 40, item.h ?? 40];
return {
width: item.w ?? defaultWidth,
height: item.h ?? defaultHeight,
};
};
export const FURNITURE_ROTATION: Record<string, number> = {
couch: Math.PI,
couch_v: Math.PI / 2,
executive_desk: -Math.PI / 2,
whiteboard: Math.PI / 2,
};
export const getItemRotationRadians = (item: FurnitureItem) =>
((item.facing ?? 0) * Math.PI) / 180 +
(FURNITURE_ROTATION[resolveItemTypeKey(item)] ?? 0);
export const getItemBounds = (item: FurnitureItem) => {
const { width, height } = getItemBaseSize(item);
const rotation = getItemRotationRadians(item);
const absCos = Math.abs(Math.cos(rotation));
const absSin = Math.abs(Math.sin(rotation));
const boundsWidth = width * absCos + height * absSin;
const boundsHeight = width * absSin + height * absCos;
const centerX = item.x + width / 2;
const centerY = item.y + height / 2;
return {
x: centerX - boundsWidth / 2,
y: centerY - boundsHeight / 2,
w: boundsWidth,
h: boundsHeight,
width,
height,
};
};
export const createWallItem = (
start: CanvasPoint,
end: CanvasPoint,
uid: string,
): FurnitureItem => {
const dx = end.x - start.x;
const dy = end.y - start.y;
const horizontal = Math.abs(dx) >= Math.abs(dy);
if (horizontal) {
const minX = Math.min(start.x, end.x);
const maxX = Math.max(start.x, end.x);
return {
_uid: uid,
type: "wall",
x: snap(minX),
y: snap(start.y) - WALL_THICKNESS / 2,
w: Math.max(MIN_WALL_LENGTH, snap(maxX - minX) + WALL_THICKNESS),
h: WALL_THICKNESS,
facing: 0,
};
}
const minY = Math.min(start.y, end.y);
const maxY = Math.max(start.y, end.y);
return {
_uid: uid,
type: "wall",
x: snap(start.x) - WALL_THICKNESS / 2,
y: snap(minY),
w: WALL_THICKNESS,
h: Math.max(MIN_WALL_LENGTH, snap(maxY - minY) + WALL_THICKNESS),
facing: 0,
};
};