mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-06 08:25:19 +01:00
fix extrude from sketch tests
This commit is contained in:
parent
e6c9eb4ca4
commit
7df876df50
10 changed files with 152 additions and 98 deletions
|
|
@ -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<Vertex> {
|
||||
|
|
|
|||
|
|
@ -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<ExtrudeParams> = {
|
|||
|
||||
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: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<string>;
|
||||
|
||||
constructor(consumed: MObject[], mustAdvance: MObject[] = []) {
|
||||
super();
|
||||
this.consumed = consumed;
|
||||
this.mustAdvance = new Set<string>(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 {
|
|||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ export function activate(context) {
|
|||
if (LOG_FLAGS.PICK) {
|
||||
initRayCastDebug();
|
||||
}
|
||||
initRayCastDebug();
|
||||
}
|
||||
|
||||
export function traversePickResults(event, pickResults, kind, visitor) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue