From 952e2cd85ba9d31a9ebc2e09b7ef1becaf18f257 Mon Sep 17 00:00:00 2001 From: xiange Date: Sat, 16 Sep 2023 18:43:35 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20Add=20selection=20filters?= =?UTF-8?q?=20and=20multiple=20types=20of=20choices?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple shape types can be selected when selecting, and filter conditions can be added to the target shape at the same time --- packages/chili-core/src/geometry/geometry.ts | 5 + packages/chili-core/src/geometry/shape.ts | 2 + packages/chili-core/src/geometry/shapeType.ts | 45 +++++-- .../chili-core/src/visual/detectedData.ts | 2 +- packages/chili-core/src/visual/index.ts | 1 + packages/chili-core/src/visual/shapeFilter.ts | 7 ++ packages/chili-core/src/visual/view.ts | 12 +- packages/chili-core/src/visual/visualShape.ts | 4 +- packages/chili-occ/src/occHelps.ts | 14 ++- packages/chili-occ/src/occShape.ts | 10 +- packages/chili-occ/test/occ.test.ts | 22 ++-- packages/chili-three/src/threeShape.ts | 36 ++++-- packages/chili-three/src/threeView.ts | 113 ++++++++++++++---- packages/chili-three/test/testEdge.ts | 8 ++ .../chili-vis/src/selectionEventHandler.ts | 19 ++- .../src/shapeSelectionEventHandler.ts | 6 +- packages/chili/src/commands/create/revolve.ts | 16 ++- packages/chili/src/commands/create/sweep.ts | 4 +- packages/chili/src/selection.ts | 4 +- packages/chili/src/snap/objectSnap.ts | 4 +- .../snap/snapEventHandler/snapEventHandler.ts | 4 +- packages/chili/src/step/selectStep.ts | 4 +- 22 files changed, 261 insertions(+), 81 deletions(-) create mode 100644 packages/chili-core/src/visual/shapeFilter.ts diff --git a/packages/chili-core/src/geometry/geometry.ts b/packages/chili-core/src/geometry/geometry.ts index 8355171e..af5cc3e4 100644 --- a/packages/chili-core/src/geometry/geometry.ts +++ b/packages/chili-core/src/geometry/geometry.ts @@ -28,4 +28,9 @@ export namespace ICurve { let circle = curve as ICircle; return circle.center !== undefined && circle.radius !== undefined; } + + export function isLine(curve: ICurve): curve is ILine { + let line = curve as ILine; + return line.direction !== undefined; + } } diff --git a/packages/chili-core/src/geometry/shape.ts b/packages/chili-core/src/geometry/shape.ts index fa772e6b..8b6428c5 100644 --- a/packages/chili-core/src/geometry/shape.ts +++ b/packages/chili-core/src/geometry/shape.ts @@ -39,6 +39,8 @@ export interface IShape extends ISerialize { get mesh(): IShapeMeshData; setMatrix(matrix: Matrix4): void; isEqual(other: IShape): boolean; + findAncestor(ancestorType: ShapeType, fromShape: IShape): IShape[]; + findSubShapes(subshapeType: ShapeType, unique: boolean): IShape[]; } export interface IVertex extends IShape {} diff --git a/packages/chili-core/src/geometry/shapeType.ts b/packages/chili-core/src/geometry/shapeType.ts index 8196f7d2..de3154d5 100644 --- a/packages/chili-core/src/geometry/shapeType.ts +++ b/packages/chili-core/src/geometry/shapeType.ts @@ -1,13 +1,40 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. export enum ShapeType { - Compound, - CompoundSolid, - Solid, - Shell, - Face, - Wire, - Edge, - Vertex, - Shape, + Shape = 0b0, + Compound = 0b1, + CompoundSolid = 0b10, + Solid = 0b100, + Shell = 0b1000, + Face = 0b10000, + Wire = 0b100000, + Edge = 0b1000000, + Vertex = 0b10000000, +} + +export namespace ShapeType { + export function hasCompound(type: ShapeType): boolean { + return (type & ShapeType.Compound) !== 0; + } + export function hasCompoundSolid(type: ShapeType): boolean { + return (type & ShapeType.CompoundSolid) !== 0; + } + export function hasSolid(type: ShapeType): boolean { + return (type & ShapeType.Solid) !== 0; + } + export function hasShell(type: ShapeType): boolean { + return (type & ShapeType.Shell) !== 0; + } + export function hasFace(type: ShapeType): boolean { + return (type & ShapeType.Face) !== 0; + } + export function hasWire(type: ShapeType): boolean { + return (type & ShapeType.Wire) !== 0; + } + export function hasEdge(type: ShapeType): boolean { + return (type & ShapeType.Edge) !== 0; + } + export function hasVertex(type: ShapeType): boolean { + return (type & ShapeType.Vertex) !== 0; + } } diff --git a/packages/chili-core/src/visual/detectedData.ts b/packages/chili-core/src/visual/detectedData.ts index ab794022..4af4e3a5 100644 --- a/packages/chili-core/src/visual/detectedData.ts +++ b/packages/chili-core/src/visual/detectedData.ts @@ -6,5 +6,5 @@ import { IVisualShape } from "./visualShape"; export interface VisualShapeData { shape: IShape; owner: IVisualShape; - index?: number; + indexes: number[]; } diff --git a/packages/chili-core/src/visual/index.ts b/packages/chili-core/src/visual/index.ts index 6b64a5fa..ac412c7f 100644 --- a/packages/chili-core/src/visual/index.ts +++ b/packages/chili-core/src/visual/index.ts @@ -3,6 +3,7 @@ export * from "./cursorType"; export * from "./detectedData"; export * from "./eventHandler"; +export * from "./shapeFilter"; export * from "./view"; export * from "./viewer"; export * from "./visual"; diff --git a/packages/chili-core/src/visual/shapeFilter.ts b/packages/chili-core/src/visual/shapeFilter.ts new file mode 100644 index 00000000..64cc49e9 --- /dev/null +++ b/packages/chili-core/src/visual/shapeFilter.ts @@ -0,0 +1,7 @@ +// Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. + +import { IShape } from "../geometry"; + +export interface IShapeFilter { + allow(shape: IShape): boolean; +} diff --git a/packages/chili-core/src/visual/view.ts b/packages/chili-core/src/visual/view.ts index 699ffeba..406202b0 100644 --- a/packages/chili-core/src/visual/view.ts +++ b/packages/chili-core/src/visual/view.ts @@ -5,6 +5,7 @@ import { ShapeType } from "../geometry"; import { Plane, Ray, XY, XYZ } from "../math"; import { CursorType } from "./cursorType"; import { VisualShapeData } from "./detectedData"; +import { IShapeFilter } from "./shapeFilter"; import { IViewer } from "./viewer"; export interface IView extends IPropertyChanged { @@ -26,8 +27,15 @@ export interface IView extends IPropertyChanged { pan(dx: number, dy: number): void; rotation(dx: number, dy: number): void; zoom(x: number, y: number, delta: number): void; - detected(shapeType: ShapeType, x: number, y: number): VisualShapeData[]; - rectDetected(shapeType: ShapeType, x1: number, y1: number, x2: number, y2: number): VisualShapeData[]; + detected(shapeType: ShapeType, x: number, y: number, shapeFilter?: IShapeFilter): VisualShapeData[]; + rectDetected( + shapeType: ShapeType, + x1: number, + y1: number, + x2: number, + y2: number, + shapeFilter?: IShapeFilter, + ): VisualShapeData[]; } export namespace IView { diff --git a/packages/chili-core/src/visual/visualShape.ts b/packages/chili-core/src/visual/visualShape.ts index 9ff5f879..a1f12aa6 100644 --- a/packages/chili-core/src/visual/visualShape.ts +++ b/packages/chili-core/src/visual/visualShape.ts @@ -32,7 +32,7 @@ export interface VisualGroup { export interface IVisualShape extends IVisualObject { get shape(): IShape; - addState(state: VisualState, type: ShapeType, index?: number): void; - removeState(state: VisualState, type: ShapeType, index?: number): void; + addState(state: VisualState, type: ShapeType, ...indexes: number[]): void; + removeState(state: VisualState, type: ShapeType, ...indexes: number[]): void; resetState(): void; } diff --git a/packages/chili-occ/src/occHelps.ts b/packages/chili-occ/src/occHelps.ts index cf6bf5b5..26c5a621 100644 --- a/packages/chili-occ/src/occHelps.ts +++ b/packages/chili-occ/src/occHelps.ts @@ -141,8 +141,10 @@ export class OccHelps { return occ.TopAbs_ShapeEnum.TopAbs_EDGE as TopAbs_ShapeEnum; case ShapeType.Vertex: return occ.TopAbs_ShapeEnum.TopAbs_VERTEX as TopAbs_ShapeEnum; - default: + case ShapeType.Shape: return occ.TopAbs_ShapeEnum.TopAbs_SHAPE as TopAbs_ShapeEnum; + default: + throw new Error("Unknown shape type: " + shapeType); } } @@ -218,14 +220,14 @@ export class OccHelps { const hashes = unique ? new Map() : undefined; while (explorer.More()) { const item = explorer.Current(); - if (!unique) { - yield item; - } else { + if (unique) { const hash = OccHelps.hashCode(item); - if (!hashes?.has(hash)) { - hashes?.set(hash, true); + if (!hashes!.has(hash)) { + hashes!.set(hash, true); yield this.getActualShape(item); } + } else { + yield item; } explorer.Next(); } diff --git a/packages/chili-occ/src/occShape.ts b/packages/chili-occ/src/occShape.ts index 7b75c981..6d57d0a4 100644 --- a/packages/chili-occ/src/occShape.ts +++ b/packages/chili-occ/src/occShape.ts @@ -99,7 +99,15 @@ export class OccShape implements IShape { return tshape; } - findSubShapes(shapeType: ShapeType, unique: boolean = false): IShape[] { + findAncestor(ancestorType: ShapeType, fromShape: IShape): IShape[] { + if (!(fromShape instanceof OccShape)) { + throw new Error(`${fromShape} is not an OccShape`); + } + let occType = OccHelps.getShapeEnum(ancestorType); + return OccHelps.findAncestors(this.shape, fromShape.shape, occType).map((x) => OccHelps.getShape(x)); + } + + findSubShapes(shapeType: ShapeType, unique: boolean): IShape[] { let result = new Array(); let iter = OccHelps.findSubShapes(this.shape, OccHelps.getShapeEnum(shapeType), unique); for (const it of iter) { diff --git a/packages/chili-occ/test/occ.test.ts b/packages/chili-occ/test/occ.test.ts index 695f4cdf..2f045247 100644 --- a/packages/chili-occ/test/occ.test.ts +++ b/packages/chili-occ/test/occ.test.ts @@ -50,19 +50,19 @@ describe("shape test", () => { let end = new occ.gp_Pnt_3(10, 0, 0); let make = new occ.BRepBuilderAPI_MakeEdge_3(start, end); let edge = new OccEdge(make.Edge()); - expect(edge.findSubShapes(ShapeType.Edge).length).toBe(1); - expect(edge.findSubShapes(ShapeType.Vertex).length).toBe(2); - expect(edge.findSubShapes(ShapeType.Shape).length).toBe(0); + expect(edge.findSubShapes(ShapeType.Edge, false).length).toBe(1); + expect(edge.findSubShapes(ShapeType.Vertex, false).length).toBe(2); + expect(edge.findSubShapes(ShapeType.Shape, false).length).toBe(0); let make2 = new occ.BRepPrimAPI_MakeBox_2(10, 10, 10); let box = new OccSolid(make2.Solid()); - expect(box.findSubShapes(ShapeType.Edge).length).toBe(24); - expect(box.findSubShapes(ShapeType.Face).length).toBe(6); - expect(box.findSubShapes(ShapeType.Vertex).length).toBe(48); - expect(box.findSubShapes(ShapeType.Wire).length).toBe(6); - expect(box.findSubShapes(ShapeType.Shell).length).toBe(1); - expect(box.findSubShapes(ShapeType.Shell)[0].shapeType).toBe(ShapeType.Shell); - expect(box.findSubShapes(ShapeType.Shape).length).toBe(0); + expect(box.findSubShapes(ShapeType.Edge, false).length).toBe(24); + expect(box.findSubShapes(ShapeType.Face, false).length).toBe(6); + expect(box.findSubShapes(ShapeType.Vertex, false).length).toBe(48); + expect(box.findSubShapes(ShapeType.Wire, false).length).toBe(6); + expect(box.findSubShapes(ShapeType.Shell, false).length).toBe(1); + expect(box.findSubShapes(ShapeType.Shell, false)[0].shapeType).toBe(ShapeType.Shell); + expect(box.findSubShapes(ShapeType.Shape, false).length).toBe(0); let v1s: any[] = []; let iter = box.iterSubShapes(ShapeType.Vertex); @@ -82,7 +82,7 @@ describe("shape test", () => { test("test ancestors", () => { let make2 = new occ.BRepPrimAPI_MakeBox_2(10, 10, 10); let box = new OccSolid(make2.Solid()); - let edge = box.findSubShapes(ShapeType.Edge)[0] as any; + let edge = box.findSubShapes(ShapeType.Edge, false)[0] as any; let wire = OccHelps.findAncestors(edge.shape, box.shape, OccHelps.getShapeEnum(ShapeType.Wire)); expect(wire.length).toBe(2); expect(wire[0].ShapeType()).toBe(OccHelps.getShapeEnum(ShapeType.Wire)); diff --git a/packages/chili-three/src/threeShape.ts b/packages/chili-three/src/threeShape.ts index 62adab37..8628733a 100644 --- a/packages/chili-three/src/threeShape.ts +++ b/packages/chili-three/src/threeShape.ts @@ -132,17 +132,34 @@ export class ThreeShape extends Object3D implements IVisualShape { } } - addState(state: VisualState, type: ShapeType, index?: number) { - let newState = this.updateState("add", state, type, index); - this.setState(type, newState, index); + addState(state: VisualState, type: ShapeType, ...indexes: number[]) { + this.removeOrAddState("add", state, type, ...indexes); } - removeState(state: VisualState, type: ShapeType, index?: number) { - let newState = this.updateState("remove", state, type, index); - this.setState(type, newState, index); + removeState(state: VisualState, type: ShapeType, ...indexes: number[]) { + this.removeOrAddState("remove", state, type, ...indexes); } - private setState(type: ShapeType, newState: VisualState, index: number | undefined) { + private removeOrAddState( + action: "remove" | "add", + state: VisualState, + type: ShapeType, + ...indexes: number[] + ) { + const setState = (index?: number) => { + let newState = this.updateState(action, state, type, index); + this.setState(type, newState, index); + }; + if (indexes.length === 0) { + setState(); + } else { + indexes.forEach((index) => { + setState(index); + }); + } + } + + private setState(type: ShapeType, newState: VisualState, index?: number) { const setFaceState = () => { if (this._faces) this.setGroupsMaterial(this._faces.geometry, newState, index); }; @@ -153,9 +170,10 @@ export class ThreeShape extends Object3D implements IVisualShape { setFaceState(); setEdgeState(); } else if (index !== undefined) { - if (type === ShapeType.Face) { + if (ShapeType.hasEdge(type)) { setFaceState(); - } else if (type === ShapeType.Edge && this._edges) { + } + if (ShapeType.hasEdge(type) && this._edges) { setEdgeState(); } } diff --git a/packages/chili-three/src/threeView.ts b/packages/chili-three/src/threeView.ts index 52f2db74..7b1d2ee6 100644 --- a/packages/chili-three/src/threeView.ts +++ b/packages/chili-three/src/threeView.ts @@ -3,6 +3,8 @@ import { CursorType, IDisposable, + IShape, + IShapeFilter, IView, IViewer, Observable, @@ -334,63 +336,115 @@ export class ThreeView extends Observable implements IView, IDisposable { detecteds.push({ owner: shape.parent, shape: shape.parent.shape, + indexes: [], }); } } return detecteds; } - detected(shapeType: ShapeType, mx: number, my: number): VisualShapeData[] { + detected(shapeType: ShapeType, mx: number, my: number, shapeFilter?: IShapeFilter): VisualShapeData[] { let intersections = this.findIntersections(shapeType, mx, my); return shapeType === ShapeType.Shape - ? this.detectThreeShapes(intersections) - : this.detectSubShapes(shapeType, intersections); + ? this.detectThreeShapes(intersections, shapeFilter) + : this.detectSubShapes(shapeType, intersections, shapeFilter); } - private detectThreeShapes(intersections: Intersection[]) { + private detectThreeShapes(intersections: Intersection[], shapeFilter?: IShapeFilter) { let result: VisualShapeData[] = []; for (const element of intersections) { const parent = element.object.parent; if (parent instanceof ThreeShape) { + if (shapeFilter && !shapeFilter.allow(parent.shape)) { + continue; + } result.push({ owner: parent, shape: parent.shape, + indexes: [], }); } } return result; } - private detectSubShapes(shapeType: ShapeType, intersections: Intersection[]) { + private detectSubShapes( + shapeType: ShapeType, + intersections: Intersection[], + shapeFilter?: IShapeFilter, + ) { let result: VisualShapeData[] = []; - for (const element of intersections) { - const parent = element.object.parent; - if (!(parent instanceof ThreeShape)) continue; - let { groupIndex, groups } = this.getGroupInfo(shapeType, parent, element); - if (groupIndex !== undefined && groups) { + for (const intersected of intersections) { + const visualShape = intersected.object.parent; + if (!(visualShape instanceof ThreeShape)) continue; + let { shape, indexes } = this.getShape(shapeType, visualShape, intersected); + if (shape) { + if (shapeFilter && !shapeFilter.allow(shape)) { + continue; + } result.push({ - owner: parent, - shape: groups[groupIndex].shape, - index: groupIndex, + owner: visualShape, + shape: shape, + indexes, }); } } return result; } - private getGroupInfo(shapeType: ShapeType, parent: ThreeShape, element: Intersection) { + private getShape( + shapeType: ShapeType, + parent: ThreeShape, + element: Intersection, + ): { + shape: IShape | undefined; + indexes: number[]; + } { + let { shape, index, groups } = this.findShapeAndIndex(parent, element); + if (!shape) return { shape: undefined, indexes: [] }; + if (ShapeType.hasWire(shapeType)) { + let wire = this.getWireAndIndexes(shape, groups!, parent); + if (wire.shape) return wire; + } + // TODO: other type + + return { shape, indexes: [index!] }; + } + + private getWireAndIndexes(shape: IShape, groups: ShapeMeshGroup[], parent: ThreeShape) { + let wire = shape.findAncestor(ShapeType.Wire, parent.shape).at(0); + let indexes: number[] = []; + if (wire) { + let edges = wire.findSubShapes(ShapeType.Edge, true); + for (const edge of edges) { + for (let i = 0; i < groups!.length; i++) { + if (edge.isEqual(groups![i].shape)) { + indexes.push(i); + } + } + } + } + return { shape: wire, indexes }; + } + + private findShapeAndIndex(parent: ThreeShape, element: Intersection) { + let shape: IShape | undefined = undefined; + let index: number | undefined = undefined; let groups: ShapeMeshGroup[] | undefined = undefined; - let groupIndex: number | undefined = undefined; - if (shapeType === ShapeType.Face) { + if (element.faceIndex !== null) { groups = parent.shape.mesh.faces?.groups; - if (groups && element.faceIndex !== undefined) - groupIndex = ThreeHelper.findGroupIndex(groups, element.faceIndex * 3)!; - } else { + if (groups) { + index = ThreeHelper.findGroupIndex(groups, element.faceIndex! * 3)!; + shape = groups[index].shape; + } + } else if (element.index !== null) { groups = parent.shape.mesh.edges?.groups; - if (groups && element.index !== undefined) - groupIndex = ThreeHelper.findGroupIndex(groups, element.index); + if (groups) { + index = ThreeHelper.findGroupIndex(groups, element.index!)!; + shape = groups[index].shape; + } } - return { groupIndex, groups }; + return { shape, index, groups }; } private findIntersections(shapeType: ShapeType, mx: number, my: number) { @@ -406,12 +460,23 @@ export class ThreeView extends Observable implements IView, IDisposable { }; this.viewer.visual.context.shapes().forEach((x) => { if (!(x instanceof ThreeShape)) return; - if (shapeType === ShapeType.Face || shapeType === ShapeType.Shape) { + if ( + shapeType === ShapeType.Shape || + ShapeType.hasCompound(shapeType) || + ShapeType.hasCompoundSolid(shapeType) || + ShapeType.hasSolid(shapeType) + ) { + addObject(x.faces()); + addObject(x.edges()); + return; + } + if (ShapeType.hasFace(shapeType) || ShapeType.hasShell(shapeType)) { addObject(x.faces()); } - if (shapeType !== ShapeType.Face) { + if (ShapeType.hasEdge(shapeType) || ShapeType.hasWire(shapeType)) { addObject(x.edges()); } + // TODO: vertex }); return shapes; } diff --git a/packages/chili-three/test/testEdge.ts b/packages/chili-three/test/testEdge.ts index 524ce9fb..9be788fb 100644 --- a/packages/chili-three/test/testEdge.ts +++ b/packages/chili-three/test/testEdge.ts @@ -22,6 +22,14 @@ export class TestEdge implements IEdge { readonly end: XYZ, ) {} + findAncestor(ancestorType: ShapeType, fromShape: IShape): IShape[] { + throw new Error("Method not implemented."); + } + + findSubShapes(subshapeType: ShapeType, unique: boolean): IShape[] { + throw new Error("Method not implemented."); + } + intersect(other: IEdge | Ray): XYZ[] { return []; } diff --git a/packages/chili-vis/src/selectionEventHandler.ts b/packages/chili-vis/src/selectionEventHandler.ts index 15293756..a0e478dd 100644 --- a/packages/chili-vis/src/selectionEventHandler.ts +++ b/packages/chili-vis/src/selectionEventHandler.ts @@ -5,6 +5,7 @@ import { IDocument, IEventHandler, IShape, + IShapeFilter, IView, ShapeType, VisualShapeData, @@ -41,6 +42,7 @@ export abstract class SelectionHandler implements IEventHandler { readonly shapeType: ShapeType, readonly multiMode: boolean, readonly controller?: AsyncController, + readonly filter?: IShapeFilter, ) { controller?.onCancelled((s) => { this.clearSelected(document); @@ -67,10 +69,17 @@ export abstract class SelectionHandler implements IEventHandler { let detecteds: VisualShapeData[] = []; if (this.rect) { detecteds = detecteds.concat( - view.rectDetected(this.shapeType, this.mouse.x, this.mouse.y, event.offsetX, event.offsetY), + view.rectDetected( + this.shapeType, + this.mouse.x, + this.mouse.y, + event.offsetX, + event.offsetY, + this.filter, + ), ); } else { - this._detectAtMouse = view.detected(this.shapeType, event.offsetX, event.offsetY); + this._detectAtMouse = view.detected(this.shapeType, event.offsetX, event.offsetY, this.filter); let detected = this.getDetecting(); if (detected) detecteds.push(detected); } @@ -91,10 +100,12 @@ export abstract class SelectionHandler implements IEventHandler { private setHighlight(view: IView, detecteds: VisualShapeData[]) { this._selected?.forEach((x) => { - if (!detecteds.includes(x)) x.owner.removeState(VisualState.hilight, this.shapeType, x.index); + if (!detecteds.includes(x)) + x.owner.removeState(VisualState.hilight, this.shapeType, ...x.indexes); }); detecteds.forEach((x) => { - if (!this._selected?.includes(x)) x.owner.addState(VisualState.hilight, this.shapeType, x.index); + if (!this._selected?.includes(x)) + x.owner.addState(VisualState.hilight, this.shapeType, ...x.indexes); }); this._selected = detecteds; view.viewer.redraw(); diff --git a/packages/chili-vis/src/shapeSelectionEventHandler.ts b/packages/chili-vis/src/shapeSelectionEventHandler.ts index 007701a3..5430f117 100644 --- a/packages/chili-vis/src/shapeSelectionEventHandler.ts +++ b/packages/chili-vis/src/shapeSelectionEventHandler.ts @@ -17,7 +17,7 @@ export class ShapeSelectionHandler extends SelectionHandler { override clearSelected(document: IDocument): void { for (const shape of this._shapes.values()) { - shape.owner.removeState(VisualState.selected, shape.shape.shapeType, shape.index); + shape.owner.removeState(VisualState.selected, shape.shape.shapeType, ...shape.indexes); } this._shapes.clear(); } @@ -42,11 +42,11 @@ export class ShapeSelectionHandler extends SelectionHandler { private removeSelected(shape: VisualShapeData) { this._shapes.delete(shape); - shape.owner.removeState(VisualState.selected, shape.shape.shapeType, shape.index); + shape.owner.removeState(VisualState.selected, shape.shape.shapeType, ...shape.indexes); } private addSelected(shape: VisualShapeData) { - shape.owner.addState(VisualState.selected, shape.shape.shapeType, shape.index); + shape.owner.addState(VisualState.selected, shape.shape.shapeType, ...shape.indexes); this._shapes.add(shape); } } diff --git a/packages/chili/src/commands/create/revolve.ts b/packages/chili/src/commands/create/revolve.ts index 688fe610..8ac008e3 100644 --- a/packages/chili/src/commands/create/revolve.ts +++ b/packages/chili/src/commands/create/revolve.ts @@ -1,6 +1,6 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { GeometryModel, IEdge, ILine, ShapeType, command } from "chili-core"; +import { GeometryModel, ICurve, IEdge, ILine, IShape, IShapeFilter, ShapeType, command } from "chili-core"; import { RevolveBody } from "../../bodys"; import { IStep } from "../../step"; import { SelectStep } from "../../step/selectStep"; @@ -33,7 +33,19 @@ export class Revolve extends CreateCommand { protected override getSteps(): IStep[] { return [ new SelectStep(ShapeType.Shape, "prompt.select.shape", false), - new SelectStep(ShapeType.Edge, "prompt.select.edges", false), + new SelectStep(ShapeType.Edge, "prompt.select.edges", false, new LineFilter()), ]; } } + +class LineFilter implements IShapeFilter { + allow(shape: IShape): boolean { + if (shape.shapeType === ShapeType.Edge) { + let edge = shape as IEdge; + let curve = edge.asCurve().getValue(); + if (curve === undefined) return false; + return ICurve.isLine(curve); + } + return false; + } +} diff --git a/packages/chili/src/commands/create/sweep.ts b/packages/chili/src/commands/create/sweep.ts index dcb3d0a8..ab7d4b2c 100644 --- a/packages/chili/src/commands/create/sweep.ts +++ b/packages/chili/src/commands/create/sweep.ts @@ -24,8 +24,8 @@ export class Sweep extends CreateCommand { protected override getSteps(): IStep[] { return [ - new SelectStep(ShapeType.Shape, "prompt.select.shape", false), - new SelectStep(ShapeType.Edge, "prompt.select.edges", false), + new SelectStep(ShapeType.Edge | ShapeType.Wire | ShapeType.Face, "prompt.select.shape", false), + new SelectStep(ShapeType.Edge | ShapeType.Wire, "prompt.select.edges", false), ]; } } diff --git a/packages/chili/src/selection.ts b/packages/chili/src/selection.ts index 74be53e6..0955fbf5 100644 --- a/packages/chili/src/selection.ts +++ b/packages/chili/src/selection.ts @@ -8,6 +8,7 @@ import { IEventHandler, IModel, INode, + IShapeFilter, Logger, PubSub, ShapeType, @@ -21,8 +22,9 @@ export class Selection { prompt: I18nKeys, controller: AsyncController, multiMode: boolean = true, + filter?: IShapeFilter, ) { - let handler = new ShapeSelectionHandler(document, shapeType, multiMode, controller); + let handler = new ShapeSelectionHandler(document, shapeType, multiMode, controller, filter); await this.pickAsync(document, handler, prompt, controller, multiMode === true); let shapes = handler.shapes(); handler.dispose(); diff --git a/packages/chili/src/snap/objectSnap.ts b/packages/chili/src/snap/objectSnap.ts index c755b98e..5e6388a4 100644 --- a/packages/chili/src/snap/objectSnap.ts +++ b/packages/chili/src/snap/objectSnap.ts @@ -177,13 +177,13 @@ export class ObjectSnap implements ISnapper { } private hilighted(view: IView, shapes: VisualShapeData[]) { - shapes.forEach((x) => x.owner.addState(VisualState.hilight, x.shape.shapeType, x.index)); + shapes.forEach((x) => x.owner.addState(VisualState.hilight, x.shape.shapeType, ...x.indexes)); this._hilightedShapes.push(...shapes); } private unHilighted() { this._hilightedShapes.forEach((x) => { - x.owner.removeState(VisualState.hilight, x.shape.shapeType, x.index); + x.owner.removeState(VisualState.hilight, x.shape.shapeType, ...x.indexes); }); this._hilightedShapes.length = 0; } diff --git a/packages/chili/src/snap/snapEventHandler/snapEventHandler.ts b/packages/chili/src/snap/snapEventHandler/snapEventHandler.ts index 19f1cb80..9e4a53af 100644 --- a/packages/chili/src/snap/snapEventHandler/snapEventHandler.ts +++ b/packages/chili/src/snap/snapEventHandler/snapEventHandler.ts @@ -5,6 +5,7 @@ import { Config, I18nKeys, IEventHandler, + IShapeFilter, IView, MessageType, PubSub, @@ -27,6 +28,7 @@ export abstract class SnapEventHandler implements IEventHandler { readonly controller: AsyncController, readonly snaps: ISnapper[], readonly data: SnapPointData, + readonly filter?: IShapeFilter, ) { if (data.validators) { this.validators.push(...data.validators); @@ -135,7 +137,7 @@ export abstract class SnapEventHandler implements IEventHandler { } private findDetecteds(shapeType: ShapeType, view: IView, event: MouseEvent): MouseAndDetected { - let shapes = view.detected(shapeType, event.offsetX, event.offsetY); + let shapes = view.detected(shapeType, event.offsetX, event.offsetY, this.filter); return { shapes, view, diff --git a/packages/chili/src/step/selectStep.ts b/packages/chili/src/step/selectStep.ts index 6c4cb881..29c211df 100644 --- a/packages/chili/src/step/selectStep.ts +++ b/packages/chili/src/step/selectStep.ts @@ -1,6 +1,6 @@ // Copyright 2022-2023 the Chili authors. All rights reserved. AGPL-3.0 license. -import { AsyncController, I18nKeys, IDocument, ShapeType } from "chili-core"; +import { AsyncController, I18nKeys, IDocument, IShapeFilter, ShapeType } from "chili-core"; import { Selection } from "../selection"; import { SnapedData } from "../snap"; import { IStep } from "./step"; @@ -10,6 +10,7 @@ export class SelectStep implements IStep { readonly snapeType: ShapeType, readonly prompt: I18nKeys, readonly multiple: boolean = false, + readonly filter?: IShapeFilter, ) {} async execute(document: IDocument, controller: AsyncController): Promise { @@ -19,6 +20,7 @@ export class SelectStep implements IStep { this.prompt, controller, this.multiple, + this.filter, ); return { view: document.visual.viewer.activeView!,