Snap: Center-/Quadrant-Snaps für Kreise + Bögen (Bogen nur im Spannbereich) + Bogen-Endpunkte, mit Tests
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { computeSnap, wallLayerBoundarySegments } from "./snapping";
|
||||
import { DEFAULT_SNAP } from "./types";
|
||||
import type { Project, Vec2, Wall } from "../model/types";
|
||||
import type { Drawing2D, Project, Vec2, Wall } from "../model/types";
|
||||
|
||||
/** Minimalprojekt mit einer dreischichtigen Wand (0.1 / 0.15 / 0.2 m). */
|
||||
function wallProject(wall: Wall): Project {
|
||||
@@ -134,3 +134,95 @@ describe("computeSnap — fängt auf Wand-Schichttrennlinien", () => {
|
||||
expect(result!.point.y).toBeCloseTo(0.025, 6);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Kreis-/Bogen-Snaps (Center/Quadrant/Bogen-Endpunkt) ──────────────────────
|
||||
|
||||
/** Minimalprojekt mit gegebenen 2D-Geometrien auf dem Geschoss „eg". */
|
||||
function drawProject(geoms: Drawing2D["geom"][]): Project {
|
||||
return {
|
||||
id: "t",
|
||||
name: "T",
|
||||
lineStyles: [],
|
||||
hatches: [],
|
||||
components: [{ id: "c", name: "C", color: "#ccc", hatchId: "none", joinPriority: 10 }],
|
||||
wallTypes: [],
|
||||
drawingLevels: [
|
||||
{
|
||||
id: "eg",
|
||||
name: "EG",
|
||||
kind: "floor",
|
||||
visible: true,
|
||||
locked: false,
|
||||
floorHeight: 2.6,
|
||||
cutHeight: 1.0,
|
||||
baseElevation: 0,
|
||||
},
|
||||
],
|
||||
layers: [{ code: "20", name: "Zeichnung", color: "#0a0a0a", lw: 0.5, visible: true, locked: false }],
|
||||
walls: [],
|
||||
doors: [],
|
||||
openings: [],
|
||||
ceilings: [],
|
||||
stairs: [],
|
||||
rooms: [],
|
||||
drawings2d: geoms.map((geom, i) => ({
|
||||
id: `d${i}`,
|
||||
type: "drawing2d" as const,
|
||||
levelId: "eg",
|
||||
categoryCode: "20",
|
||||
geom,
|
||||
})),
|
||||
context: [],
|
||||
};
|
||||
}
|
||||
|
||||
function snapAt(project: Project, raw: Vec2, settings = DEFAULT_SNAP) {
|
||||
return computeSnap({
|
||||
raw,
|
||||
project,
|
||||
levelId: "eg",
|
||||
visibleCodes: new Set(["20"]),
|
||||
settings,
|
||||
draftPoints: [],
|
||||
lastPoint: null,
|
||||
pxPerMeter: 100,
|
||||
shift: false,
|
||||
ctrl: false,
|
||||
});
|
||||
}
|
||||
|
||||
describe("computeSnap — Kreis/Bogen", () => {
|
||||
const withCenter = { ...DEFAULT_SNAP, center: true };
|
||||
|
||||
it("fängt Mittelpunkt + Quadranten eines Kreises (center aktiv)", () => {
|
||||
const p = drawProject([{ shape: "circle", center: { x: 0, y: 0 }, r: 1 }]);
|
||||
const c = snapAt(p, { x: 0, y: 0 }, withCenter);
|
||||
expect(c?.kind).toBe("center");
|
||||
expect(c!.point.x).toBeCloseTo(0, 6);
|
||||
expect(c!.point.y).toBeCloseTo(0, 6);
|
||||
const q = snapAt(p, { x: 1, y: 0 }, withCenter);
|
||||
expect(q?.kind).toBe("quadrant");
|
||||
expect(q!.point.x).toBeCloseTo(1, 6);
|
||||
expect(q!.point.y).toBeCloseTo(0, 6);
|
||||
});
|
||||
|
||||
it("fängt Bogen-Endpunkte (endpoint per Default aktiv)", () => {
|
||||
// Viertelbogen um (5,0), r=1, 0…90° → Endpunkte (6,0) und (5,1).
|
||||
const p = drawProject([
|
||||
{ shape: "arc", center: { x: 5, y: 0 }, r: 1, a0: 0, a1: Math.PI / 2 },
|
||||
]);
|
||||
const e = snapAt(p, { x: 6, y: 0 });
|
||||
expect(e?.kind).toBe("endpoint");
|
||||
expect(e!.point.x).toBeCloseTo(6, 6);
|
||||
expect(e!.point.y).toBeCloseTo(0, 6);
|
||||
});
|
||||
|
||||
it("bietet Quadranten AUSSERHALB der Bogen-Spanne NICHT an", () => {
|
||||
// Viertelbogen 0…90°: der West-Quadrant (4,0) liegt NICHT im Bogen.
|
||||
const p = drawProject([
|
||||
{ shape: "arc", center: { x: 5, y: 0 }, r: 1, a0: 0, a1: Math.PI / 2 },
|
||||
]);
|
||||
const r = snapAt(p, { x: 4, y: 0 }, withCenter);
|
||||
expect(r?.kind).not.toBe("quadrant");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user