From 7df876df50b257feffac150bbd80841be94f921d Mon Sep 17 00:00:00 2001 From: Val Erastov Date: Fri, 10 Jun 2022 22:30:10 -0700 Subject: [PATCH] fix extrude from sketch tests --- modules/brep/topo/shell.ts | 12 +++ .../features/extrude/extrude.operation.ts | 69 ++++------------ .../testCases/craftExtrudeBasicShapes.js | 33 ++++---- test/coreTests/utils/asserts.js | 2 +- test/coreTests/utils/scripts.js | 4 +- web/app/cad/craft/e0/OCCUtils.ts | 39 +++++++--- .../craft/production/productionAnalyzer.ts | 78 +++++++++++++++++-- web/app/cad/model/mface.ts | 8 -- web/app/cad/model/mobject.ts | 4 + .../cad/scene/controls/pickControlPlugin.ts | 1 + 10 files changed, 152 insertions(+), 98 deletions(-) diff --git a/modules/brep/topo/shell.ts b/modules/brep/topo/shell.ts index c5726a6c..4e799ab0 100644 --- a/modules/brep/topo/shell.ts +++ b/modules/brep/topo/shell.ts @@ -107,6 +107,18 @@ export class Shell extends TopoObject { }); } } + + traverse(callback: (child: TopoObject) => any) { + for (let face of this.faces) { + callback(face); + } + for (let edge of this.edges) { + callback(edge); + } + for (let vertex of this.vertices) { + callback(vertex); + } + } } export function* verticesGenerator(shell: Shell): Generator { diff --git a/modules/workbenches/modeler/features/extrude/extrude.operation.ts b/modules/workbenches/modeler/features/extrude/extrude.operation.ts index 9fec1dc7..ac5f502a 100644 --- a/modules/workbenches/modeler/features/extrude/extrude.operation.ts +++ b/modules/workbenches/modeler/features/extrude/extrude.operation.ts @@ -11,8 +11,8 @@ import {FaceRef} from "cad/craft/e0/OCCUtils"; import {GetRef} from "cad/craft/e0/interact"; import { FromMObjectProductionAnalyzer, - FromSketchProductionAnalyzer, - ProductionAnalyzer + FromSketchProductionAnalyzer, NULL_ANALYZER, + ProductionAnalyzer, PushPullFaceProductionAnalyzer } from "cad/craft/production/productionAnalyzer"; @@ -53,76 +53,37 @@ export const ExtrudeOperation: OperationDescriptor = { if (!sketch) { if (face instanceof MBrepFace) { - occ.io.pushModel(face, face.id) - const edges = face.edges; - edges.forEach(e => occ.io.pushModel(e, e.id)); - sweepSources = [{ - face: face.id, - edges: edges.map(e => e.id) - }]; + oci.prism("FaceTool", face, ...extrusionVector.data()); + return occ.utils.applyBooleanModifier([occ.io.getShell("FaceTool")], params.boolean, face, [], + (targets, tools) => new PushPullFaceProductionAnalyzer(targets, face.brepFace)); } else { throw "can't extrude an empty surface"; } - } else { - let csys = face.csys; - if (params.doubleSided) { - csys = csys.clone(); - csys.origin._minus(extrusionVector); - extrusionVector._scale(2); - } - sweepSources = occ.utils.sketchToFaces(sketch, csys) } + let csys = face.csys; + if (params.doubleSided) { + csys = csys.clone(); + csys.origin._minus(extrusionVector); + extrusionVector._scale(2); + } + sweepSources = occ.utils.sketchToFaces(sketch, csys) + const productionAnalyzer = new FromSketchProductionAnalyzer(sweepSources); const tools = sweepSources.map((faceRef, i) => { const faceName = faceRef.face; const shapeName = "Tool/" + i; - - // for (let i = 0; i < faceRef.edges.length; ++i) { - // const edge = faceRef.edges[i]; - // const seg = faceRef.contour.segments[i]; - // const ref = GetRef(edge); - // productionAnalyzer.mapRef(ref, `SK[${sketchId}:${seg.id}]`); - // } - oci.prism(shapeName, faceName, ...extrusionVector.data()); - - - // occIterateFaces(oc, shape, face => { - // let role; - // if (face.IsSame(prismAPI.FirstShape())) { - // role = "bottom"; - // } else if (face.IsSame(prismAPI.LastShape())) { - // role = "top"; - // } else { - // role = "sweep"; - // } - // getProductionInfo(face).role = role; - // }); - // - // occIterateEdges(oc, wire, edge => { - // const generatedList = prismAPI.Generated(edge); - // occIterateListOfShape(oc, generatedList, face => { - // console.log(face); - // }) - // }) - return shapeName; - }); + }).map(shapeName => occ.io.getShell(shapeName, productionAnalyzer)); - // const productionAnalyzer = new ProductionAnalyzer(); - // ctx.cadRegistry.models.forEach(m => productionAnalyzer.preRegister(m)); - return occ.utils.applyBooleanModifier(tools, params.boolean, productionAnalyzer, [face]); + return occ.utils.applyBooleanModifier(tools, params.boolean, face, [face]); }, - // useBoolean: { - // booleanField: 'boolean', - // impliedTargetField: 'face' - // }, form: [ { diff --git a/test/coreTests/testCases/craftExtrudeBasicShapes.js b/test/coreTests/testCases/craftExtrudeBasicShapes.js index a9ca7054..b97185ab 100644 --- a/test/coreTests/testCases/craftExtrudeBasicShapes.js +++ b/test/coreTests/testCases/craftExtrudeBasicShapes.js @@ -4,8 +4,8 @@ import {createPlaneAndOpenSketcher} from '../utils/scripts'; export const TEST_MODE = 'modellerUI'; export async function testExtrudeFromSketch(env, ui) { - let sketcherUI = await createPlaneAndOpenSketcher(ui); - let sketchedFace = ui.context.services.selection.face.single; + let [sketcherUI, sketchedFace] = await createPlaneAndOpenSketcher(ui); + let seg1 = sketcherUI.addSegment(-100, -100, 100, -100); let seg2 = sketcherUI.addSegment(100, -100, 100, 100); let seg3 = sketcherUI.addSegment(100, 100, -100, 100); @@ -17,7 +17,7 @@ export async function testExtrudeFromSketch(env, ui) { ui.openWizard('EXTRUDE'); - ui.wizardContext.updateParam('value', 200); + ui.wizardContext.updateParam('length', 200); await ui.wizardOK(); let [leftFace] = ui.rayCastFaces([-110, 0, 100], [-90, 0, 100]); @@ -34,8 +34,8 @@ export async function testExtrudeFromSketch(env, ui) { assertFaceRole(rightFace, "sweep"); assertFaceRole(topFace, "sweep"); assertFaceRole(bottomFace, "sweep"); - assertFaceRole(frontFace, "top"); - assertFaceRole(backFace, "bottom"); + assertFaceRole(frontFace, "lid"); + assertFaceRole(backFace, "base"); let sketchId = sketchedFace.defaultSketchId; @@ -76,8 +76,8 @@ export async function testExtrudeArc(env, ui) { assertFaceRole(curvedFace, "sweep"); assertFaceRole(flatFace, "sweep"); - assertFaceRole(topFace, "top"); - assertFaceRole(bottomFace, "bottom"); + assertFaceRole(topFace, "lid"); + assertFaceRole(bottomFace, "base"); let sketchId = sketchedFace.defaultSketchId; @@ -107,8 +107,8 @@ export async function testExtrudeCircle(env, ui) { assertFaceRole(curvedFace, "sweep"); - assertFaceRole(topFace, "top"); - assertFaceRole(bottomFace, "bottom"); + assertFaceRole(topFace, "lid"); + assertFaceRole(bottomFace, "base"); let sketchId = sketchedFace.defaultSketchId; @@ -137,8 +137,8 @@ export async function testExtrudeEllipse(env, ui) { assertFaceRole(curvedFace, "sweep"); - assertFaceRole(topFace, "top"); - assertFaceRole(bottomFace, "bottom"); + assertFaceRole(topFace, "lid"); + assertFaceRole(bottomFace, "base"); let sketchId = sketchedFace.defaultSketchId; @@ -148,8 +148,7 @@ export async function testExtrudeEllipse(env, ui) { } export async function testExtrudeEllipticalArc(env, ui) { - let sketcherUI = await createPlaneAndOpenSketcher(ui); - let sketchedFace = ui.context.services.selection.face.single; + let [sketcherUI, sketchedFace] = await createPlaneAndOpenSketcher(ui); let eArc = sketcherUI.addEllipticalArc(-100, 100, 100, 100, 0, 150); sketcherUI.move(100, 100, -50, 170); sketcherUI.addSegment(eArc.a.x, eArc.a.y, eArc.b.x, eArc.b.y); @@ -169,8 +168,8 @@ export async function testExtrudeEllipticalArc(env, ui) { assertFaceRole(curvedFace, "sweep"); - assertFaceRole(topFace, "top"); - assertFaceRole(bottomFace, "bottom"); + assertFaceRole(topFace, "lid"); + assertFaceRole(bottomFace, "base"); let sketchId = sketchedFace.defaultSketchId; @@ -203,8 +202,8 @@ export async function testExtrudeBezier(env, ui) { assertFaceRole(curvedFace, "sweep"); - assertFaceRole(topFace, "top"); - assertFaceRole(bottomFace, "bottom"); + assertFaceRole(topFace, "lid"); + assertFaceRole(bottomFace, "base"); let sketchId = sketchedFace.defaultSketchId; diff --git a/test/coreTests/utils/asserts.js b/test/coreTests/utils/asserts.js index fa20d767..750124a6 100644 --- a/test/coreTests/utils/asserts.js +++ b/test/coreTests/utils/asserts.js @@ -57,7 +57,7 @@ export function assertFaceIsPlane(face) { export function assertFaceOrigination(face, sketchId, primitiveId) { assertEquals(sketchObjectGlobalId(sketchId, primitiveId), - face.productionInfo.originatedFromPrimitive); + face.productionInfo.originatingPrimitive); } export function assertFaceRole(face, expectedRole) { diff --git a/test/coreTests/utils/scripts.js b/test/coreTests/utils/scripts.js index 1d487d45..fb6fa388 100644 --- a/test/coreTests/utils/scripts.js +++ b/test/coreTests/utils/scripts.js @@ -3,7 +3,9 @@ export async function createPlaneAndOpenSketcher(ui) { ui.openWizard('PLANE'); await ui.wizardOK(); ui.selectFaces([0, 0, -10], [0, 0, 10]); - return ui.openSketcher(); + let sketchedFace = ui.context.services.selection.face.single; + let sketcher = ui.openSketcher(); + return [sketcher, sketchedFace]; } export async function extrudeCube(ui) { diff --git a/web/app/cad/craft/e0/OCCUtils.ts b/web/app/cad/craft/e0/OCCUtils.ts index 84723a38..2af6d4b0 100644 --- a/web/app/cad/craft/e0/OCCUtils.ts +++ b/web/app/cad/craft/e0/OCCUtils.ts @@ -7,6 +7,7 @@ import {WireRef} from "cad/craft/e0/occSketchLoader"; import {FromMObjectProductionAnalyzer, ProductionAnalyzer} from "cad/craft/production/productionAnalyzer"; import {MObject} from "cad/model/mobject"; import {Shell} from "brep/topo/shell"; +import {MOpenFaceShell} from "cad/model/mopenFace"; export interface OCCUtils { @@ -14,10 +15,11 @@ export interface OCCUtils { sketchToFaces(sketch: SketchGeom, csys: CSys): FaceRef[]; - // applyBoolean(tools: string[], kind: BooleanKind): string[]; - - applyBooleanModifier(tools: string[], booleanDef?: BooleanDefinition, productionAnalyzer?: ProductionAnalyzer, - mustAdvance? : MObject[]): OperationResult; + applyBooleanModifier(tools: MObject[], + booleanDef: BooleanDefinition, + sketchSource: MObject, + mustAdvance? : MObject[], + analyzerCreator?: (targets: MObject[], tools: MObject[]) => ProductionAnalyzer): OperationResult; } export interface FaceRef extends WireRef { @@ -48,20 +50,29 @@ export function createOCCUtils(ctx: CoreContext): OCCUtils { }); } - function applyBooleanModifier(tools: string[], booleanDef?: BooleanDefinition, - productionAnalyzer?: ProductionAnalyzer, mustAdvance? : MObject[]): OperationResult { + + function applyBooleanModifier(tools: MObject[], + booleanDef: BooleanDefinition, + sketchSource: MObject, + mustAdvance? : MObject[], + analyzerCreator?: (targets: MObject[], tools: MObject[]) => ProductionAnalyzer): OperationResult { const occ = ctx.occService; const oci = ctx.occService.commandInterface; + const consumed = []; + + if (sketchSource.parent instanceof MOpenFaceShell) { + consumed.push(sketchSource.parent); + } + if (!booleanDef || booleanDef.kind === 'NONE') { return { - created: tools.map(shapeName => occ.io.getShell(shapeName, productionAnalyzer)), - consumed: [] + created: tools, + consumed } } else { - const toolsMobj = tools.map(shapeName => occ.io.getShell(shapeName, productionAnalyzer)); const kind = booleanDef.kind; @@ -87,15 +98,19 @@ export function createOCCUtils(ctx: CoreContext): OCCUtils { oci.bcleartools(); targetNames.forEach(targetName => oci.baddobjects(targetName)); - tools.forEach(toolName => oci.baddtools(toolName)); + tools.forEach(tool => oci.baddtools(tool)); oci.bcheckinverted(1); oci.bfillds(); oci.bapibop("BooleanResult", booleanKindToOCCBopType(kind)); - const booleanProdAnalyzer = new FromMObjectProductionAnalyzer([...toolsMobj, ...targets], mustAdvance); + targets.forEach(t => consumed.push(t)); + tools.forEach(t => consumed.push(t)); + + const booleanProdAnalyzer = analyzerCreator ? analyzerCreator(targets, tools) + : new FromMObjectProductionAnalyzer([...targets, ...tools], mustAdvance); return { - consumed: targets, + consumed, created: [occ.io.getShell("BooleanResult", booleanProdAnalyzer)] } } diff --git a/web/app/cad/craft/production/productionAnalyzer.ts b/web/app/cad/craft/production/productionAnalyzer.ts index 15f90b63..915b1311 100644 --- a/web/app/cad/craft/production/productionAnalyzer.ts +++ b/web/app/cad/craft/production/productionAnalyzer.ts @@ -88,18 +88,41 @@ export class NullProductionAnalyzer implements ProductionAnalyzer { export const NULL_ANALYZER = new NullProductionAnalyzer(); -export class FromSketchProductionAnalyzer implements ProductionAnalyzer { +abstract class BasicProductionAnalyzer implements ProductionAnalyzer { + + abstract assignIdentificationImpl(createdShell: Shell); + + assignIdentification(createdShell: Shell) { + + classifier.prepare(createdShell); + + this.assignIdentificationImpl(createdShell); + + createdShell.traverse(obj => { + if (!obj.data.productionInfo) { + obj.data.productionInfo = { + unstable: true + } + } + }); + + } + +} + +export class FromSketchProductionAnalyzer extends BasicProductionAnalyzer { profiles: FaceRef[]; constructor(profiles: FaceRef[]) { + super(); this.profiles = profiles; for (let originFace of this.profiles) { classifier.prepare(originFace.topoShape); } } - assignIdentification(createdShell: Shell) { + assignIdentificationImpl(createdShell: Shell) { classifier.prepare(createdShell); @@ -253,12 +276,13 @@ function forceAdvance(idToAssign: string): string { } } -export class FromMObjectProductionAnalyzer implements ProductionAnalyzer { +export class FromMObjectProductionAnalyzer extends BasicProductionAnalyzer { consumed: MObject[] = []; mustAdvance: Set; constructor(consumed: MObject[], mustAdvance: MObject[] = []) { + super(); this.consumed = consumed; this.mustAdvance = new Set(mustAdvance&&mustAdvance.map(o => o.id)); consumed.forEach(mShell => { @@ -269,7 +293,7 @@ export class FromMObjectProductionAnalyzer implements ProductionAnalyzer { } - assignIdentification(createdShell: Shell) { + assignIdentificationImpl(createdShell: Shell) { classifier.prepare(createdShell); @@ -393,6 +417,10 @@ export class FromMObjectProductionAnalyzer implements ProductionAnalyzer { } }); }); + if (edgeCreators.length < 2) { + //dangled edge - random id will be assigned + return; + } const edgeCreatorIds = edgeCreators.map(f => f.id).sort(); const id = '[' + edgeCreatorIds.join('|') + ']'; addToListInMap(newEdges, id, edge); @@ -416,4 +444,44 @@ export class FromMObjectProductionAnalyzer implements ProductionAnalyzer { } -} \ No newline at end of file +} + +export class PushPullFaceProductionAnalyzer extends FromMObjectProductionAnalyzer { + + baseFace: Face; + + constructor(consumed: MObject[], baseFace: Face) { + super(consumed, []); + this.baseFace = baseFace; + } + + assignIdentificationImpl(createdShell: Shell) { + super.assignIdentificationImpl(createdShell); + + const edgeMap = new Map(); + for (let he of this.baseFace.edges) { + debugger + const twin = he.twin(); + if (twin) { + edgeMap.set(twin.loop.face.data.id, twin.edge); + } + } + + for (let face of createdShell.faces) { + if (!face.data.productionInfo) { + face.data.id = this.baseFace.id; + face.data.productionInfo = this.baseFace.data.productionInfo; + for (let he of face.edges) { + const twin = he.twin(); + if (twin) { + const originEdge = edgeMap.get(twin.loop.face.data.id); + he.edge.data.id = originEdge.data.id; + he.edge.data.productionInfo = originEdge.data.productionInfo; + } + } + } + } + + } + +} diff --git a/web/app/cad/model/mface.ts b/web/app/cad/model/mface.ts index f42e0a99..4a97bad3 100644 --- a/web/app/cad/model/mface.ts +++ b/web/app/cad/model/mface.ts @@ -158,14 +158,6 @@ export class MFace extends MObject { return this._worldToSketchTransformation; } - get productionInfo() { - if (this._productionInfo === undefined) { - this._productionInfo = !this.brepFace?.data?.productionInfo ? null : - ProductionInfo.fromRawData(this.brepFace.data.productionInfo); - } - return this._productionInfo; - } - traverse(callback: (obj: MObject) => void) { callback(this); this.traverseSketchRelatedEntities(callback); diff --git a/web/app/cad/model/mobject.ts b/web/app/cad/model/mobject.ts index c5f038ff..b251174d 100644 --- a/web/app/cad/model/mobject.ts +++ b/web/app/cad/model/mobject.ts @@ -3,6 +3,7 @@ import {EntityKind} from "cad/model/entities"; import Vector, {UnitVector} from "math/vector"; import {TopoObject} from "brep/topo/topo-object"; import Axis from "math/axis"; +import {ProductionInfo} from "cad/model/productionInfo"; export abstract class MObject { @@ -47,6 +48,9 @@ export abstract class MObject { return null; } + get productionInfo() { + return this.topology.data?.productionInfo; + } } export const MObjectIdGenerator = { diff --git a/web/app/cad/scene/controls/pickControlPlugin.ts b/web/app/cad/scene/controls/pickControlPlugin.ts index 68b08cc9..136a736c 100644 --- a/web/app/cad/scene/controls/pickControlPlugin.ts +++ b/web/app/cad/scene/controls/pickControlPlugin.ts @@ -227,6 +227,7 @@ export function activate(context) { if (LOG_FLAGS.PICK) { initRayCastDebug(); } + initRayCastDebug(); } export function traversePickResults(event, pickResults, kind, visitor) {