RepliCAD

Форк
0
640 строк · 14.2 Кб
1
import { WrappingObj, GCWithScope } from "./register.js";
2
import { DEG2RAD, RAD2DEG } from "./constants.js";
3
import { getOC } from "./oclib.js";
4

5
import {
6
  gp_Ax1,
7
  gp_Ax2,
8
  gp_Ax3,
9
  gp_Vec,
10
  gp_XYZ,
11
  gp_Dir,
12
  gp_Pnt,
13
  OpenCascadeInstance,
14
  gp_Trsf,
15
  TopoDS_Shape,
16
  Bnd_Box,
17
} from "replicad-opencascadejs";
18

19
const round3 = (v: number) => Math.round(v * 1000) / 1000;
20

21
export type SimplePoint = [number, number, number];
22
export type Point =
23
  | SimplePoint
24
  | Vector
25
  | [number, number]
26
  | { XYZ: () => gp_XYZ; delete: () => void };
27

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;
32
  return false;
33
}
34

35
export const makeAx3 = (center: Point, dir: Point, xDir?: Point): gp_Ax3 => {
36
  const oc = getOC();
37
  const origin = asPnt(center);
38
  const direction = asDir(dir);
39

40
  let axis: gp_Ax3;
41
  if (xDir) {
42
    const xDirection = asDir(xDir);
43
    axis = new oc.gp_Ax3_3(origin, direction, xDirection);
44
    xDirection.delete();
45
  } else {
46
    axis = new oc.gp_Ax3_4(origin, direction);
47
  }
48
  origin.delete();
49
  direction.delete();
50
  return axis;
51
};
52

53
export const makeAx2 = (center: Point, dir: Point, xDir?: Point): gp_Ax2 => {
54
  const oc = getOC();
55
  const origin = asPnt(center);
56
  const direction = asDir(dir);
57

58
  let axis: gp_Ax2;
59
  if (xDir) {
60
    const xDirection = asDir(xDir);
61
    axis = new oc.gp_Ax2_2(origin, direction, xDirection);
62
    xDirection.delete();
63
  } else {
64
    axis = new oc.gp_Ax2_3(origin, direction);
65
  }
66
  origin.delete();
67
  direction.delete();
68
  return axis;
69
};
70

71
export const makeAx1 = (center: Point, dir: Point): gp_Ax1 => {
72
  const oc = getOC();
73
  const origin = asPnt(center);
74
  const direction = asDir(dir);
75
  const axis = new oc.gp_Ax1_2(origin, direction);
76
  origin.delete();
77
  direction.delete();
78
  return axis;
79
};
80

81
const makeVec = (vector: Point = [0, 0, 0]): gp_Vec => {
82
  const oc = getOC();
83

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);
91
};
92

93
export class Vector extends WrappingObj<gp_Vec> {
94
  constructor(vector: Point = [0, 0, 0]) {
95
    super(makeVec(vector));
96
  }
97

98
  get repr(): string {
99
    return `x: ${round3(this.x)}, y: ${round3(this.y)}, z: ${round3(this.z)}`;
100
  }
101

102
  get x(): number {
103
    return this.wrapped.X();
104
  }
105

106
  get y(): number {
107
    return this.wrapped.Y();
108
  }
109

110
  get z(): number {
111
    return this.wrapped.Z();
112
  }
113

114
  get Length(): number {
115
    return this.wrapped.Magnitude();
116
  }
117

118
  toTuple(): [number, number, number] {
119
    return [this.x, this.y, this.z];
120
  }
121

122
  cross(v: Vector): Vector {
123
    return new Vector(this.wrapped.Crossed(v.wrapped));
124
  }
125

126
  dot(v: Vector): number {
127
    return this.wrapped.Dot(v.wrapped);
128
  }
129

130
  sub(v: Vector): Vector {
131
    return new Vector(this.wrapped.Subtracted(v.wrapped));
132
  }
133

134
  add(v: Vector): Vector {
135
    return new Vector(this.wrapped.Added(v.wrapped));
136
  }
137

138
  multiply(scale: number): Vector {
139
    return new Vector(this.wrapped.Multiplied(scale));
140
  }
141

142
  normalized(): Vector {
143
    return new Vector(this.wrapped.Normalized());
144
  }
145

146
  normalize(): Vector {
147
    this.wrapped.Normalize();
148
    return this;
149
  }
150

151
  getCenter(): Vector {
152
    return this;
153
  }
154

155
  getAngle(v: Vector): number {
156
    return this.wrapped.Angle(v.wrapped) * RAD2DEG;
157
  }
158

159
  projectToPlane(plane: Plane): Vector {
160
    const base = plane.origin;
161
    const normal = plane.zDir;
162

163
    const v1 = this.sub(base);
164

165
    const v2 = normal.multiply(v1.dot(normal) / normal.Length ** 2);
166
    const projection = this.sub(v2);
167

168
    v1.delete();
169
    v2.delete();
170

171
    return projection;
172
  }
173
  equals(other: Vector): boolean {
174
    return this.wrapped.IsEqual(other.wrapped, 0.00001, 0.00001);
175
  }
176

177
  toPnt(): gp_Pnt {
178
    return new this.oc.gp_Pnt_2(this.wrapped.XYZ());
179
  }
180

181
  toDir(): gp_Dir {
182
    return new this.oc.gp_Dir_3(this.wrapped.XYZ());
183
  }
184

185
  rotate(
186
    angle: number,
187
    center: Point = [0, 0, 0],
188
    direction: Point = [0, 0, 1]
189
  ): Vector {
190
    const ax = makeAx1(center, direction);
191
    this.wrapped.Rotate(ax, angle * DEG2RAD);
192
    ax.delete();
193
    return this;
194
  }
195
}
196

