Version 0.25 has a slightly changed script API. This article describes the non-backward compatible changes and how to migrate to the new API version.
Vectors are new objects that appeared in the API to provide a more consistent definition of distances. Vectors are basically differences between two points. This means:
p1 = RBA::Point::new(1, 0) p2 = RBA::Point::new(2, 0) # This gives a RBA::Vector (1, 0): puts (p2 - p1).class
The opposite way, adding a vector to a point gives a point:
p = RBA::Point::new(1, 0) v = RBA::Vector::new(1, 0) # This gives a RBA::Point (2, 0): puts (p + v).class
The displacement part of the transformations is a vector now.
Vectors can be turned into points by adding the default (0, 0) point. Points can be turned into vectors by subtracting the default (0, 0) point:
p = RBA::Point::new(1, 0) # This gives a RBA::Vector (1, 0): puts (p - RBA::Point::new).class v = RBA::Vector::new(1, 0) # This gives a RBA::Point (1, 0): puts (v + RBA::Point::new).class
Vectors behave differently than points upon transformation: the displacement is not added to vectors. The reasoning behind this implementation is the distributive law. It becomes valid this way. Note that "*" is a new operator that transforms an object with the transformation as the first argument:
p1 = RBA::Point::new(1, 0) p2 = RBA::Point::new(2, 0) t = RBA::Trans::new(1, false, RBA::Vector::new(1, 0)) # Renders (0, 1): puts t * (p2 - p1) # Renders the same result: puts t * p2 - t * p1
Vectors appear in the two flavours: RBA::Vector for the integer types and RBA::DVector for the floating point types.
So, migration of scripts requires to check for places where point differences are computed and is used as input to transformations. In such cases, the vectors that result from point differences need to be converted to points to render the same results. Conversion to points can be done in a backward-compatible way by adding a default point.
If using the displacement part of a transformation as input to other transformations, the displacement part needs to be turned into a point to get the same results than before.
Complex transformations have the ability to translate coordinate types. For example, the RBA::CplxTrans class is able to translate an integer-type point to a floating-point type point:
t = RBA::CplxTrans::new(1.5, 90, false, RBA::DVector::new(1, 0)) # gives a RBA::DPoint with (1.0, 1.5): puts t * RBA::Point::new(1, 0)
With version 0.25, the new RBA::VCplxTrans type has been added. Hence a consistent set of types is available for all four possible translations. The inverse of a transformation is able to translate the output type of the original transformation into it's input type:
|Input type||Output type||Transformation||Inverse transformation|
The concatenation of complex transformations is forbidden for transformations with incompatible input and output types now. If allowed, the concatenation operator "*" renders the transformation class that translates the input of the second to the output of the first transformation. Some combinations are granted for simplicity, although not correct in the strict sense:
|A*B||B: RBA::ICplxTrans||B: RBA::CplxTrans||B: RBA::VCplxTrans||B: RBA::DCplxTrans|
Scripts written for 0.24 may fail if the complex transformation types are mixed too liberally. Type consistency needs to be observed for 0.25-compatible scripts.
If required, a type mismatch can be corrected by casting the transformation into the right type using the type cast constructor:
t1 = RBA::CplxTrans::new(1.5) t2 = RBA::DCplxTrans::new(2.0) # this will fail now: t = t1 * t2 # this will work (type cast): t = RBA::DCplxTrans::new(t1) * t2 # the backward-compatible (but deprecated) way is: t = RBA::DCplxTrans::from_itrans(t1) * t2
Because the Ruby binding did not provide support for overloads initially, some non-overload functions existed that have been removed now.
The former Observer pattern was dropped in favor of events. Events are much easier to handle and with version 0.25, event listeners can be added and removed. In addition, events can carry arbitrary arguments which the observer can't.
The events are usually called "on_...". For example, to register a selection observer, the required code was this:
class FollowSelection < RBA::Observer def signal .. provide your event handler coder here .. end end view = .. some RBA::LayoutView .. view.add_selection_changed_observer(FollowSelection::new)
Now the code is:
view = .. some RBA::LayoutView .. view.on_selection_changed += lambda do .. provide your event handler coder here .. end
Instead of "lamdba" you can use Proc objects too. With lambdas the argument list needs to match the event signature, while for Procs the argument list can be shorter. It's also possible to use plain blocks, if you don't care about other receivers for this event. Blocks will take exclusive ownership over this event:
view = .. some RBA::LayoutView .. # This will detach all other listeners: view.on_selection_changed do .. provide your event handler coder here .. end
For details about events, see Events And Callbacks.