Onshape's Part Studios are written in FeatureScript. User actions that modify a Part Studio, under the hood, actually modify a representation of the Part Studio consisting of FeatureScript code.
You can see the underlying FeatureScript of any Part Studio by right-clicking its tab and clicking "Show code":
The individual components of a Part Studio's FeatureScript are familiar concepts for programmers of other languages:
In addition to custom features, FeatureScript can be used to define custom tables as functions that compute the data in the table by executing once a Part Studio has finished regenerating.
Feature types (along with other user-defined functions, types, etc.) are defined in Feature Studios within Onshape documents. Onshape's standard features are defined in the Feature Studios of a public document, where their source code may be viewed.
Feature Studios, Part Studios, and many types of data tabs in Onshape have a FeatureScript representation. These tabs, called "modules", can be imported into Feature Studios, either in the same document or from versions of linked documents. This consistent interface makes code and data reuse simple and reliable.
One consequence of Feature Studios living inside Onshape documents is that they share the version control and sharing systems used by the rest of Onshape, complete with permissions, versions, branches, and a full history of edits.
Feature Studios are implemented based on the Ace editor and many Ace shortcuts work in a Feature Studio.
FeatureScript's basic syntax is designed to look and feel familiar to programmers of other C-family languages like C++, JavaScript, or Java. The language is fully whitespace insensitive, and FeatureScript statements always end in a semicolon. /* block comments */
and // end of line comments
can be added in the standard way.
Determinism is a core principle of FeatureScript. At Onshape we believe that models must regenerate the same way every time, everywhere. Thus, FeatureScript has no concept of undefined behavior, and execution cannot be influenced by external input, time, or randomness.
FeatureScript discourages hidden state and shared references. Value semantics are used everywhere, and when references are needed, a distinctive syntax (using boxes) makes it obvious. Assignment, argument passing, and function return values all behave as if making deep copies.
FeatureScript is strongly typed and dynamically typed, and overloads for functions and operators can be cleanly defined for each FeatureScript type.
The Standard Library defines natural overloads for operators (like +
and *
) on geometric types (like Vector
and Transform
). This makes calculations involving these types simple and readable:
var v is Vector = vector(1, 2) + vector(3, 4);
var transformedPoint = transform2 * transform1 * originalPoint;
FeatureScript is also a natural fit for working with units. Operator overloads are defined to create a ValueWithUnits
:
const width is ValueWithUnits = 1.5 * inch;
const angle is ValueWithUnits = 30 * degree;
const area is ValueWithUnits = 12 * centimeter^2;
These values know their own dimensionality (length, angle, etc.), but don't differentiate unit systems (like meters vs. inches) making 1 * meter
equal to and indistinguishable from 1000 * millimeter
.
Overloads on ValueWithUnits
make math with units straightforward:
var squareArea = (3 * meter + 3 * inch)^2; // Has area units
var pendulumPeriod = 2 * PI * sqrt(armLength / (9.8 * meter/second^2)); // Has time units
var numberOfBricks = floor(wallLength / brickLength); // Has no units
var nonsense = (3 * meter) + (3 * degree); // Throws an error from unit mismatch
Values with units also play nicely with vectors and transforms. The code below asserts that a point transformed by rotation around the z-axis is equal to the same point constructed trigonometrically:
const start is Vector = vector(2, 0, 0) * inch;
const zAxis is Line = line(vector(0, 0, 0) * inch, vector(0, 0, 1));
const end is Vector = rotationAround(zAxis, 30 * degree) * start;
const goal is Vector = vector(cos(30 * degree), sin(30 * degree), 0) * (2 * inch);
println(tolerantEquals(end, goal)); // Prints "true"
For more on units, vectors, geometry, and other math in FeatureScript, see the math section of the Standard Library documentation.