197
type Direction = Point | "X" | "Y" | "Z";
198

199
const DIRECTIONS: Record<string, Point> = {
200
  X: [1, 0, 0],
201
  Y: [0, 1, 0],
202
  Z: [0, 0, 1],
203
};
204

205
export function makeDirection(p: Direction): Point {
206
  if (p === "X" || p === "Y" || p === "Z") {
207
    return DIRECTIONS[p];
208
  }
209
  return p;
210
}
211

212
export function asPnt(coords: Point): gp_Pnt {
213
  const v = new Vector(coords);
214
  const pnt = v.toPnt();
215
  v.delete();
216
  return pnt;
217
}
218

219
export function asDir(coords: Point): gp_Dir {
220
  const v = new Vector(coords);
221
  const dir = v.toDir();
222
  v.delete();
223
  return dir;
224
}
225

226
type CoordSystem = "reference" | { origin: Point; zDir: Point; xDir: Point };
227

228
export class Transformation extends WrappingObj<gp_Trsf> {
229
  constructor(transform?: gp_Trsf) {
230
    const oc = getOC();
231
    super(transform || new oc.gp_Trsf_1());
232
  }
233

234
  translate(xDist: number, yDist: number, zDist: number): Transformation;
235
  translate(vector: Point): Transformation;
236
  translate(
237
    xDistOrVector: number | Point,
238
    yDist = 0,
239
    zDist = 0
240
  ): Transformation {
241
    const translation = new Vector(
242
      typeof xDistOrVector === "number"
243
        ? [xDistOrVector, yDist, zDist]
244
        : xDistOrVector
245
    );
246

247
    this.wrapped.SetTranslation_1(translation.wrapped);
248

249
    return this;
250
  }
251

252
  rotate(
253
    angle: number,
254
    position: Point = [0, 0, 0],
255
    direction: Point = [0, 0, 1]
256
  ): Transformation {
257
    const dir = asDir(direction);
258
    const origin = asPnt(position);
259
    const axis = new this.oc.gp_Ax1_2(origin, dir);
260

261
    this.wrapped.SetRotation_1(axis, angle * DEG2RAD);
262
    axis.delete();
263
    dir.delete();
264
    origin.delete();
265

266
    return this;
267
  }
268

269
  mirror(
270
    inputPlane: Plane | PlaneName | Point = "YZ",
271
    inputOrigin?: Point
272
  ): this {
273
    const r = GCWithScope();
274

275
    let origin: Point;
276
    let direction: Point;
277

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;
285
    } else {
286
      origin = inputOrigin || [0, 0, 0];
287
      direction = inputPlane;
288
    }
289

290
    const mirrorAxis = r(makeAx2(origin, direction));
291
    this.wrapped.SetMirror_3(mirrorAxis);
292

293
    return this;
294
  }
295

296
  scale(center: Point, scale: number): this {
297
    const pnt = asPnt(center);
298
    this.wrapped.SetScale(pnt, scale);
299
    pnt.delete();
300
    return this;
301
  }
302

303
  coordSystemChange(fromSystem: CoordSystem, toSystem: CoordSystem): this {
304
    const r = GCWithScope();
305
    const fromAx = r(
306
      fromSystem === "reference"
307
        ? new this.oc.gp_Ax3_1()
308
        : makeAx3(fromSystem.origin, fromSystem.zDir, fromSystem.xDir)
309
    );
310

311
    const toAx = r(
312
      toSystem === "reference"
313
        ? new this.oc.gp_Ax3_1()
314
        : makeAx3(toSystem.origin, toSystem.zDir, toSystem.xDir)
315
    );
316
    this.wrapped.SetTransformation_1(fromAx, toAx);
317
    return this;
318
  }
