Shared CAM Data Models
July 2, 2026 ยท View on GitHub
This spec defines the common data structures used by the detailed CAM algorithms. Implement these first so each algorithm has the same contracts.
Coordinate Space
- All algorithm inputs use machine coordinates.
- Current BREP.io mapping is
machine.X = scene.x,machine.Y = scene.z,machine.Z = scene.y. - The cutter axis is machine
+Z. - Cutting depths descend in machine Z.
- Display conversion back to scene coordinates must be centralized and tested.
Numeric Policy
- Use one module-level tolerance object, not hard-coded tolerances in each algorithm.
- Required tolerances:
pointEps: point equality, default1e-6.distanceEps: geometric distance comparisons, default1e-7to1e-5depending on model size.areaEps: zero-area loop/triangle checks.angleEps: dot/cross comparisons.sampleEps: minimum accepted span/sample distance.
- Scale tolerances by model bounding-box diagonal when user models are very large or very small.
- Any
NaNor infinite coordinate invalidates the local result and must create a warning.
Core Types
Use names close to these in implementation, even if exact TypeScript names differ:
type CamPoint3 = [number, number, number];
type CamPoint2 = [number, number];
type CamTriangle = {
id: number;
a: CamPoint3;
b: CamPoint3;
c: CamPoint3;
bounds: CamBounds3;
normal?: CamPoint3;
};
type CamTargetSelection = {
targetSolidIds: string[];
driveFaceIds?: string[];
protectedSolidIds: string[];
};
type CamFaceRegion = {
solidId: string;
faceId: string;
triangleIds: number[];
projectedBoundary?: CamPoint2[];
};
type CamBounds3 = {
min: CamPoint3;
max: CamPoint3;
};
type CamContactType =
| "none"
| "vertex"
| "facet"
| "edge"
| "shaft"
| "tool-profile"
| "numeric-fallback";
type CamContact = {
type: CamContactType;
point?: CamPoint3;
triangleId?: number;
distance?: number;
detail?: string;
};
type CamCLPoint = {
x: number;
y: number;
z: number;
contact?: CamContact;
};
Toolpath Types
Toolpath output must preserve cutting and non-cutting motion separately:
type CamMoveKind = "rapid" | "plunge" | "cut" | "retract" | "link";
type CamMotionSegment = {
start: CamPoint3;
end: CamPoint3;
kind: CamMoveKind;
feedRate?: number;
sourcePathId?: string;
};
type CamToolpathPath = {
id: string;
z: number;
feedRate: number;
plungeRate: number;
points: CamPoint3[];
closed?: boolean;
strategy?: string;
};
Requirements:
- The actual cutter path is always represented as a polyline.
- The simulation toolhead must follow every
CamMotionSegment. - Slider snap points are built from motion segment endpoints and selected interior points.
- G-code order and simulation order must match.
- Face-selected operations use
driveFaceIdsto define machining scope but still build protection checks fromprotectedSolidIds.
Progress Events
All long-running algorithms must support:
type CamProgress = {
phase: string;
message: string;
detail?: string;
current: number;
total: number;
operationId?: string;
};
Rules:
- Emit progress at algorithm-stage boundaries and chunk boundaries.
- Never emit progress for every triangle in a large mesh.
- Call
progressYield()after meaningful chunks in web-worker-friendly async algorithms. - If a fallback path is used, include the fallback in
detail.
Warnings and Errors
Use warnings for recoverable degradation:
- Empty path after clipping.
- Numeric solve did not converge for one candidate contact.
- Spatial index falls back to brute force for a small mesh.
- Arc discretized because arc-preserving output is not available yet.
Use errors for generation-stopping failures:
- No target mesh triangles for an enabled operation.
- Invalid cutter dimensions.
- Unsupported selected cutter shape for selected strategy.
- Worker serialization failure.
Serialization Boundary
Worker jobs must pass plain data:
- Numbers, strings, booleans, arrays, and object literals.
- No
THREE.Vector3. - No scene objects.
- No methods/functions except local worker callbacks supplied outside serialized payload.
The worker should reconstruct internal classes from plain data at job start.