xpgl Transformations¶
To use:
from XPPython3 import xpgl
You can transform any drawn object/text/image (or set of objects) using:
setRotateTransform(): Rotate around a specific point.setTranslateTransform(): Move horizontally and/or vertically to new position.setScaleTransform(): Enlarge or shrink, relative a specific point.
These transformation need to be surrounded by their own graphics context so as to not alter all subsequent drawing.
Within your draw callback, you’ll bracket the set of objects you want modfied with calls to save and restore the graphics context:
- graphicsContext() None¶
- saveGraphicsContext() None¶
- restoreGraphicsContext() None¶
- Param
None
- Returns
None
Call
saveGraphicsContext()immediately before you want to transform a drawn object, andrestoreGraphicsContext()after you’ve completed drawing.The sequence is:
saveGraphicsContext() <rotate>, <translate> and / or <scale> <draw> restoreGraphicsContext()
-or-:
with graphicsContext(): <rotate>, <translate> and / or <scale> <draw>You must pair each
saveGraphicsContext()withrestoreGraphicsContext()and they may be nested.For example:
xpgl.saveGraphicsContext() xpgl.setRotateTransform(-xp.getCycleNumber() % 360, 320, 380) xpgl.drawWideLine(320, 380, 320, 430, thickness=7, color=Colors['orange']) xpgl.restoreGraphicsContext() xpgl.saveGraphicsContext() xpgl.setRotateTransform(xp.getCycleNumber() % 360, 320, 240) xpgl.drawWideLine(320, 100, 320, 380, thickness=7, color=Colors['red']) xpgl.saveGraphicsContext() xpgl.setRotateTransform(-xp.getCycleNumber() * 4 % 360, 320, 400) xpgl.drawWideLine(320, 380, 320, 420, thickness=3, color=Colors['white']) xpgl.restoreGraphicsContext() xpgl.restoreGraphicsContext() xpgl.saveGraphicsContext() xpgl.setRotateTransform(-xp.getCycleNumber() % 360, 320, 75) xpgl.drawWideLine(320, 50, 320, 100, thickness=7, color=Colors['green']) xpgl.restoreGraphicsContext()
Note that for each drawn line, we have a separate graphics context, and for the white line at the end of the red line, we’ve nested an additional rotation.
A more pythonic approach is to use a context manager which handles the save/restore automatically, and improves code clarity by adding indentation blocks.
The same code using the context manager
graphicsContext():with xpgl.graphicsContext(): xpgl.setRotateTransform(-xp.getCycleNumber() % 360, 320, 380) xpgl.drawWideLine(320, 380, 320, 430, thickness=7, color=Colors['orange']) with xpgl.graphicsContext(): xpgl.setRotateTransform(xp.getCycleNumber() % 360, 320, 240) xpgl.drawWideLine(320, 100, 320, 380, thickness=7, color=Colors['red']) with xpgl.graphicsContext(): xpgl.setRotateTransform(-xp.getCycleNumber() * 4 % 360, 320, 400) xpgl.drawWideLine(320, 380, 320, 420, thickness=3, color=Colors['white']) with xpgl.graphicsContext(): xpgl.setRotateTransform(-xp.getCycleNumber() % 360, 320, 75) xpgl.drawWideLine(320, 50, 320, 100, thickness=7, color=Colors['green'])
- setRotateTransform(angle, x=0, y=0) None¶
- Parameters
angle (float) – Angle of rotation, in degrees, counter-clockwise.
x (float) –
y (float) – Origin, around which the rotation is performed.
- Returns
None
If the origin is not specific, the rotation will be performed around (0,0), the lower left corner. More commonly, you’ll want to perform rotation around the center or one end of the object being displayed. In this case, you’d calculate center of rotation and specify it in the function call:
def drawLine(color): xpgl.drawWideLine(208, 156, 432, 324, thickness=7, color=color) degrees = xp.getCycleNumber() * 4 % 360 with xpgl.graphicsContext(): xpgl.setRotateTransform(degrees, 320, 240) drawLine(Colors['red']) with xpgl.graphicsContext(): xpgl.setRotateTransform(degrees, 0, 0) drawLine(Colors['white']) with xpgl.graphicsContext(): xpgl.setRotateTransform(degrees, 432, 324) drawLine(Colors['orange'])
For example, here the same line is draw three times. As a red line, it rotates around its center (320, 240). As a white line, it rotates around the screen origin (0, 0). As an orange line, it rotates around one of the line’s end points (432, 324).
In the image displayed with the previous command,
graphicsContext()above, note how the small white line is simultaneously subjected to two rotations due to nesting. The first rotation causes it to rotate with the red line, relative the mid-point on the screen. The second rotation causes it to rotate around its own center point. When objects are subjected to multiple transformations, the order in which they are performed matters.
- setTranslateTransform(dx=0, dy=0) None¶
- Parameters
dx (float) –
dy (float) – move to the right (+dx) and up (+dy)
Translation moves an object (or set of objects) along one or both axis:
xpgl.drawWideLine(320, 0, 320, 480, thickness=7, color=Colors['red']) span = 200 offset = xp.getCycleNumber() % span if offset >= span / 2: offset = span - offset offset = offset - span / 4 with xpgl.graphicsContext(): xpgl.setTranslateTransform(offset, 0) xpgl.drawTriangle(320, 370, 335, 390, 305, 390) xpgl.drawTriangle(320, 110, 335, 90, 305, 90) xpgl.drawLine(320, 100, 320, 380)
In most cases, you could simply change the (x,y) coordinates for each object instead of using a translation transform, but as your drawings become more complicated, using translations can simplify things.
Alternatively, you could design your objects to use (0, 0) as their center point, and then always use a translation to “move” the object into the correct position on the screen.
- translateContext(dx=0, dy=0)¶
- Parameters
dx (float) –
dy (float) – move to the right (+dx) and up (+dy)
Sometimes you’ll want to perform a translation, do some work and then translate “back”. For this you can use
translateContext()with awithstatement:with xpgl.translateContext(10, 10): xpgl.drawLine(...)
This is identical to:
xpgl.setTranslateTransform(10, 10) xpgl.drawLine(...) xpgl.setTranslateTransform(-10, -10)
- setScaleTransform(dx=1, dy=1, x=0, y=0) None¶
- Parameters
dx (float) –
dy (float) – Amount to scale in x and y directions (multiplier)
x (float) –
y (float) – Location of origin point for scaling
Scaling refers to mulitiplying the size of the drawing in one or both directions. By default (dx, dy) == (1, 1) results in no change to scale.
The origin point (x, y) sets the point from which the drawing is scaled. If the point is in the center of a object, the object will appear to grow (or shrink) relative its center, for x:
span = 20 offset = (xp.getCycleNumber() / 4) % span if offset >= span / 2: offset = span - offset with xpgl.graphicsContext(): xpgl.setScaleTransform(offset, offset, 320, 240) xpgl.drawCircle(320, 240, 10, thickness=5, color=Colors['orange'])
By default it is the screen origin (0,0) which is unlikey what you want:
span = 20 offset = (xp.getCycleNumber() / 4) % span if offset >= span / 2: offset = span - offset offset = offset / 5 with xpgl.graphicsContext(): xpgl.setScaleTransform(offset, offset) xpgl.drawCircle(320, 240, 10, thickness=5, color=Colors['orange'])
