Object » WindowingTransformation
Instances of WindowingTransformation are used to transform objects from a source coordinate system to a destination coordinate system. Each instance contains a scale and a translation which can be applied to objects that respond to scaleBy: and translateBy:. It can be created with a default identity scale and translation, or with a specified scale and translation, or with a scale and translation computed from a window (a Rectangle in the source coordinate system) and a viewport (a Rectangle in the destination coordinate system). In applying a WindowingTransformation to an object, the object is first scaled (around the origin of the source coordinate system) and then translated. WindowingTransformations can be composed to form a single compound transformation.
In the out-of-the box image, a WindowingTransformation can be applied to instances of the following classes: Point, Rectangle as well as most subclasses of Path.
Every View uses two windowing transformations:
scale - nil or Point
No scale is in effect when this instance variable is nil. Otherwise, if scale is aPoint, scale defines the scaling factors to be
the coordinates of a scaling.
translation - Point
a Point that defines a translation amount.
Three class methods are provided to create instances. One of these methods creates the identical transformation, the two remaining methods provide two possibilities to define a windowing transformation:
identity
Create an instance that has no scaling and no translation. Such an instance is
a suitable initial value for the scroll transformation of a view.
scale: aScale translation: aTranslation
aScale, aTranslation are expected to be instances of
Point. The method answers a new windowing transformation with a scale
factor of aScale and a translation offset of aTranslation.
When the transformation is applied (see WindowingTransformation>>apply:),
the scale is applied first, followed by the translation.
window: aWindow viewport: aViewport
aWindow, aViewport are expected to be instances of
Rectangle.
This method is used to map the rectangle aWindow onto the rectangle aViewport.
The scale and translation of the new instance are computed such that aWindow,
when transformed, coincides with aViewport.
applyTo: anObject
Apply the receiver to anObject and answer the result. Used to map
some object in source coordinates to one in destination coordinates.
applyInverseTo: anObject
Apply the inverse of the receiver to anObject and answer the result.
Used to map some object in destination coordinates to one in
source coordinates.
compose: aTransformation
Answer a WindowingTransformation that is the
composition of the receiver and aTransformation. The effect of applying
the resulting WindowingTransformation to an object is the same
as that of first applying aTransformation to
the object and then applying the receiver to its result.
In other words:
(transf2 compose: transf1) applyTo: <aPoint>
transf2 applyTo: (transf1 applyTo: <aPoint>)
scrollBy: aPoint
Answer a WindowingTransformation with the same scale as the receiver
and with a translation of the current translation plus aPoint scaled by
the current scale. It is used when the translation is known in source
coordinates, rather than scaled source coordinates (see
WindowingTransformation|translateBy:). An example is that of scrolling
objects with respect to a stationary window in the source coordinate
system. If no scaling is in effect (scale = nil), then
WindowingTransformation|translateBy: and
WindowingTransformation|scrollBy: are equivalent.
A WindowingTransformation without a scale is used to implement scrolling.
The first example shows the application of a translation to a collection of points and rectangles:
| transformation items |
transformation := WindowingTransformation scale: 1@1 translation: 0@25.
items := OrderedCollection new.
items add: 0@0.
items add: (10@10 corner: 30@ 25).
items collect: [:obj | transformation applyTo: obj].
an OrderedCollection(0@25 10@35 corner: 30@50)
To undo that transformation, we can now apply the inverse transformation to the transformed objects:
| transformation items result |
transformation := WindowingTransformation scale: 1@1 translation: 0@25.
items := OrderedCollection new.
items add: 0@0.
items add: (10@10 corner: 30@ 25).
result := items collect: [:obj | transformation applyTo: obj].
result collect: [:obj | transformation applyInverseTo: obj].
an OrderedCollection(0.0@0.0 10.0@10.0 corner: 30.0@25.0)
Note that the application of the inverse transformation does not only undo the previous transformation, it also changes the coordinates of the geometric objects to Float.
A scaling transformation can be used to magnify, to reduce or to mirror geometric objects.
The following example demonstrates the use of two windowing transformations to scale and to erect a data set.
| displayRect dataPoints ranges scale erect tr pen first | dataPoints := (0 to: 100 by: 10) with: #(0 30 90 50 150 200 170 110 80 20 0) collect: [:x :y | x @ y]. ranges := 0@0 corner: 100@200. displayRect := 10@10 extent: 200@200. Display fill: displayRect fillColor: Color white. pen := Pen new. pen color: Color red. scale := WindowingTransformation window: ranges viewport: (0@0 extent: displayRect extent - 1). erect := WindowingTransformation scale: 1@-1 translation: 0@ (displayRect height - 1) + displayRect origin. tr := erect compose: scale. first := true. dataPoints do: [:pt | transformedPt := (tr applyTo: pt) rounded. first ifTrue: [pen place: transformedPt. first := false. ] ifFalse: [pen goto: transformedPt.]. ].
Here is a beautiful example from the Squeak developers mailing list:
| form frames | frames := { 6@2 corner: 449@238. 186@210 corner: 434@390. 505@218 corner: 653@388. }. form := Form extent: 700@500 depth: 8. form fillWithColor: Color yellow. " first loop: A subForm has twice the size of a frame. A windowing transformation is then used to transform the subForm back to the size of the frame and to display it in the frame. This loop displays blue rectangles " frames do: [:frame | | subForm | subForm := Form extent: frame extent * 2 depth: 8. subForm fillWithColor: Color blue. subForm displayOn: form transformation: (WindowingTransformation window: subForm boundingBox viewport: frame) clippingBox: form boundingBox align: 0@0 with: 0@0 rule: Form over fillColor: nil]. " second loop: Forms that have the exact size of the frames are displayed in the frames. A windowing transformation is used to correctly position the forms. This loop displays red rectangles " frames do: [:frame | | subForm | subForm := Form extent: frame extent depth: 8. subForm fillWithColor: Color red. subForm displayOn: form transformation: (WindowingTransformation window: subForm boundingBox viewport: frame) clippingBox: form boundingBox align: 0@0 with: 0@0 rule: Form over fillColor: nil]. form displayAt: 10@10.
When you interchange the two loops, you will see blue rectangles in the positions of the red ones.