319

320
  transformPoint(point: Point): gp_Pnt {
321
    const pnt = asPnt(point);
322
    const newPoint = pnt.Transformed(this.wrapped);
323
    pnt.delete();
324
    return newPoint;
325
  }
326

327
  transform(shape: TopoDS_Shape): TopoDS_Shape {
328
    const transformer = new this.oc.BRepBuilderAPI_Transform_2(
329
      shape,
330
      this.wrapped,
331
      true
332
    );
333
    return transformer.ModifiedShape(shape);
334
  }
335
}
336

337
export class Plane {
338
  oc: OpenCascadeInstance;
339

340
  xDir: Vector;
341
  yDir: Vector;
342
  zDir: Vector;
343

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;
350

351
  constructor(
352
    origin: Point,
353
    xDirection: Point | null = null,
354
    normal: Point = [0, 0, 1]
355
  ) {
356
    this.oc = getOC();
357

358
    const zDir = new Vector(normal);
359
    if (zDir.Length === 0) {
360
      throw new Error("normal should be non null");
361
    }
362
    this.zDir = zDir.normalize();
363

364
    let xDir: Vector;
365
    if (!xDirection) {
366
      const ax3 = makeAx3(origin, zDir);
367
      xDir = new Vector(ax3.XDirection());
368
      ax3.delete();
369
    } else {
370
      xDir = new Vector(xDirection);
371
    }
372

373
    if (xDir.Length === 0) {
374
      throw new Error("xDir should be non null");
375
    }
376

377
    this.xDir = xDir.normalize();
378
    this.yDir = this.zDir.cross(this.xDir).normalize();
379

380
    this.origin = new Vector(origin);
381
  }
382

383
  delete(): void {
384
    this.localToGlobal.delete();
385
    this.xDir.delete();
386
    this.yDir.delete();
387
    this.zDir.delete();
388
    this._origin.delete();
389
  }
390

391
  clone(): Plane {
392
    return new Plane(this.origin, this.xDir, this.zDir);
393
  }
394

395
  get origin(): Vector {
396
    return this._origin;
397
  }
398

399
  set origin(newOrigin: Vector) {
400
    this._origin = newOrigin;
401
    this._calcTransforms();
402
  }
403

404
  translateTo(point: Point): Plane {
405
    const newPlane = this.clone();
406
    newPlane.origin = new Vector(point);
407
    return newPlane;
408
  }
409

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]
416
        : xDistOrVector
417
    );
418

419
    return this.translateTo(this.origin.add(translation));
420
  }
421

422
  translateX(xDist: number): Plane {
423
    return this.translate(xDist, 0, 0);
424
  }
425

426
  translateY(yDist: number): Plane {
427
    return this.translate(0, yDist, 0);
428
  }
429

430
  translateZ(zDist: number): Plane {
431
    return this.translate(0, 0, zDist);
432
  }
433

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);
438

439
    return new Plane(this.origin, xDir, zDir);
440
  }
441

442
  rotate2DAxes(angle: number): Plane {
443
    const xDir = new Vector(this.xDir).rotate(angle, [0, 0, 0], this.zDir);
444

445
    return new Plane(this.origin, xDir, this.zDir);
446
  }
447

448
  _calcTransforms(): void {
449
    const globalCoordSystem = new this.oc.gp_Ax3_1();
450
    const localCoordSystem = makeAx3(this.origin, this.zDir, this.xDir);
451

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", {
456
      origin: this.origin,
457
      zDir: this.zDir,
458
      xDir: this.xDir,
459
    });
460

461
    this.localToGlobal = new Transformation();
462
    this.localToGlobal.coordSystemChange(
463
      {
464
        origin: this.origin,
465
        zDir: this.zDir,
466
        xDir: this.xDir,
467
      },
468
      "reference"
469
    );
470
  }
471

472
  setOrigin2d(x: number, y: number): void {
473
    this.origin = this.toWorldCoords([x, y]);
474
  }
475

476
  toLocalCoords(vec: Vector): Vector {
477
    const pnt = this.globalToLocal.transformPoint(vec);
478
    const newVec = new Vector(pnt);
479
    pnt.delete();
480
    return newVec;
481
  }
482

