fluidcad 0.0.32 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/lib/dist/common/scene-object.d.ts +2 -0
  2. package/lib/dist/common/scene-object.js +6 -0
  3. package/lib/dist/common/solid.d.ts +4 -1
  4. package/lib/dist/common/solid.js +13 -0
  5. package/lib/dist/core/index.d.ts +2 -1
  6. package/lib/dist/core/index.js +1 -0
  7. package/lib/dist/core/interfaces.d.ts +76 -0
  8. package/lib/dist/core/load.d.ts +2 -2
  9. package/lib/dist/core/rib.d.ts +18 -0
  10. package/lib/dist/core/rib.js +37 -0
  11. package/lib/dist/features/load.d.ts +6 -0
  12. package/lib/dist/features/load.js +53 -1
  13. package/lib/dist/features/rib.d.ts +31 -0
  14. package/lib/dist/features/rib.js +321 -0
  15. package/lib/dist/features/select.d.ts +1 -0
  16. package/lib/dist/features/select.js +81 -10
  17. package/lib/dist/filters/edge/belongs-to-face.d.ts +12 -9
  18. package/lib/dist/filters/edge/belongs-to-face.js +64 -15
  19. package/lib/dist/filters/filter-builder-base.d.ts +25 -0
  20. package/lib/dist/filters/filter-builder-base.js +47 -0
  21. package/lib/dist/filters/filter.js +39 -14
  22. package/lib/dist/filters/from-object.d.ts +4 -0
  23. package/lib/dist/filters/from-object.js +10 -0
  24. package/lib/dist/helpers/scene-helpers.d.ts +1 -1
  25. package/lib/dist/helpers/scene-helpers.js +146 -12
  26. package/lib/dist/io/file-import.d.ts +5 -1
  27. package/lib/dist/io/file-import.js +29 -18
  28. package/lib/dist/oc/color-transfer.d.ts +19 -8
  29. package/lib/dist/oc/color-transfer.js +70 -12
  30. package/lib/dist/oc/extrude-ops.d.ts +2 -1
  31. package/lib/dist/oc/extrude-ops.js +51 -2
  32. package/lib/dist/oc/rib-ops.d.ts +35 -0
  33. package/lib/dist/oc/rib-ops.js +619 -0
  34. package/lib/dist/oc/topology-index.d.ts +6 -0
  35. package/lib/dist/oc/topology-index.js +36 -0
  36. package/lib/dist/rendering/render-solid.js +1 -3
  37. package/lib/dist/rendering/render.d.ts +1 -0
  38. package/lib/dist/rendering/render.js +44 -1
  39. package/lib/dist/rendering/scene-compare.js +3 -0
  40. package/lib/dist/rendering/scene.d.ts +1 -0
  41. package/lib/dist/rendering/scene.js +4 -0
  42. package/lib/dist/tests/features/color-lineage.test.js +18 -0
  43. package/lib/dist/tests/features/filter-positional.test.d.ts +1 -0
  44. package/lib/dist/tests/features/filter-positional.test.js +129 -0
  45. package/lib/dist/tests/features/rib.test.d.ts +1 -0
  46. package/lib/dist/tests/features/rib.test.js +598 -0
  47. package/lib/dist/tests/scene-compare.test.d.ts +1 -0
  48. package/lib/dist/tests/scene-compare.test.js +77 -0
  49. package/lib/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/ui/dist/assets/{index-DMw0OYCF.js → index-CFi9p7wR.js} +9 -9
  52. package/ui/dist/index.html +1 -1
@@ -59,6 +59,7 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
59
59
  constructor();
60
60
  get id(): string;
61
61
  get parentId(): string | null;
62
+ inheritIdentityFrom(other: SceneObject): void;
62
63
  private setParent;
63
64
  protected setAlwaysVisible(): void;
64
65
  isAlwaysVisible(): boolean;
@@ -150,6 +151,7 @@ export declare abstract class SceneObject implements Comparable<SceneObject>, Se
150
151
  setOrder(order: number): void;
151
152
  getOrder(): number;
152
153
  getName(): string;
154
+ hasCustomName(): boolean;
153
155
  name(value: string): this;
