ShapeOp
0.1.0
|
This tutorial will require to rebuild the library. See the ShapeOp Documentation for detailed instructions and requirements Compilation and Installation.
A constraint projection of a given shape is defined as the shape that satisfies the constraint and is closest to the given shape in the least-squares sense. In this tutorial we will implement a orientation constraint. A orientation constraint acts on a set of 3 dimensional point and is satisfied if those points all lie on a plane with a prescribed orientation.
Since this constraint only concerns the relative position of points, we can first subtract the mean of all points, project them onto the closest shape satisfying the constraint, and then formulate the global step of ShapeOp with respect to the mean. The global step solves a linear system in the least-square sense. The computation of the mean of a set of given points is also linear, and can therefore be implemented in the linear system.
The orientation of a plane can be defined by a normal . After subtracting the mean the least-square fitting plane with normal contains the origin. The projection of a given point onto that plane is given by , where denotes the dot-product of and .
All constraints of ShapeOp are implemented in Constraint.h and Constaint.cpp. We therefore add the orientation constraint to those files. In the header file we add the declaration of our class inheriting from the abstract class ShapeOp::Constraint:
The private member input is used in the projection function. It ensures that memory is allocated at the construction of the OrientationConstraint object. This has a big impact execution speed, since ShapeOp uses parallelization, while memory allocation is inherently sequential.
The three functions following the constructor are inherited from the parent ShapeOp::Constraint class:
The last function allows to set a new orientation, which is specific to this constraint. In Constraint.cpp we add the implementation of our OrientationConstraint class. Lets start with the constructor:
The macro SHAPEOP_INLINE allows to inline a function and if applied to all build the whole library inline as a header-only library. The arguments to the constructor are documented in the header file. The setOrientation function is very short:
We now add the projection function:
ShapeOp uses the Eigen library for linear algebra. Please refer to their documentation for details. Note that the Matrix projections is a global Matrix for all projections in a problem. Each constraint therefore stores an integer id0_ to know where in the global layout of constraints he is. id0_ is stored in the function addConstraint which the solver calls on each constraint so that he adds himself to the linear system used for the global solve in ShapeOp:
This function sets up part of the global linear system matrix so that if multiplied with the vector of x-coordinates it yields the mean-centered x-coordinate for the corresponding points. The same holds for y- and z-coordinates. The right-hand side of the system is the Matrix projections we saw in the projection constraint. ShapeOp then solves the system - in a least-square sense if it is over-constrained. All code we have added so far yields a working version of our orientation constraint in the C++ part of our ShapeOp library. To support the factory pattern, we add the following line before the return in the static function Constraint::shapeConstraintFactory:
Most interlingual-bindings require C interfaces. ShapeOp has a C interface in the files API.h and API.cpp. Our orientation constraint is already included by the last line of code we added to the factory. To enable editing the orientation normal, we add the following code to the implementation of the function shapeop_editConstraint in API.cpp:
Now the library needs to be rebuilt. To do so, follow the compilation documentation of ShapeOp Compilation and Installation.
The Grasshopper integration of ShapeOp uses Ironpythons c-types to call our library. First make sure the library loads properly by following the documentation at ShapeOp for Grasshopper(Rhino). Copy the freshly built library to the folder Rhino is loading it from. After loading one of our examples files and running it successfully, the only thing we need to add to use our constraint is an additional option in the Value List component which feeds into the SOCSig Component. To do so, double-click the Value List component and add the following line:
This string is then forwarded without further changes to the SOCSig component, which bundles all parameters for a set of constraints into Constraint-Signatures. The SOSolver component uses the string when adding a new constraint with a call to the dll, where the C API will internally call ShapeOp::Constraint::shapeConstraintFactory where the string ultimately gets resolved.