Interactive Animated Rectangle

Scott Stevenson recently began a new series on Cocoa Dev Central (I really like the new layout and formatting) titled Learn Quartz. The first article, Introduction to Quartz, is necessarily simple and talks about little more than NSRect and its associated convenience methods. This is important information to understand before delving further into Cocoa drawing. At the end of the article Scott provides a code snippet to draw 12 squares, each a different color, in one function call. For me the article was a refresh for information I was already comfortable with but it did plant a seed of thought. What could make for a more interesting demonstration and yet still rely on one function call for drawing?

The first thing the demo would need is some animation. A static rectangle in a view isn’t very exciting. Animation is just the illusion of movement caused by redrawing visual objects in different positions. The most simple animation of NSRect would be to change the origin and/or size a number of times per second and cause the view to redraw. I wanted something more interesting. I wanted rotation. It doesn’t take long looking at NSRect to realize it is not possible to represent a rotated rectangular region with it. Rotation is definitely beyond the scope of what NSRect exists for but there is a way for NSRect to be rotated when drawn. Enter NSAffineTransform.

NSAffineTransform enables us to transform points in one coordinate system (such as the cartesian coordinate system utilized by Cocoa) to points in another coordinate system while preserving parallel lines in a path. One of the ways points can be transformed is by rotation. As we are working with NSRect it will be necessary to apply the transformation to the current graphics context ( NSGraphicsContext ) before we issue any drawing command. The ADC Transform Basics article provides visual representations of the effect of applying transforms to a current graphics context. In order to achieve the appearance of a rectangle spinning we would need to change the angle of rotation and redraw the view several times every second.

It is important to note that a rotation transform is rotating around the origin. If the NSRect had an origin of 0, 0 it would stay anchored at the point regardless of the rotation angle. As the rotation angle changed one corner would stay fixed at 0, 0 while the rest of the rectangle spun about that axis. This isn’t really the effect I am interested in so I set the coordinates of the NSRect such that 0, 0 is the center of my rectangle (a square in this case at origin -25, -25 and size 50, 50).

Now with the animation handled I wanted to add interactivity. I decided that the user would be able to drag the rotating rectangle around in the view space. To provide such support I would need to know when the user clicked within the rectangle. But as you will see in the code, the NSRect is never changed. So I need to determine when the user clicks within the drawn rectangle. Luckily the NSAffineTransform can be employed to transform from the drawn coordinate system to the base coordinate system.

Finally instead of having a single rectangle rotating and being dragged I have 10 of them. You can download the RectTransformDemo project.

Leave a Reply