1
import { WrappingObj, GCWithScope } from "./register.js";
2
import { DEG2RAD, RAD2DEG } from "./constants.js";
3
import { getOC } from "./oclib.js";
17
} from "replicad-opencascadejs";
19
const round3 = (v: number) => Math.round(v * 1000) / 1000;
21
export type SimplePoint = [number, number, number];
26
| { XYZ: () => gp_XYZ; delete: () => void };
28
export function isPoint(p: unknown): p is Point {
29
if (Array.isArray(p)) return p.length === 3 || p.length === 2;
30
else if (p instanceof Vector) return true;
31
else if (p && typeof (p as any)?.XYZ === "function") return true;
35
export const makeAx3 = (center: Point, dir: Point, xDir?: Point): gp_Ax3 => {
37
const origin = asPnt(center);
38
const direction = asDir(dir);
42
const xDirection = asDir(xDir);
43
axis = new oc.gp_Ax3_3(origin, direction, xDirection);
46
axis = new oc.gp_Ax3_4(origin, direction);
53
export const makeAx2 = (center: Point, dir: Point, xDir?: Point): gp_Ax2 => {
55
const origin = asPnt(center);
56
const direction = asDir(dir);
60
const xDirection = asDir(xDir);
61
axis = new oc.gp_Ax2_2(origin, direction, xDirection);
64
axis = new oc.gp_Ax2_3(origin, direction);
71
export const makeAx1 = (center: Point, dir: Point): gp_Ax1 => {
73
const origin = asPnt(center);
74
const direction = asDir(dir);
75
const axis = new oc.gp_Ax1_2(origin, direction);
81
const makeVec = (vector: Point = [0, 0, 0]): gp_Vec => {
84
if (Array.isArray(vector)) {
85
if (vector.length === 3) return new oc.gp_Vec_4(...vector);
86
else if (vector.length === 2) return new oc.gp_Vec_4(...vector, 0);
87
} else if (vector instanceof Vector) {
88
return new oc.gp_Vec_3(vector.wrapped.XYZ());
89
} else if (vector.XYZ) return new oc.gp_Vec_3(vector.XYZ());
90
return new oc.gp_Vec_4(0, 0, 0);
93
export class Vector extends WrappingObj<gp_Vec> {
94
constructor(vector: Point = [0, 0, 0]) {
95
super(makeVec(vector));
99
return `x: ${round3(this.x)}, y: ${round3(this.y)}, z: ${round3(this.z)}`;
103
return this.wrapped.X();
107
return this.wrapped.Y();
111
return this.wrapped.Z();
114
get Length(): number {
115
return this.wrapped.Magnitude();
118
toTuple(): [number, number, number] {
119
return [this.x, this.y, this.z];
122
cross(v: Vector): Vector {
123
return new Vector(this.wrapped.Crossed(v.wrapped));
126
dot(v: Vector): number {
127
return this.wrapped.Dot(v.wrapped);
130
sub(v: Vector): Vector {
131
return new Vector(this.wrapped.Subtracted(v.wrapped));
134
add(v: Vector): Vector {
135
return new Vector(this.wrapped.Added(v.wrapped));
138
multiply(scale: number): Vector {
139
return new Vector(this.wrapped.Multiplied(scale));
142
normalized(): Vector {
143
return new Vector(this.wrapped.Normalized());
146
normalize(): Vector {
147
this.wrapped.Normalize();
151
getCenter(): Vector {
155
getAngle(v: Vector): number {
156
return this.wrapped.Angle(v.wrapped) * RAD2DEG;
159
projectToPlane(plane: Plane): Vector {
160
const base = plane.origin;
161
const normal = plane.zDir;
163
const v1 = this.sub(base);
165
const v2 = normal.multiply(v1.dot(normal) / normal.Length ** 2);
166
const projection = this.sub(v2);
173
equals(other: Vector): boolean {
174
return this.wrapped.IsEqual(other.wrapped, 0.00001, 0.00001);
178
return new this.oc.gp_Pnt_2(this.wrapped.XYZ());
182
return new this.oc.gp_Dir_3(this.wrapped.XYZ());
187
center: Point = [0, 0, 0],
188
direction: Point = [0, 0, 1]
190
const ax = makeAx1(center, direction);
191
this.wrapped.Rotate(ax, angle * DEG2RAD);
197
type Direction = Point | "X" | "Y" | "Z";
199
const DIRECTIONS: Record<string, Point> = {
205
export function makeDirection(p: Direction): Point {
206
if (p === "X" || p === "Y" || p === "Z") {
207
return DIRECTIONS[p];
212
export function asPnt(coords: Point): gp_Pnt {
213
const v = new Vector(coords);
214
const pnt = v.toPnt();
219
export function asDir(coords: Point): gp_Dir {
220
const v = new Vector(coords);
221
const dir = v.toDir();
226
type CoordSystem = "reference" | { origin: Point; zDir: Point; xDir: Point };
228
export class Transformation extends WrappingObj<gp_Trsf> {
229
constructor(transform?: gp_Trsf) {
231
super(transform || new oc.gp_Trsf_1());
234
translate(xDist: number, yDist: number, zDist: number): Transformation;
235
translate(vector: Point): Transformation;
237
xDistOrVector: number | Point,
241
const translation = new Vector(
242
typeof xDistOrVector === "number"
243
? [xDistOrVector, yDist, zDist]
247
this.wrapped.SetTranslation_1(translation.wrapped);
254
position: Point = [0, 0, 0],
255
direction: Point = [0, 0, 1]
257
const dir = asDir(direction);
258
const origin = asPnt(position);
259
const axis = new this.oc.gp_Ax1_2(origin, dir);
261
this.wrapped.SetRotation_1(axis, angle * DEG2RAD);
270
inputPlane: Plane | PlaneName | Point = "YZ",
273
const r = GCWithScope();
276
let direction: Point;
278
if (typeof inputPlane === "string") {
279
const plane = r(createNamedPlane(inputPlane, inputOrigin));
280
origin = plane.origin;
281
direction = plane.zDir;
282
} else if (inputPlane instanceof Plane) {
283
origin = inputOrigin || inputPlane.origin;
284
direction = inputPlane.zDir;
286
origin = inputOrigin || [0, 0, 0];
287
direction = inputPlane;
290
const mirrorAxis = r(makeAx2(origin, direction));
291
this.wrapped.SetMirror_3(mirrorAxis);
296
scale(center: Point, scale: number): this {
297
const pnt = asPnt(center);
298
this.wrapped.SetScale(pnt, scale);
303
coordSystemChange(fromSystem: CoordSystem, toSystem: CoordSystem): this {
304
const r = GCWithScope();
306
fromSystem === "reference"
307
? new this.oc.gp_Ax3_1()
308
: makeAx3(fromSystem.origin, fromSystem.zDir, fromSystem.xDir)
312
toSystem === "reference"
313
? new this.oc.gp_Ax3_1()
314
: makeAx3(toSystem.origin, toSystem.zDir, toSystem.xDir)
316
this.wrapped.SetTransformation_1(fromAx, toAx);
320
transformPoint(point: Point): gp_Pnt {
321
const pnt = asPnt(point);
322
const newPoint = pnt.Transformed(this.wrapped);
327
transform(shape: TopoDS_Shape): TopoDS_Shape {
328
const transformer = new this.oc.BRepBuilderAPI_Transform_2(
333
return transformer.ModifiedShape(shape);
338
oc: OpenCascadeInstance;
344
// @ts-expect-error initialised indirectly
345
private _origin: Vector;
346
// @ts-expect-error initialised indirectly
347
private localToGlobal: Transformation;
348
// @ts-expect-error initialised indirectly
349
private globalToLocal: Transformation;
353
xDirection: Point | null = null,
354
normal: Point = [0, 0, 1]
358
const zDir = new Vector(normal);
359
if (zDir.Length === 0) {
360
throw new Error("normal should be non null");
362
this.zDir = zDir.normalize();
366
const ax3 = makeAx3(origin, zDir);
367
xDir = new Vector(ax3.XDirection());
370
xDir = new Vector(xDirection);
373
if (xDir.Length === 0) {
374
throw new Error("xDir should be non null");
377
this.xDir = xDir.normalize();
378
this.yDir = this.zDir.cross(this.xDir).normalize();
380
this.origin = new Vector(origin);
384
this.localToGlobal.delete();
388
this._origin.delete();
392
return new Plane(this.origin, this.xDir, this.zDir);
395
get origin(): Vector {
399
set origin(newOrigin: Vector) {
400
this._origin = newOrigin;
401
this._calcTransforms();
404
translateTo(point: Point): Plane {
405
const newPlane = this.clone();
406
newPlane.origin = new Vector(point);
410
translate(xDist: number, yDist: number, zDist: number): Plane;
411
translate(vector: Point): Plane;
412
translate(xDistOrVector: number | Point, yDist = 0, zDist = 0): Plane {
413
const translation = new Vector(
414
typeof xDistOrVector === "number"
415
? [xDistOrVector, yDist, zDist]
419
return this.translateTo(this.origin.add(translation));
422
translateX(xDist: number): Plane {
423
return this.translate(xDist, 0, 0);
426
translateY(yDist: number): Plane {
427
return this.translate(0, yDist, 0);
430
translateZ(zDist: number): Plane {
431
return this.translate(0, 0, zDist);
434
pivot(angle: number, direction: Direction = [1, 0, 0]): Plane {
435
const dir = makeDirection(direction);
436
const zDir = new Vector(this.zDir).rotate(angle, [0, 0, 0], dir);
437
const xDir = new Vector(this.xDir).rotate(angle, [0, 0, 0], dir);
439
return new Plane(this.origin, xDir, zDir);
442
rotate2DAxes(angle: number): Plane {
443
const xDir = new Vector(this.xDir).rotate(angle, [0, 0, 0], this.zDir);
445
return new Plane(this.origin, xDir, this.zDir);
448
_calcTransforms(): void {
449
const globalCoordSystem = new this.oc.gp_Ax3_1();
450
const localCoordSystem = makeAx3(this.origin, this.zDir, this.xDir);
452
const forwardT = new this.oc.gp_Trsf_1();
453
forwardT.SetTransformation_1(globalCoordSystem, localCoordSystem);
454
this.globalToLocal = new Transformation();
455
this.globalToLocal.coordSystemChange("reference", {
461
this.localToGlobal = new Transformation();
462
this.localToGlobal.coordSystemChange(
472
setOrigin2d(x: number, y: number): void {
473
this.origin = this.toWorldCoords([x, y]);
476
toLocalCoords(vec: Vector): Vector {
477
const pnt = this.globalToLocal.transformPoint(vec);
478
const newVec = new Vector(pnt);
483
toWorldCoords(v: Point): Vector {
484
const pnt = this.localToGlobal.transformPoint(v);
485
const newVec = new Vector(pnt);
491
export type PlaneName =
505
const PLANES_CONFIG: Record<
508
xDir: [number, number, number];
509
normal: [number, number, number];
562
export const createNamedPlane = (
564
sourceOrigin: Point | number = [0, 0, 0]
566
const config = PLANES_CONFIG[plane];
567
if (!config) throw new Error(`Could not find plane ${plane}`);
570
if (typeof sourceOrigin === "number") {
571
origin = config.normal.map((v: number) => v * sourceOrigin) as Point;
573
origin = sourceOrigin;
575
return new Plane(origin, config.xDir, config.normal);
578
export class BoundingBox extends WrappingObj<Bnd_Box> {
579
constructor(wrapped?: Bnd_Box) {
581
let boundBox = wrapped;
583
boundBox = new oc.Bnd_Box_1();
589
const [min, max] = this.bounds;
590
return `${new Vector(min).repr} - ${new Vector(max).repr}`;
593
get bounds(): [SimplePoint, SimplePoint] {
594
const xMin = { current: 0 };
595
const yMin = { current: 0 };
596
const zMin = { current: 0 };
597
const xMax = { current: 0 };
598
const yMax = { current: 0 };
599
const zMax = { current: 0 };
601
// @ts-ignore missing type in oc
602
this.wrapped.Get(xMin, yMin, zMin, xMax, yMax, zMax);
604
[xMin.current, yMin.current, zMin.current],
605
[xMax.current, yMax.current, zMax.current],
609
get center(): SimplePoint {
610
const [[xmin, ymin, zmin], [xmax, ymax, zmax]] = this.bounds;
612
xmin + (xmax - xmin) / 2,
613
ymin + (ymax - ymin) / 2,
614
zmin + (zmax - zmin) / 2,
618
get width(): number {
619
const [[xmin], [xmax]] = this.bounds;
620
return Math.abs(xmax - xmin);
623
get height(): number {
624
const [[, ymin], [, ymax]] = this.bounds;
625
return Math.abs(ymax - ymin);
628
get depth(): number {
629
const [[, , zmin], [, , zmax]] = this.bounds;
630
return Math.abs(zmax - zmin);
633
add(other: BoundingBox) {
634
this.wrapped.Add_1(other.wrapped);
637
isOut(other: BoundingBox): boolean {
638
return this.wrapped.IsOut_4(other.wrapped);