154
156
  guide(): this;
155
157
  reusable(): this;
@@ -36,6 +36,9 @@ export class SceneObject {
36
36
  get parentId() {
37
37
  return this._parent?.id || null;
38
38
  }
39
+ inheritIdentityFrom(other) {
40
+ this._id = other._id;
41
+ }
39
42
  setParent(parent) {
40
43
  this._parent = parent;
41
44
  }
@@ -452,6 +455,9 @@ export class SceneObject {
452
455
  getName() {
453
456
  return this._name ?? this.getType();
454
457
  }
458
+ hasCustomName() {
459
+ return this._name !== null;
460
+ }
455
461
  name(value) {
456
462
  this._name = value;
457
463
  return this;
@@ -1,4 +1,4 @@
1
- import type { TopoDS_Edge, TopoDS_Face, TopoDS_Solid } from "occjs-wrapper";
1
+ import type { TopoDS_Edge, TopoDS_Face, TopoDS_Solid, TopTools_IndexedDataMapOfShapeListOfShape } from "occjs-wrapper";
2
2
  import { ShapeType } from "./shape-type.js";
3
3
  import { Shape } from "./shape.js";
4
4
  import { Face } from "./face.js";
@@ -6,6 +6,7 @@ import { Edge } from "./edge.js";
6
6
  export declare class Solid extends Shape<TopoDS_Solid> {
7
7
  private faces;
8
8
  private edges;
9
+ private edgeToFacesIndex;
9
10
  constructor(solid: TopoDS_Solid);
10
11
  getType(): ShapeType;
11
12
  isSolid(): boolean;
@@ -15,6 +16,8 @@ export declare class Solid extends Shape<TopoDS_Solid> {
15
16
  getFace(face: TopoDS_Face): Face | null;
16
17
  hasFace(face: TopoDS_Face): boolean;
17
18
  hasEdge(edge: TopoDS_Edge): TopoDS_Edge;
19
+ getEdgeToFacesIndex(): TopTools_IndexedDataMapOfShapeListOfShape;
20
+ dispose(): void;
18
21
  copy(): Shape;
19
22
  static fromTopoDSSolid(solid: TopoDS_Solid): Solid;
20
23
  }
@@ -1,8 +1,10 @@
1
1
  import { Explorer } from "../oc/explorer.js";
2
+ import { TopologyIndex } from "../oc/topology-index.js";
2
3
  import { Shape } from "./shape.js";
3
4
  export class Solid extends Shape {
4
5
  faces = null;
5
6
  edges = null;
7
+ edgeToFacesIndex = null;
6
8
  constructor(solid) {
7
9
  super(solid);
8
10
  }
@@ -59,6 +61,17 @@ export class Solid extends Shape {
59
61
  }
60
62
  return null;
61
63
  }
64
+ getEdgeToFacesIndex() {
65
+ if (!this.edgeToFacesIndex) {
66
+ this.edgeToFacesIndex = TopologyIndex.buildEdgeToFaces(this.getShape());
67
+ }
68
+ return this.edgeToFacesIndex;
69
+ }
70
+ dispose() {
71
+ this.edgeToFacesIndex?.delete();
72
+ this.edgeToFacesIndex = null;
73
+ super.dispose();
74
+ }
62
75
  copy() {
63
76
  const copied = new Solid(this.getShape());
64
77
  for (const entry of this.colorMap) {
@@ -1,4 +1,4 @@
1
- export type { ISceneObject, ITransformable, IBooleanOperation, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft } from "./interfaces.js";
1
+ export type { ISceneObject, ITransformable, IBooleanOperation, IPlane, IAxis, ISelect, IGeometry, IExtrudableGeometry, IRect, ISlot, IPolygon, ITwoObjectsTangentLine, ITangentArcTwoObjects, IExtrude, ICut, ICommon, ISweep, ILoft, IRevolve, IDraft, IRib } from "./interfaces.js";
2
2
  export { default as axis } from "./axis.js";
3
3
  export { default as local } from "./local.js";
4
4
  export { default as plane } from "./plane.js";
@@ -23,6 +23,7 @@ export { default as repeat } from "./repeat.js";
23
23
  export { default as load } from "./load.js";
24
24
  export { default as loft } from "./loft.js";
25
25
  export { default as sweep } from "./sweep.js";
26
+ export { default as rib } from "./rib.js";
26
27
  export { default as color } from "./color.js";
27
28
  export { default as draft } from "./draft.js";
28
29
  export { default as remove } from "./remove.js";
@@ -22,6 +22,7 @@ export { default as repeat } from "./repeat.js";
22
22
  export { default as load } from "./load.js";
23
23
  export { default as loft } from "./loft.js";
24
24
  export { default as sweep } from "./sweep.js";
25
+ export { default as rib } from "./rib.js";
25
26
  export { default as color } from "./color.js";
26
27
  export { default as draft } from "./draft.js";
27
28
  export { default as remove } from "./remove.js";
@@ -19,6 +19,25 @@ export interface ISceneObject {
19
19
  */
20
20
  reusable(): this;
21
21
  }
22
+ export interface ILoadFile extends ISceneObject {
23
+ /**
24
+ * Skip applying colors from the imported file's color metadata sidecar.
25
+ */
26
+ noColors(): this;
27
+ /**
28
+ * Keep only the solids at the given 0-based indices (in load order).
29
+ * Combined with {@link exclude} by applying include first, then exclude.
30
+ * Repeated calls accumulate.
31
+ * @param indices - The 0-based solid indices to keep.
32
+ */
33
+ include(...indices: number[]): this;
34
+ /**
35
+ * Drop the solids at the given 0-based indices. Applied after {@link include}.
36
+ * Repeated calls accumulate.
37
+ * @param indices - The 0-based solid indices to drop.
38
+ */
39
+ exclude(...indices: number[]): this;
40
+ }
22
41
  export interface IBooleanOperation extends ISceneObject {
23
42
  /**
24
43
  * Additive boolean operation — fuses the result with all intersecting scene objects.
@@ -695,6 +714,63 @@ export interface IRotate extends ISceneObject {
695
714
  }
696
715
  export interface IDraft extends ISceneObject {
697
716
  }
717
+ export interface IRib extends IBooleanOperation {
718
+ /**
719
+ * Selects faces at the start (base) of the rib — the profile face at the sketch plane.
720
+ * @param args - Numeric indices or {@link FaceFilterBuilder} instances to filter the selection.
721
+ */
722
+ startFaces(...args: (number | FaceFilterBuilder)[]): ISceneObject;
723
+ /**
724
+ * Selects faces at the end (top) of the rib — where the rib meets the boundary.
725
+ * @param args - Numeric indices or {@link FaceFilterBuilder} instances to filter the selection.
726
+ */
727
+ endFaces(...args: (number | FaceFilterBuilder)[]): ISceneObject;
728
+ /**
729
+ * Selects the lateral wall faces of the rib.
730
+ * @param args - Numeric indices or {@link FaceFilterBuilder} instances to filter the selection.
731
+ */
732
+ sideFaces(...args: (number | FaceFilterBuilder)[]): ISceneObject;
733
+ /**
734
+ * Selects the small cap faces at the spine endpoints.
735
+ * @param args - Numeric indices or {@link FaceFilterBuilder} instances to filter the selection.
736
+ */
737
+ capFaces(...args: (number | FaceFilterBuilder)[]): ISceneObject;
738
+ /**
739
+ * Selects edges on the start faces.
740
+ * @param args - Numeric indices or {@link EdgeFilterBuilder} instances to filter the selection.
741
+ */
742
+ startEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
743
+ /**
744
+ * Selects edges on the end faces.
745
+ * @param args - Numeric indices or {@link EdgeFilterBuilder} instances to filter the selection.
746
+ */
747
+ endEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
748
+ /**
749
+ * Selects edges on the side faces, excluding edges shared with start/end faces.
750
+ * @param args - Numeric indices or {@link EdgeFilterBuilder} instances to filter the selection.
751
+ */
752
+ sideEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
753
+ /**
754
+ * Selects edges on the cap faces.
755
+ * @param args - Numeric indices or {@link EdgeFilterBuilder} instances to filter the selection.
756
+ */
757
+ capEdges(...args: (number | EdgeFilterBuilder)[]): ISceneObject;
758
+ /**
759
+ * Applies a draft (taper) angle to the rib walls.
760
+ * @param value - A single angle for uniform draft, or a `[start, end]` tuple for asymmetric draft.
761
+ */
762
+ draft(value: number | [number, number]): this;
763
+ /**
764
+ * Switches the extrusion direction to parallel to the sketch plane
765
+ * (perpendicular to the spine within the plane) instead of normal to it.
766
+ */
767
+ parallel(): this;
768
+ /**
769
+ * Extends the rib's side faces at the spine endpoints outward to blend
770
+ * with the target solids' walls.
771
+ */
772
+ extend(): this;
773
+ }
698
774
  export interface IShell extends ISceneObject {
699
775
  /**
700
776
  * Selects the inner wall faces created by the shell operation (from thickness removal).
@@ -1,10 +1,10 @@
1
- import { ISceneObject } from "./interfaces.js";
1
+ import { ILoadFile } from "./interfaces.js";
2
2
  interface LoadFunction {
3
3
  /**
4
4
  * Loads a 3D model file (STEP, STL, etc.) by filename.
5
5
  * @param fileName - The path to the model file
6
6
  */
7
- (fileName: string): ISceneObject;
7
+ (fileName: string): ILoadFile;
8
8
  }
9
9
  declare const _default: LoadFunction;
10
10
  export default _default;
@@ -0,0 +1,18 @@
1
+ import { IRib, ISceneObject } from "./interfaces.js";
2
+ interface RibFunction {
3
+ /**
4
+ * Creates a rib from the last sketch with the given thickness.
5
+ * The rib extends in the sketch normal direction until it reaches
6
+ * surrounding solids. Positive thickness = forward, negative = reverse.
7
+ * @param thickness - Wall thickness (sign controls direction)
8
+ */
9
+ (thickness: number): IRib;
10
+ /**
11
+ * Creates a rib from an explicit sketch spine with the given thickness.
12
+ * @param thickness - Wall thickness (sign controls direction)
13
+ * @param spine - The sketch providing the rib spine wire and plane
14
+ */
15
+ (thickness: number, spine: ISceneObject): IRib;
16
+ }
17
+ declare const _default: RibFunction;
18
+ export default _default;
@@ -0,0 +1,37 @@
1
+ import { SceneObject } from "../common/scene-object.js";
2
+ import { registerBuilder } from "../index.js";
3
+ import { Rib } from "../features/rib.js";
4
+ function isExtrudable(obj) {
5
+ return obj instanceof SceneObject && obj.isExtrudable();
6
+ }
7
+ function build(context) {
8
+ //@ts-ignore
9
+ return function rib() {
10
+ const args = [...arguments];
11
+ if (args.length === 0) {
12
+ throw new Error("rib() requires at least a thickness argument.");
13
+ }
14
+ const thickness = args[0];
15
+ if (typeof thickness !== 'number' || thickness === 0) {
16
+ throw new Error("rib() thickness must be a non-zero number.");
17
+ }
18
+ let spine;
19
+ let extrudable;
20
+ if (args.length > 1 && isExtrudable(args[1])) {
21
+ spine = args[1];
22
+ extrudable = args[1];
23
+ }
24
+ else {
25
+ const lastExtrudable = context.getLastExtrudable();
26
+ if (!lastExtrudable) {
27
+ throw new Error("rib() requires a sketch. No sketch found in the scene.");
28
+ }
29
+ spine = lastExtrudable;
30
+ extrudable = lastExtrudable;
31
+ }
32
+ const result = new Rib(thickness, spine, extrudable);
33
+ context.addSceneObject(result);
34
+ return result;
35
+ };
36
+ }
37
+ export default registerBuilder(build);
@@ -1,7 +1,13 @@
1
1
  import { SceneObject } from "../common/scene-object.js";
2
2
  export declare class LoadFile extends SceneObject {
3
3
  fileName: string;
4
+ private _noColors;
5
+ private _include?;
6
+ private _exclude;
4
7
  constructor(fileName: string);
8
+ noColors(): this;
9
+ include(...indices: number[]): this;
10
+ exclude(...indices: number[]): this;
5
11
  build(): void;
6
12
  compareTo(other: LoadFile): boolean;
7
13
  getType(): string;
@@ -2,12 +2,38 @@ import { SceneObject } from "../common/scene-object.js";
2
2
  import { FileImport } from "../io/file-import.js";
3
3
  export class LoadFile extends SceneObject {
4
4
  fileName;
5
+ _noColors = false;
6
+ _include;
7
+ _exclude = new Set();
5
8
  constructor(fileName) {
6
9
  super();
7
10
  this.fileName = fileName;
8
11
  }
12
+ noColors() {
13
+ this._noColors = true;
14
+ return this;
15
+ }
16
+ include(...indices) {
17
+ if (!this._include) {
18
+ this._include = new Set();
19
+ }
20
+ for (const i of indices) {
21
+ this._include.add(i);
22
+ }
23
+ return this;
24
+ }
25
+ exclude(...indices) {
26
+ for (const i of indices) {
27
+ this._exclude.add(i);
28
+ }
29
+ return this;
30
+ }
9
31
  build() {
10
- const shapes = FileImport.deserializeShapesWithMetadata(this.fileName);
32
+ const shapes = FileImport.deserializeShapesWithMetadata(this.fileName, {
33
+ noColors: this._noColors,
34
+ include: this._include,
35
+ exclude: this._exclude.size > 0 ? this._exclude : undefined,
36
+ });
11
37
  this.addShapes(shapes);
12
38
  }
13
39
  compareTo(other) {
@@ -20,6 +46,15 @@ export class LoadFile extends SceneObject {
20
46
  if (this.fileName !== other.fileName) {
21
47
  return false;
22
48
  }
49
+ if (this._noColors !== other._noColors) {
50
+ return false;
51
+ }
52
+ if (!equalSets(this._include, other._include)) {
53
+ return false;
54
+ }
55
+ if (!equalSets(this._exclude, other._exclude)) {
56
+ return false;
57
+ }
23
58
  return true;
24
59
  }
25
60
  getType() {
@@ -29,3 +64,20 @@ export class LoadFile extends SceneObject {
29
64
  return {};
30
65
  }
31
66
  }
67
+ function equalSets(a, b) {
68
+ if (a === b) {
69
+ return true;
70
+ }
71
+ if (!a || !b) {
72
+ return false;
73
+ }
74
+ if (a.size !== b.size) {
75
+ return false;
76
+ }
77
+ for (const v of a) {
78
+ if (!b.has(v)) {
79
+ return false;
80
+ }
81
+ }
82
+ return true;
83
+ }
@@ -0,0 +1,31 @@
1
+ import { BuildSceneObjectContext, SceneObject } from "../common/scene-object.js";
2
+ import { Extrudable } from "../helpers/types.js";
3
+ import { ExtrudeBase } from "./extrude-base.js";
4
+ import { IRib } from "../core/interfaces.js";
5
+ export declare class Rib extends ExtrudeBase implements IRib {
6
+ private _thickness;
7
+ private _spine;
8
+ private _parallel;
9
+ private _extend;
10
+ constructor(thickness: number, spine: SceneObject, extrudable?: Extrudable);
11
+ get thickness(): number;
12
+ get spine(): SceneObject;
13
+ parallel(): this;
14
+ extend(): this;
15
+ validate(): void;
16
+ build(context: BuildSceneObjectContext): void;
17
+ private getSpineWire;
18
+ getDependencies(): SceneObject[];
19
+ createCopy(remap: Map<SceneObject, SceneObject>): SceneObject;
20
+ compareTo(other: Rib): boolean;
21
+ getType(): string;
22
+ serialize(): {
23
+ thickness: number;
24
+ spine: any;
25
+ extrudable: any;
26
+ operationMode: "new" | "remove";
27
+ parallel: true;
28
+ extend: true;
29
+ draft: number | [number, number];
30
+ };
31
+ }