From ab9495be0e2452ef6bef45bc7b27c69bc2a227dc Mon Sep 17 00:00:00 2001 From: Steve Genoud Date: Fri, 27 Sep 2024 15:33:47 +0200 Subject: [PATCH] Add some generic filters --- packages/replicad/src/finders/definitions.ts | 13 ++- .../replicad/src/finders/generic3dfinder.ts | 97 +++++++++++-------- packages/replicad/src/finders/index.ts | 4 +- packages/replicad/src/measureShape.ts | 63 +++++++++++- packages/replicad/src/shapeHelpers.ts | 8 ++ packages/replicad/src/utils/ProgressRange.ts | 10 ++ 6 files changed, 143 insertions(+), 52 deletions(-) create mode 100644 packages/replicad/src/utils/ProgressRange.ts diff --git a/packages/replicad/src/finders/definitions.ts b/packages/replicad/src/finders/definitions.ts index f6ca49b..0975c3c 100644 --- a/packages/replicad/src/finders/definitions.ts +++ b/packages/replicad/src/finders/definitions.ts @@ -17,14 +17,13 @@ export const PLANE_TO_DIR: Record = { export type FaceOrEdge = Face | Edge; +export type FilterFcn = { + element: Type; + normal: Vector | null; +}; + export abstract class Finder { - protected filters: (({ - element, - normal, - }: { - element: Type; - normal: Vector | null; - }) => boolean)[]; + protected filters: (({ element, normal }: FilterFcn) => boolean)[]; protected abstract applyFilter(shape: FilterType): Type[]; diff --git a/packages/replicad/src/finders/generic3dfinder.ts b/packages/replicad/src/finders/generic3dfinder.ts index 0e7b6ff..b68d1f9 100644 --- a/packages/replicad/src/finders/generic3dfinder.ts +++ b/packages/replicad/src/finders/generic3dfinder.ts @@ -1,15 +1,31 @@ -import { Direction, DIRECTIONS, FaceOrEdge, Finder } from "./definitions"; - -import { Vector, asPnt, Point } from "../geom"; +import { + Direction, + DIRECTIONS, + FaceOrEdge, + FilterFcn, + Finder, +} from "./definitions"; + +import { Vector, Point } from "../geom"; import { DEG2RAD } from "../constants"; import { AnyShape } from "../shapes"; -import { getOC } from "../oclib"; -import { GCWithObject, GCWithScope } from "../register"; +import { makeBox, makeVertex } from "../shapeHelpers"; +import { DistanceQuery } from "../measureShape"; export abstract class Finder3d extends Finder< Type, AnyShape > { + /** + * Filter to find elements following a custom function. + * + * @category Filter + */ + when(filter: (filter: FilterFcn) => boolean): this { + this.filters.push(filter); + return this; + } + /** * Filter to find elements that are in the list. * @@ -60,28 +76,14 @@ export abstract class Finder3d extends Finder< * @category Filter */ atDistance(distance: number, point: Point = [0, 0, 0]): this { - const pnt = asPnt(point); - - const oc = getOC(); - const vertexMaker = new oc.BRepBuilderAPI_MakeVertex(pnt); - const vertex = vertexMaker.Vertex(); - vertexMaker.delete(); - - const distanceBuilder = new oc.BRepExtrema_DistShapeShape_1(); - distanceBuilder.LoadS1(vertex); + const vertex = makeVertex(point); + const query = new DistanceQuery(vertex); const checkPoint = ({ element }: { element: Type }) => { - const r = GCWithScope(); - distanceBuilder.LoadS2(element.wrapped); - const progress = r(new oc.Message_ProgressRange_1()); - distanceBuilder.Perform(progress); - - return Math.abs(distanceBuilder.Value() - distance) < 1e-6; + return Math.abs(query.distanceTo(element) - distance) < 1e-6; }; this.filters.push(checkPoint); - GCWithObject(checkPoint)(distanceBuilder); - return this; } @@ -94,6 +96,23 @@ export abstract class Finder3d extends Finder< return this.atDistance(0, point); } + /** + * Filter to find elements that are within a certain distance from a point. + * + * @category Filter + */ + withinDistance(distance: number, point: Point = [0, 0, 0]): this { + const vertex = makeVertex(point); + const query = new DistanceQuery(vertex); + + const checkPoint = ({ element }: { element: Type }) => { + return query.distanceTo(element) - distance < 1e-6; + }; + + this.filters.push(checkPoint); + return this; + } + /** * Filter to find elements that are within a box * @@ -102,33 +121,25 @@ export abstract class Finder3d extends Finder< * @category Filter */ inBox(corner1: Point, corner2: Point) { - const oc = getOC(); - const boxMaker = new oc.BRepPrimAPI_MakeBox_4( - asPnt(corner1), - asPnt(corner2) - ); - const box = boxMaker.Solid(); - boxMaker.delete(); + const box = makeBox(corner1, corner2); + return this.inShape(box); + } - const distanceBuilder = new oc.BRepExtrema_DistShapeShape_1(); - distanceBuilder.LoadS1(box); + /** + * Filter to find elements that are within a generic shape + * + * The elements that are not fully contained in the shape are also found. + * + * @category Filter + */ + inShape(shape: AnyShape) { + const query = new DistanceQuery(shape); const checkPoint = ({ element }: { element: Type }) => { - const r = GCWithScope(); - distanceBuilder.LoadS2(element.wrapped); - const progress = r(new oc.Message_ProgressRange_1()); - distanceBuilder.Perform(progress); - - return distanceBuilder.Value() < 1e-6; + return query.distanceTo(element) < 1e-6; }; - // We cleanup the box and the distance builder when the function disappears - const gc = GCWithObject(checkPoint); - gc(box); - gc(distanceBuilder); - this.filters.push(checkPoint); - return this; } } diff --git a/packages/replicad/src/finders/index.ts b/packages/replicad/src/finders/index.ts index f70141b..bb06456 100644 --- a/packages/replicad/src/finders/index.ts +++ b/packages/replicad/src/finders/index.ts @@ -1,4 +1,6 @@ -import { Finder } from "./definitions"; +import { Finder, FilterFcn } from "./definitions"; + +export { FilterFcn as filterFcn }; /** * Combine a set of finder filters (defined with radius) to pass as a filter diff --git a/packages/replicad/src/measureShape.ts b/packages/replicad/src/measureShape.ts index 31c3cac..e26d46a 100644 --- a/packages/replicad/src/measureShape.ts +++ b/packages/replicad/src/measureShape.ts @@ -1,8 +1,12 @@ import { getOC } from "./oclib"; import { AnyShape, Face, Shape3D } from "./shapes"; -import type { GProp_GProps } from "replicad-opencascadejs"; +import type { + BRepExtrema_DistShapeShape, + GProp_GProps, +} from "replicad-opencascadejs"; import { GCWithScope, WrappingObj } from "./register"; +import { ProgressRange } from "./utils/ProgressRange"; class PhysicalProperties extends WrappingObj { get centerOfMass(): [number, number, number] { @@ -63,14 +67,71 @@ export function measureShapeVolumeProperties( return new VolumePhysicalProperties(properties); } +/** + * Measure the volume of a shape + * + * @category Measure + */ export function measureVolume(shape: Shape3D) { return measureShapeVolumeProperties(shape).volume; } +/** + * Measure the area of a shape + * + * @category Measure + */ export function measureArea(shape: Face | Shape3D) { return measureShapeSurfaceProperties(shape).area; } +/** + * Measure the length of a shape + * + * @category Measure + */ export function measureLength(shape: AnyShape) { return measureShapeLinearProperties(shape).length; } + +export class DistanceTool extends WrappingObj { + constructor() { + const oc = getOC(); + super(new oc.BRepExtrema_DistShapeShape_1()); + } + + distanceBetween(shape1: AnyShape, shape2: AnyShape): number { + this.wrapped.LoadS1(shape1.wrapped); + this.wrapped.LoadS2(shape2.wrapped); + const progress = new ProgressRange(); + this.wrapped.Perform(progress.wrapped); + return this.wrapped.Value(); + } +} + +/** + * Measure the distance between two shapes + * + * @category Measure + */ +export function measureDistanceBetween( + shape1: AnyShape, + shape2: AnyShape +): number { + return new DistanceTool().distanceBetween(shape1, shape2); +} + +export class DistanceQuery extends WrappingObj { + constructor(shape: AnyShape) { + const oc = getOC(); + super(new oc.BRepExtrema_DistShapeShape_1()); + this.wrapped.LoadS1(shape.wrapped); + } + + distanceTo(shape: AnyShape): number { + this.wrapped.LoadS2(shape.wrapped); + const progress = new ProgressRange(); + this.wrapped.Perform(progress.wrapped); + return this.wrapped.Value(); + } +} diff --git a/packages/replicad/src/shapeHelpers.ts b/packages/replicad/src/shapeHelpers.ts index cdea2ff..f62646e 100644 --- a/packages/replicad/src/shapeHelpers.ts +++ b/packages/replicad/src/shapeHelpers.ts @@ -391,6 +391,14 @@ export const makeSphere = (radius: number): Solid => { return sphere; }; +export const makeBox = (corner1: Point, corner2: Point): Solid => { + const oc = getOC(); + const boxMaker = new oc.BRepPrimAPI_MakeBox_4(asPnt(corner1), asPnt(corner2)); + const box = new Solid(boxMaker.Solid()); + boxMaker.delete(); + return box; +}; + export const makeVertex = (point: Point): Vertex => { const oc = getOC(); const pnt = asPnt(point); diff --git a/packages/replicad/src/utils/ProgressRange.ts b/packages/replicad/src/utils/ProgressRange.ts new file mode 100644 index 0000000..e25f07d --- /dev/null +++ b/packages/replicad/src/utils/ProgressRange.ts @@ -0,0 +1,10 @@ +import type { Message_ProgressRange } from "replicad-opencascadejs"; +import { WrappingObj } from "../register"; +import { getOC } from "../oclib"; + +export class ProgressRange extends WrappingObj { + constructor() { + const oc = getOC(); + super(new oc.Message_ProgressRange_1()); + } +}