Skip to content

Commit b3fb8f9

Browse files
author
Onshape Standard Library Importer
committed
Version 0.3.0
version id: 53bca1b5e4b034f430934ecf parent: 53bca1b2e4b034f430934ece microversion: 13aab956d74244c9bf28e29d created at: 2014-07-09T01:58:13.101+00:00
1 parent 8cb59cc commit b3fb8f9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+6387
-0
lines changed

boolean.fs

Lines changed: 383 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,383 @@
1+
export import(path : "onshape/std/geomUtils.fs", version : "");
2+
export import(path : "onshape/std/evaluate.fs", version : "");
3+
export import(path : "onshape/std/query.fs", version : "");
4+
export import(path : "onshape/std/utils.fs", version : "");
5+
export import(path : "onshape/std/errorstringenum.gen.fs", version : "");
6+
7+
export enum BooleanOperationType
8+
{
9+
annotation {"Name" : "Union"}
10+
UNION,
11+
annotation {"Name" : "Subtract"}
12+
SUBTRACTION,
13+
annotation {"Name" : "Intersect"}
14+
INTERSECTION,
15+
annotation {"Hidden" : true}
16+
SUBTRACT_COMPLEMENT
17+
}
18+
19+
//Boolean Operation
20+
annotation {"Feature Type Name" : "Boolean"}
21+
export function booleanBodies(context is Context, id is Id, booleanDefinition is map)
22+
precondition
23+
{
24+
annotation {"Name" : "Operation type"}
25+
booleanDefinition.operationType is BooleanOperationType;
26+
annotation {"Name" : "Tools", "Filter" : EntityType.BODY && BodyType.SOLID}
27+
booleanDefinition.tools is Query;
28+
29+
if (booleanDefinition.operationType == BooleanOperationType.SUBTRACTION)
30+
{
31+
annotation {"Name" : "Targets", "Filter" : EntityType.BODY && BodyType.SOLID}
32+
booleanDefinition.targets is Query;
33+
34+
if( booleanDefinition.keepTools != undefined )
35+
{
36+
annotation {"Name" : "Keep tools"}
37+
booleanDefinition.keepTools is boolean;
38+
}
39+
40+
}
41+
}
42+
{
43+
if(booleanDefinition.keepTools == undefined)
44+
booleanDefinition.keepTools = false;
45+
46+
startFeature(context, id, booleanDefinition);
47+
48+
49+
opBoolean(context, id, booleanDefinition);
50+
endFeature(context, id);
51+
}
52+
53+
export enum NewBodyOperationType
54+
{
55+
annotation {"Name" : "New"}
56+
NEW,
57+
annotation {"Name" : "Add"}
58+
ADD,
59+
annotation {"Name" : "Remove"}
60+
REMOVE,
61+
annotation {"Name" : "Intersect"}
62+
INTERSECT
63+
}
64+
65+
export predicate booleanStepTypePredicate(booleanDefinition is map)
66+
{
67+
if(booleanDefinition.operationType != undefined)
68+
{
69+
annotation {"Name" : "Result body operation type"}
70+
booleanDefinition.operationType is NewBodyOperationType;
71+
}
72+
}
73+
74+
export predicate booleanStepScopePredicate(booleanDefinition is map)
75+
{
76+
if(booleanDefinition.operationType != undefined)
77+
{
78+
if (booleanDefinition.operationType != NewBodyOperationType.NEW)
79+
{
80+
if (booleanDefinition.defaultScope != undefined)
81+
{
82+
annotation{"Name" : "Merge with all", "Default" : false}
83+
booleanDefinition.defaultScope is boolean;
84+
if (booleanDefinition.defaultScope != true)
85+
{
86+
annotation { "Name" : "Merge scope", "Filter" : EntityType.BODY && BodyType.SOLID}
87+
booleanDefinition.booleanScope is Query;
88+
}
89+
}
90+
}
91+
}
92+
}
93+
94+
export predicate booleanStepPredicate(booleanDefinition is map)
95+
{
96+
booleanStepTypePredicate(booleanDefinition);
97+
booleanStepScopePredicate(booleanDefinition);
98+
}
99+
100+
export function processNewBodyIfNeeded(context is Context, id is Id, definition is map) returns boolean
101+
{
102+
// This function returns a logical boolean to indicate if the boolean operation was successful
103+
// If there is a problem with the inputs it will return true, if everything is fine it will return true
104+
// If the boolean can't be done it will return false
105+
106+
if( !featureHasError(context, id) && definition.operationType != NewBodyOperationType.NEW)
107+
{
108+
var booleanDefinition = {};
109+
booleanDefinition.operationType = {
110+
NewBodyOperationType.ADD : BooleanOperationType.UNION,
111+
NewBodyOperationType.REMOVE : BooleanOperationType.SUBTRACTION,
112+
NewBodyOperationType.INTERSECT : BooleanOperationType.SUBTRACT_COMPLEMENT
113+
}[definition.operationType];
114+
booleanDefinition.tools = qBodyType(qCreatedBy(id, EntityType.BODY), BodyType.SOLID);
115+
if ( size(evaluateQuery(context, booleanDefinition.tools)) == 0 )
116+
{
117+
//TODO : this would probably be better to block on the UI, but that would be something
118+
//to do if/when we have a "Make surface" checkbox or similar indication.
119+
reportFeatureError(context, id, ErrorStringEnum.BOOLEAN_NEED_ONE_SOLID);
120+
return true;
121+
}
122+
123+
if (definition.defaultScope != false)
124+
{
125+
booleanDefinition.targets = qSubtraction(qBodyType(qEverything(EntityType.BODY), BodyType.SOLID), booleanDefinition.tools);
126+
}
127+
else
128+
{
129+
booleanDefinition.targets = definition.booleanScope;
130+
}
131+
132+
if (size(evaluateQuery(context, booleanDefinition.targets)) > 0 )
133+
{
134+
booleanDefinition.targetsAndToolsNeedGrouping = true;
135+
booleanBodies(context, id + "boolean", booleanDefinition);
136+
return !processSubfeatureStatus(context, id + "boolean", id);
137+
}
138+
else
139+
{
140+
reportFeatureError(context, id, ErrorStringEnum.BOOLEAN_NEED_ONE_SOLID, ["booleanScope"]);
141+
return false;
142+
}
143+
}
144+
else
145+
{
146+
return true;
147+
}
148+
}
149+
150+
function classifyCollisions(context is Context, collisions is array) returns map
151+
{
152+
var targetBodyToCollisionType = {};
153+
for ( var c in collisions)
154+
{
155+
if (targetBodyToCollisionType[c.toolBody] == undefined)
156+
targetBodyToCollisionType[c.toolBody] = {};
157+
if (c.targetBody is Query)
158+
{
159+
if (targetBodyToCollisionType[c.toolBody][c.targetBody] == undefined)
160+
{
161+
targetBodyToCollisionType[c.toolBody][c.targetBody] = {};
162+
targetBodyToCollisionType[c.toolBody][c.targetBody]['collisions'] = [];
163+
}
164+
targetBodyToCollisionType[c.toolBody][c.targetBody]['type'] =
165+
combineCollisionType(targetBodyToCollisionType[c.toolBody][c.targetBody]['type'], c['type']);
166+
targetBodyToCollisionType[c.toolBody][c.targetBody]['collisions'] = append(targetBodyToCollisionType[c.toolBody][c.targetBody]['collisions'], c);
167+
}
168+
}
169+
170+
var classifyCollisions = {};
171+
for (var perTool in targetBodyToCollisionType)
172+
{
173+
var toolCollisions = { 'abutting' : [], 'intersection' : []};
174+
for (var entry in perTool.value)
175+
{
176+
if (entry.value['type'] == "INTERFERE" ||
177+
entry.value['type'] == "TARGET_IN_TOOL" ||
178+
entry.value['type'] == "TOOL_IN_TARGET")
179+
toolCollisions.intersection = append(toolCollisions.intersection, entry.key);
180+
else if ((entry.value['type'] == "ABUT_NO_CLASS" ||
181+
entry.value['type'] == "ABUT_TOOL_OUT_TARGET") &&
182+
faceToFaceCollisionsContainInterferences(context, entry.value['collisions']))
183+
toolCollisions.abutting = append(toolCollisions.abutting, entry.key);
184+
}
185+
if (size(toolCollisions.intersection) > 0 || size(toolCollisions.abutting) > 0)
186+
classifyCollisions[perTool.key] = toolCollisions;
187+
}
188+
return classifyCollisions;
189+
}
190+
191+
function faceToFaceCollisionsContainInterferences(context is Context, collisions) returns boolean
192+
{
193+
for ( var c in collisions)
194+
{
195+
if (c.tool is Query && c.target is Query)
196+
{
197+
var collisionResult = evCollision(context, {'tools' : c.tool, 'targets' : c.target});
198+
if (collisionResult.error != undefined)
199+
{
200+
return false;
201+
}
202+
203+
for ( var col1 in collisionResult.result)
204+
{
205+
if (col1['type'] == "INTERFERE" ||
206+
col1['type'] == "TARGET_IN_TOOL" ||
207+
col1['type'] == "TOOL_IN_TARGET")
208+
{
209+
return true;
210+
}
211+
}
212+
}
213+
}
214+
return false;
215+
}
216+
217+
// This function implements autoSelection rules. Is probably a subject to further changes.
218+
// Current rule: If every tool abuts one and only one target Boolean operation is set to "ADD" and abutting targets go into booleanScope list
219+
// If every tool intersects one and only one target Boolean operation is set to "REMOVE" and intersecting targets go into booleanScope list
220+
// Otherwise Boolean operation is set to "NEW" booleanScope list is cleared out
221+
export function autoSelectionForBooleanStep(context is Context, featureDefinition is map, featureInfo is map) returns map
222+
{
223+
var id = newId();
224+
var toolQ = qBodyType(qCreatedBy(id + featureInfo.featureId, EntityType.BODY), BodyType.SOLID);
225+
var excludeQ;
226+
if (featureInfo.excludeBodies is Query)
227+
excludeQ = qUnion([toolQ, featureInfo.excludeBodies]);
228+
else
229+
excludeQ = toolQ;
230+
var targetQ = qSubtraction(qBodyType(qEverything(EntityType.BODY), BodyType.SOLID), excludeQ);
231+
var collisionResult = evCollision(context, {tools : toolQ, targets : targetQ});
232+
if (collisionResult.error != undefined)
233+
{
234+
return setOperationType(featureDefinition, NewBodyOperationType.NEW, []);
235+
}
236+
var collisions = collisionResult.result;
237+
if (collisions is array)
238+
{
239+
var collisionClasses = classifyCollisions(context, collisions);
240+
var conditionsToAdd; //undefined|boolean
241+
var conditionsToRemove; //undefined|boolean
242+
var target = [];
243+
for (var entry in collisionClasses)
244+
{
245+
var nIntersections = size(entry.value.intersection);
246+
var nAbutting = size(entry.value.abutting);
247+
conditionsToRemove = (conditionsToRemove != false && nAbutting == 0 && nIntersections == 1);
248+
conditionsToAdd = (conditionsToAdd != false && nAbutting == 1 && nIntersections == 0);
249+
if (conditionsToRemove == conditionsToAdd)
250+
break;
251+
if (conditionsToRemove && !isIn(entry.value.intersection[0], target))
252+
target = append(target, entry.value.intersection[0]);
253+
if (conditionsToAdd && !isIn(entry.value.abutting[0], target))
254+
target = append(target, entry.value.abutting[0]);
255+
}
256+
if (conditionsToRemove == true && conditionsToAdd != true)
257+
return setOperationType(featureDefinition, NewBodyOperationType.REMOVE, target);
258+
else if (conditionsToAdd == true && conditionsToRemove != true)
259+
return setOperationType(featureDefinition, NewBodyOperationType.ADD, target);
260+
}
261+
return setOperationType(featureDefinition, NewBodyOperationType.NEW, []);
262+
}
263+
264+
// The latest implementation is simpler. If the number of parts hit by the tools is equal to 1 then we default to add.
265+
// If the number of parts hit by the tools is other than 1 then we default to new.
266+
export function autoSelectionForBooleanStep2(context is Context, featureDefinition is map, featureInfo is map) returns map
267+
{
268+
var id = newId();
269+
var toolQ = qBodyType(qCreatedBy(id + featureInfo.featureId, EntityType.BODY), BodyType.SOLID);
270+
var excludeQ;
271+
if (featureInfo.excludeBodies is Query)
272+
excludeQ = qUnion([toolQ, featureInfo.excludeBodies]);
273+
else
274+
excludeQ = toolQ;
275+
var targetQ = qSubtraction(qBodyType(qEverything(EntityType.BODY), BodyType.SOLID), excludeQ);
276+
var collisionResult = evCollision(context, {tools : toolQ, targets : targetQ});
277+
if (collisionResult.error != undefined)
278+
{
279+
return setOperationType(featureDefinition, NewBodyOperationType.NEW, []);
280+
}
281+
var collisions = collisionResult.result;
282+
if (collisions is array && size(collisions) > 0)
283+
{
284+
var collisionClasses = classifyCollisions(context, collisions);
285+
var target = undefined;
286+
for (var entry in collisionClasses)
287+
{
288+
var collisions = concatenateArrays([entry.value.intersection, entry.value.abutting]);
289+
var nCollisions = size(collisions);
290+
if (nCollisions > 1)
291+
{
292+
// Hits more than one thing. Use NEW.
293+
return setOperationType(featureDefinition, NewBodyOperationType.NEW, []);
294+
}
295+
else if (nCollisions == 1)
296+
{
297+
if (target != undefined && target != collisions[0])
298+
{
299+
// Hits more than one thing. Use NEW.
300+
return setOperationType(featureDefinition, NewBodyOperationType.NEW, []);
301+
}
302+
else
303+
{
304+
target = collisions[0];
305+
}
306+
}
307+
}
308+
// classifyCollisions filters out abutting along edge, so we might come empty here
309+
if (target != undefined)
310+
{
311+
return setOperationType(featureDefinition, NewBodyOperationType.ADD, [target]);
312+
}
313+
}
314+
// No collisions, use NEW
315+
return setOperationType(featureDefinition, NewBodyOperationType.NEW, []);
316+
}
317+
318+
319+
function combineCollisionType(oldType , newType is string) returns string
320+
{
321+
if (oldType == undefined || oldType == newType)
322+
return newType;
323+
if (oldType == "INTERFERE" || newType == "INTERFERE")
324+
return "INTERFERE";
325+
if (oldType == "TARGET_IN_TOOL" || oldType == "TOOL_IN_TARGET")
326+
return "INTERFERE";
327+
return oldType;
328+
}
329+
330+
function setOperationType(featureDef is map, opType is NewBodyOperationType, targets is array)
331+
{
332+
featureDef.operationType = opType;
333+
if (opType != NewBodyOperationType.NEW)
334+
{
335+
featureDef.defaultScope = false;
336+
featureDef.booleanScope = qUnion(targets);
337+
}
338+
else
339+
{
340+
featureDef.defaultScope = false;
341+
featureDef.booleanScope = qUnion([]);
342+
}
343+
return featureDef;
344+
}
345+
346+
//This function implements flip on Remove heuristics.
347+
//If tool bodies don't intersect with targets but there are abuttings, flip the direction
348+
export function flipCorrectionForRemove(context is Context, featureDefinition is map, featureInfo is map) returns map
349+
{
350+
var id = newId();
351+
var toolQ = qBodyType(qCreatedBy(id + featureInfo.featureId, EntityType.BODY), BodyType.SOLID);
352+
var excludeQ;
353+
if (featureInfo.excludeBodies is Query)
354+
excludeQ = qUnion([toolQ, featureInfo.excludeBodies]);
355+
else
356+
excludeQ = toolQ;
357+
var targetQ = qSubtraction(qBodyType(qEverything(EntityType.BODY), BodyType.SOLID), excludeQ);
358+
var collisionResult = evCollision(context, {tools : toolQ, targets : targetQ});
359+
if (collisionResult.error != undefined)
360+
{
361+
return featureDefinition;
362+
}
363+
var collisions = collisionResult.result;
364+
if (collisions is array)
365+
{
366+
var collisionClasses = classifyCollisions(context, collisions);
367+
var haveAbutting = false; //boolean
368+
for (var entry in collisionClasses)
369+
{
370+
var nIntersections = size(entry.value.intersection);
371+
if (nIntersections > 0)
372+
return featureDefinition;
373+
var nAbutting = size(entry.value.abutting);
374+
if (nAbutting > 0)
375+
haveAbutting = true;
376+
}
377+
if (haveAbutting) // if we made it here there are no intersections
378+
featureDefinition.oppositeDirection = (featureDefinition.oppositeDirection == true) ? false : true;
379+
}
380+
return featureDefinition;
381+
}
382+
383+

0 commit comments

Comments
 (0)