483
  toWorldCoords(v: Point): Vector {
484
    const pnt = this.localToGlobal.transformPoint(v);
485
    const newVec = new Vector(pnt);
486
    pnt.delete();
487
    return newVec;
488
  }
489
}
490

491
export type PlaneName =
492
  | "XY"
493
  | "YZ"
494
  | "ZX"
495
  | "XZ"
496
  | "YX"
497
  | "ZY"
498
  | "front"
499
  | "back"
500
  | "left"
501
  | "right"
502
  | "top"
503
  | "bottom";
504

505
const PLANES_CONFIG: Record<
506
  PlaneName,
507
  {
508
    xDir: [number, number, number];
509
    normal: [number, number, number];
510
  }
511
> = {
512
  XY: {
513
    xDir: [1, 0, 0],
514
    normal: [0, 0, 1],
515
  },
516
  YZ: {
517
    xDir: [0, 1, 0],
518
    normal: [1, 0, 0],
519
  },
520
  ZX: {
521
    xDir: [0, 0, 1],
522
    normal: [0, 1, 0],
523
  },
524
  XZ: {
525
    xDir: [1, 0, 0],
526
    normal: [0, -1, 0],
527
  },
528
  YX: {
529
    xDir: [0, 1, 0],
530
    normal: [0, 0, -1],
531
  },
532
  ZY: {
533
    xDir: [0, 0, 1],
534
    normal: [-1, 0, 0],
535
  },
536
  front: {
537
    xDir: [1, 0, 0],
538
    normal: [0, 0, 1],
539
  },
540
  back: {
541
    xDir: [-1, 0, 0],
542
    normal: [0, 0, -1],
543
  },
544
  left: {
545
    xDir: [0, 0, 1],
546
    normal: [-1, 0, 0],
547
  },
548
  right: {
549
    xDir: [0, 0, -1],
550
    normal: [1, 0, 0],
551
  },
552
  top: {
553
    xDir: [1, 0, 0],
554
    normal: [0, 1, 0],
555
  },
556
  bottom: {
557
    xDir: [1, 0, 0],
558
    normal: [0, -1, 0],
559
  },
560
};
561

562
export const createNamedPlane = (
563
  plane: PlaneName,
564
  sourceOrigin: Point | number = [0, 0, 0]
565
): Plane => {
566
  const config = PLANES_CONFIG[plane];
567
  if (!config) throw new Error(`Could not find plane ${plane}`);
568

569
  let origin: Point;
570
  if (typeof sourceOrigin === "number") {
571
    origin = config.normal.map((v: number) => v * sourceOrigin) as Point;
572
  } else {
573
    origin = sourceOrigin;
574
  }
575
  return new Plane(origin, config.xDir, config.normal);
576
};
577

578
export class BoundingBox extends WrappingObj<Bnd_Box> {
579
  constructor(wrapped?: Bnd_Box) {
580
    const oc = getOC();
581
    let boundBox = wrapped;
582
    if (!boundBox) {
583
      boundBox = new oc.Bnd_Box_1();
584
    }
585
    super(boundBox);
586
  }
587

588
  get repr(): string {
589
    const [min, max] = this.bounds;
590
    return `${new Vector(min).repr} - ${new Vector(max).repr}`;
591
  }
592

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 };
600

601
    // @ts-ignore missing type in oc
602
    this.wrapped.Get(xMin, yMin, zMin, xMax, yMax, zMax);
603
    return [
604
      [xMin.current, yMin.current, zMin.current],
605
      [xMax.current, yMax.current, zMax.current],
606
    ];
607
  }
608

609
  get center(): SimplePoint {
610
    const [[xmin, ymin, zmin], [xmax, ymax, zmax]] = this.bounds;
611
    return [
612
      xmin + (xmax - xmin) / 2,
613
      ymin + (ymax - ymin) / 2,
614
      zmin + (zmax - zmin) / 2,
615
    ];
616
  }
617

618
  get width(): number {
619
    const [[xmin], [xmax]] = this.bounds;
620
    return Math.abs(xmax - xmin);
621
  }
622

623
  get height(): number {
624
    const [[, ymin], [, ymax]] = this.bounds;
625
    return Math.abs(ymax - ymin);
626
  }
627

628
  get depth(): number {
629
    const [[, , zmin], [, , zmax]] = this.bounds;
630
    return Math.abs(zmax - zmin);
631
  }
632

633
  add(other: BoundingBox) {
634
    this.wrapped.Add_1(other.wrapped);
635
  }
636

637
  isOut(other: BoundingBox): boolean {
638
    return this.wrapped.IsOut_4(other.wrapped);
639
  }
640
}
641

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.