-
Notifications
You must be signed in to change notification settings - Fork 2
/
context.fs
329 lines (299 loc) · 10.9 KB
/
context.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
FeatureScript ✨; /* Automatically generated version */
// This module is part of the FeatureScript Standard Library and is distributed under the MIT License.
// See the LICENSE tab for the license text.
// Copyright (c) 2013-Present PTC Inc.
export import(path : "onshape/std/featurescriptversionnumber.gen.fs", version : "✨");
import(path : "onshape/std/containers.fs", version : "✨");
import(path : "onshape/std/string.fs", version : "✨");
//====================== Context ========================
/**
* A `Context` is a `builtin` that stores modeling data, including bodies
* (solids, sheets, wires, and points), their constituent topological entities
* (faces, edges, and vertices), variables, feature error states, etc.
*
* Every Onshape Part Studio uses a single [Context]. All features, operations,
* and evaluation functions require a context to operate on. Different contexts
* do not interact, but data may be transferred from one to another using
* [opMergeContexts].
*
* Each context keeps track of the version of the Onshape Standard Library at
* which it was created. While regenerating a feature that has been "held back"
* to an older version, the version reported by the context will be the older
* version, causing subfeatures and operations to emulate old behavior.
*/
export type Context typecheck canBeContext;
/** Typecheck for [Context] */
export predicate canBeContext(value)
{
@isContext(value); /* implies (value is builtin) */
}
/**
* @internal
* Returns a new, empty [Context].
*/
export function newContext() returns Context
{
return @newContext(FeatureScriptVersionNumberCurrent) as Context;
}
/**
* @internal
* Returns `true` if the active feature of `context` is running at a version
* number at least as new as `introduced`.
*/
export function isAtVersionOrLater(context is Context, introduced is FeatureScriptVersionNumber) returns boolean
{
return @isAtVersionOrLater(context, introduced);
}
export function isAtVersionOrLater(versionToCheck is FeatureScriptVersionNumber, versionToCompareAgainst is FeatureScriptVersionNumber) returns boolean
{
// Enum sort order within maps is based on the ordinal
const mapOfVersions = { (versionToCheck) : true, (versionToCompareAgainst) : true };
var firstKey;
for (var key, _ in mapOfVersions)
{
firstKey = key;
return firstKey == versionToCompareAgainst;
}
}
/**
* Returns true if at or after mixed modeling release version.
*/
export function isAtInitialMixedModelingReleaseVersionOrLater(context is Context) returns boolean
{
return isAtVersionOrLater(context, FeatureScriptVersionNumber.V1745_MIXED_MODELING_RELEASE);
}
/**
* @internal
* Returns version at which the active feature of `context` is running
*/
export function getCurrentVersion(context is Context) returns FeatureScriptVersionNumber
{
return @getCurrentVersion(context) as FeatureScriptVersionNumber;
}
/**
* @internal
* Returns `true` if the active feature of `context` is a sheet metal feature, or is a subfeature of a sheet metal feature
*/
export function isInSheetMetalFeature(context is Context)
{
return @isInSheetMetalFeature(context);
}
//====================== Id ========================
/**
* An Id identifies a feature or operation in a context. Each feature,
* subfeature, and operation must have a unique id. Ids are used in queries,
* error reporting, and accessing data associated with features.
*
* Ids are hierarchical. That is, each operation's id must have a parent id.
* The root id is constructed with `newId()` and subIds are added with the
* overloaded `+` operator.
*
* @example `id + "foo"` represents an id named `"foo"` whose parent is `id`
* @example `id + "foo" + "bar"` represents an id named `"bar"` whose parent
* equals `id + "foo"`
*
* Internally, an `Id` is just an array whose elements are strings,
* representing the full path of the `Id`.
* @example `newId() + "foo" + "bar"` is equivalent to `["foo", "bar"] as Id`,
* though the expressions like the latter are not recommended in
* practice.
*
* Within a feature, all operations' ids should be children of the feature's
* `Id` (which is always passed into the feature function as the variable
* `id`).
*
* Subfeatures should use a similar pattern. For instance, in the snippet
* below, `mySubfeature` is a minimal example following good practices
* for breaking out a set of operations into a subroutine.
* ```
* annotation { "Feature Type Name" : "My Feature" }
* export const myFeature = defineFeature(function(context is Context, id is Id, definition is map)
* precondition {}
* {
* fCuboid(context, id + "startingCube", {
* "corner1" : vector(0, 0, 0) * inch,
* "corner2" : vector(1, 1, 1) * inch
* });
*
* mySubfeature(context, id + "subFeature", qCreatedBy(id + "startingCube", EntityType.EDGE));
*
* fCuboid(context, id + "endingCube", {
* "corner1" : vector(0, 0, 0) * inch,
* "corner2" : vector(-1, -1, -1) * inch
* });
* }, {});
*
* function mySubfeature(context is Context, id is Id, entities is Query)
* {
* opChamfer(context, id + "chamfer", {
* "entities" : entities,
* "chamferType" : ChamferType.EQUAL_OFFSETS,
* "width" : 0.1 * inch
* });
* opFillet(context, id + "fillet1", {
* "entities" : qCreatedBy(id + "chamfer", EntityType.EDGE),
* "radius" : 0.05 * inch
* });
* }
* ```
*
* The full id hierarchy must reflect creation history. That is, each `Id`
* (including parents) must refer to a contiguous region of operations on the
* context.
*
* Thus, the following code will fail because `id + "extrude"` alone refers to
* two non-contiguous regions of history:
* ```
* for (var i in [1, 2])
* {
* opExtrude(context, id + "extrude" + i, {...}); // Fails on second iteration.
* opChamfer(context, id + "chamfer" + i, {...});
* }
* ```
*
* For the above code, a pattern like `id + i + "extrude"` or
* `id + ("loop" ~ i) + "extrude"` would work as expected, as would the
* unnested `id + ("extrude" ~ i)`.
*
* Only the following characters are allowed in a string that makes up an `Id`: `a-z`,
* `A-Z`, `0-9`, `_`, `+`, `-`, `/`. An asterisk `*` is allowed at the beginning of
* the string to mark it an "unstable" component (see below).
*/
export type Id typecheck canBeId;
/** Typecheck for [Id] */
export predicate canBeId(value)
{
value is array;
for (var component in value)
{
component is string;
replace(component, "\\*?[a-zA-Z0-9_.+/\\-]", "") == "" || component == ANY_ID; //All characters should be of this form
}
}
/**
* Returns an empty id.
*/
export function newId() returns Id
{
return [] as Id;
}
/**
* Returns an id specified by the given string.
*/
export function makeId(idComp is string) returns Id
{
return [idComp] as Id;
}
/**
* True if the `Id` represents a top-level feature or default geometry (i.e.
* if the `Id` has length `1`)
*/
export predicate isTopLevelId(id is Id)
{
size(id) == 1;
}
/**
* The string literal `"*"`, which matches any id inside certain queries.
* @ex `qCreatedBy(id + ANY_ID + "fillet")`
*/
export const ANY_ID = '*';
/**
* Marks a given id component as "unstable" causing queries to treat it as a
* wildcard. This is useful for when the id component is not expected to be
* robust, such as an index into the results of an evaluated query.
*/
export function unstableIdComponent(addend) returns string
{
return (ANY_ID ~ addend);
}
export operator+(id is Id, addend is string) returns Id
precondition
{
replace(addend, "^\\.", "_") == addend;
}
{
return append(id, addend) as Id;
}
export operator+(id is Id, addend is number) returns Id
{
return id + replace("" ~ addend, "\\.", "_");
}
export operator+(id is Id, addend is Id) returns Id
{
return concatenateArrays([id, addend]) as Id;
}
//====================== Variable builtins ========================
/**
* Attach a variable to the context, which can be retrieved by another feature
* defined later. If a variable of the same name already exists, this function
* will overwrite it.
*
* @example `setVariable(context, "foo", 1)` attaches a variable named `"foo"`,
* with value set to `1`, on the context.
*
* @param value : Can be any value, including an array or map with many elements.
*/
export function setVariable(context is Context, name is string, value)
{
@setVariable(context, { "name" : name, "value" : value, "description" : "" });
}
/**
* Attach a variable with a description to the context, which can be retrieved by another feature
* defined later. If a variable of the same name already exists, this function
* will overwrite it.
*
* @example `setVariable(context, "foo", 1, "the foo")` attaches a variable named `"foo"`,
* with value set to `1` and the description set to "the foo", on the context.
*
* @param value : Can be any value, including an array or map with many elements.
* @param description : A string describing the use or purpose of the variable.
*/
export function setVariable(context is Context, name is string, value, description is string)
{
@setVariable(context, { "name" : name, "value" : value, "description" : description });
}
/**
* Retrieve a variable attached to the context by name.
* Throws an exception if variable by the given name is not found.
*
* @example `getVariable(context, "foo")` returns the value assigned to a
* previously-set variable named `"foo"`.
*
* Variables on a context can also be accessed within a Part Studio using
* `#` syntax (e.g. `#foo`) inside any parameter which allows an expression.
*/
export function getVariable(context is Context, name is string)
{
return @getVariable(context, { "name" : name });
}
/**
* Retrieve a variable attached to the context by name.
* If variable by the given name is not found, returns `defaultValue`
*
* @example `getVariable(context, "foo", {})` returns the value assigned to a
* previously-set variable named `"foo"`. If not found returns empty map.
*
*/
export function getVariable(context is Context, name is string, defaultValue)
{
return @getVariable(context, { "name" : name, "defaultValue" : defaultValue });
}
/**
* @internal
* Retrieves all variables (including configuration variables) attached to the
* context as a map from the variable name to the variable value.
*/
export function getAllVariables(context is Context) returns map
{
return @getAllVariables(context);
}
/**
* Returns the language version of the library. Note: this function calls `@getLanguageVersion` internally,
* but if you call `@getLanguageVersion` directly, you may get a different result. That is because
* `@getLanguageVersion` returns the language version of the module making the call (which, for a module in std
* will coincide with the version of std.)
*/
export function libraryLanguageVersion()
{
return @getLanguageVersion();
}