1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
|
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of this
** file.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\page coordsys.html
\title Coordinate System
\ingroup qt-graphics
\ingroup best-practices
\brief Information about the coordinate system used by the paint
system.
The coordinate system is controlled by the QPainter
class. Together with the QPaintDevice and QPaintEngine classes,
QPainter form the basis of Qt's painting system, Arthur. QPainter
is used to perform drawing operations, QPaintDevice is an
abstraction of a two-dimensional space that can be painted on
using a QPainter, and QPaintEngine provides the interface that the
painter uses to draw onto different types of devices.
The QPaintDevice class is the base class of objects that can be
painted: Its drawing capabilities are inherited by the QWidget,
QPixmap, QPicture, QImage, and QPrinter classes. The default
coordinate system of a paint device has its origin at the top-left
corner. The \e x values increase to the right and the \e y values
increase downwards. The default unit is one pixel on pixel-based
devices and one point (1/72 of an inch) on printers.
The mapping of the logical QPainter coordinates to the physical
QPaintDevice coordinates are handled by QPainter's transformation
matrix, viewport and "window". The logical and physical coordinate
systems coincide by default. QPainter also supports coordinate
transformations (e.g. rotation and scaling).
\tableofcontents
\section1 Rendering
\section2 Logical Representation
The size (width and height) of a graphics primitive always
correspond to its mathematical model, ignoring the width of the
pen it is rendered with:
\table
\row
\o \inlineimage coordinatesystem-rect.png
\o \inlineimage coordinatesystem-line.png
\row
\o QRect(1, 2, 6, 4)
\o QLine(2, 7, 6, 1)
\endtable
\section2 Aliased Painting
When drawing, the pixel rendering is controlled by the
QPainter::Antialiasing render hint.
The \l {QPainter::RenderHint}{RenderHint} enum is used to specify
flags to QPainter that may or may not be respected by any given
engine. The QPainter::Antialiasing value indicates that the engine
should antialias edges of primitives if possible, i.e. smoothing
the edges by using different color intensities.
But by default the painter is \e aliased and other rules apply:
When rendering with a one pixel wide pen the pixels will be
rendered to the \e {right and below the mathematically defined
points}. For example:
\table
\row
\o \inlineimage coordinatesystem-rect-raster.png
\o \inlineimage coordinatesystem-line-raster.png
\row
\o
\snippet doc/src/snippets/code/doc_src_coordsys.qdoc 0
\o
\snippet doc/src/snippets/code/doc_src_coordsys.qdoc 1
\endtable
When rendering with a pen with an even number of pixels, the
pixels will be rendered symetrically around the mathematical
defined points, while rendering with a pen with an odd number of
pixels, the spare pixel will be rendered to the right and below
the mathematical point as in the one pixel case. See the QRectF
diagrams below for concrete examples.
\table
\header
\o {3,1} QRectF
\row
\o \inlineimage qrect-diagram-zero.png
\o \inlineimage qrectf-diagram-one.png
\row
\o Logical representation
\o One pixel wide pen
\row
\o \inlineimage qrectf-diagram-two.png
\o \inlineimage qrectf-diagram-three.png
\row
\o Two pixel wide pen
\o Three pixel wide pen
\endtable
Note that for historical reasons the return value of the
QRect::right() and QRect::bottom() functions deviate from the true
bottom-right corner of the rectangle.
QRect's \l {QRect::right()}{right()} function returns \l
{QRect::left()}{left()} + \l {QRect::width()}{width()} - 1 and the
\l {QRect::bottom()}{bottom()} function returns \l
{QRect::top()}{top()} + \l {QRect::height()}{height()} - 1. The
bottom-right green point in the diagrams shows the return
coordinates of these functions.
We recommend that you simply use QRectF instead: The QRectF class
defines a rectangle in the plane using floating point coordinates
for accuracy (QRect uses integer coordinates), and the
QRectF::right() and QRectF::bottom() functions \e do return the
true bottom-right corner.
Alternatively, using QRect, apply \l {QRect::x()}{x()} + \l
{QRect::width()}{width()} and \l {QRect::y()}{y()} + \l
{QRect::height()}{height()} to find the bottom-right corner, and
avoid the \l {QRect::right()}{right()} and \l
{QRect::bottom()}{bottom()} functions.
\section2 Anti-aliased Painting
If you set QPainter's \l {QPainter::Antialiasing}{anti-aliasing}
render hint, the pixels will be rendered symetrically on both
sides of the mathematically defined points:
\table
\row
\o \inlineimage coordinatesystem-rect-antialias.png
\o \inlineimage coordinatesystem-line-antialias.png
\row
\o
\snippet doc/src/snippets/code/doc_src_coordsys.qdoc 2
\o
\snippet doc/src/snippets/code/doc_src_coordsys.qdoc 3
\endtable
\section1 Transformations
By default, the QPainter operates on the associated device's own
coordinate system, but it also has complete support for affine
coordinate transformations.
You can scale the coordinate system by a given offset using the
QPainter::scale() function, you can rotate it clockwise using the
QPainter::rotate() function and you can translate it (i.e. adding
a given offset to the points) using the QPainter::translate()
function.
\table
\row
\o \inlineimage qpainter-clock.png
\o \inlineimage qpainter-rotation.png
\o \inlineimage qpainter-scale.png
\o \inlineimage qpainter-translation.png
\row
\o nop
\o \l {QPainter::rotate()}{rotate()}
\o \l {QPainter::scale()}{scale()}
\o \l {QPainter::translate()}{translate()}
\endtable
You can also twist the coordinate system around the origin using
the QPainter::shear() function. See the \l {demos/affine}{Affine
Transformations} demo for a visualization of a sheared coordinate
system. All the transformation operations operate on QPainter's
transformation matrix that you can retrieve using the
QPainter::worldTransform() function. A matrix transforms a point
in the plane to another point.
If you need the same transformations over and over, you can also
use QTransform objects and the QPainter::worldTransform() and
QPainter::setWorldTransform() functions. You can at any time save the
QPainter's transformation matrix by calling the QPainter::save()
function which saves the matrix on an internal stack. The
QPainter::restore() function pops it back.
One frequent need for the transformation matrix is when reusing
the same drawing code on a variety of paint devices. Without
transformations, the results are tightly bound to the resolution
of the paint device. Printers have high resolution, e.g. 600 dots
per inch, whereas screens often have between 72 and 100 dots per
inch.
\table 100%
\header
\o {2,1} Analog Clock Example
\row
\o \inlineimage coordinatesystem-analogclock.png
\o
The Analog Clock example shows how to draw the contents of a
custom widget using QPainter's transformation matrix.
Qt's example directory provides a complete walk-through of the
example. Here, we will only review the example's \l
{QWidget::paintEvent()}{paintEvent()} function to see how we can
use the transformation matrix (i.e. QPainter's matrix functions)
to draw the clock's face.
We recommend compiling and running this example before you read
any further. In particular, try resizing the window to different
sizes.
\row
\o {2,1}
\snippet examples/widgets/analogclock/analogclock.cpp 9
First, we set up the painter. We translate the coordinate system
so that point (0, 0) is in the widget's center, instead of being
at the top-left corner. We also scale the system by \c side / 100,
where \c side is either the widget's width or the height,
whichever is shortest. We want the clock to be square, even if the
device isn't.
This will give us a 200 x 200 square area, with the origin (0, 0)
in the center, that we can draw on. What we draw will show up in
the largest possible square that will fit in the widget.
See also the \l {Window-Viewport Conversion} section.
\snippet examples/widgets/analogclock/analogclock.cpp 18
We draw the clock's hour hand by rotating the coordinate system
and calling QPainter::drawConvexPolygon(). Thank's to the
rotation, it's drawn pointed in the right direction.
The polygon is specified as an array of alternating \e x, \e y
values, stored in the \c hourHand static variable (defined at the
beginning of the function), which corresponds to the four points
(2, 0), (0, 2), (-2, 0), and (0, -25).
The calls to QPainter::save() and QPainter::restore() surrounding
the code guarantees that the code that follows won't be disturbed
by the transformations we've used.
\snippet examples/widgets/analogclock/analogclock.cpp 24
We do the same for the clock's minute hand, which is defined by
the four points (1, 0), (0, 1), (-1, 0), and (0, -40). These
coordinates specify a hand that is thinner and longer than the
minute hand.
\snippet examples/widgets/analogclock/analogclock.cpp 27
Finally, we draw the clock face, which consists of twelve short
lines at 30-degree intervals. At the end of that, the painter is
rotated in a way which isn't very useful, but we're done with
painting so that doesn't matter.
\endtable
For a demonstation of Qt's ability to perform affine
transformations on painting operations, see the \l
{demos/affine}{Affine Transformations} demo which allows the user
to experiment with the transformation operations. See also the \l
{painting/transformations}{Transformations} example which shows
how transformations influence the way that QPainter renders
graphics primitives. In particular, it shows how the order of
transformations affects the result.
For more information about the transformation matrix, see the
QTransform documentation.
\section1 Window-Viewport Conversion
When drawing with QPainter, we specify points using logical
coordinates which then are converted into the physical coordinates
of the paint device.
The mapping of the logical coordinates to the physical coordinates
are handled by QPainter's world transformation \l
{QPainter::worldTransform()}{worldTransform()} (described in the \l
Transformations section), and QPainter's \l
{QPainter::viewport()}{viewport()} and \l
{QPainter::window()}{window()}. The viewport represents the
physical coordinates specifying an arbitrary rectangle. The
"window" describes the same rectangle in logical coordinates. By
default the logical and physical coordinate systems coincide, and
are equivalent to the paint device's rectangle.
Using window-viewport conversion you can make the logical
coordinate system fit your preferences. The mechanism can also be
used to make the drawing code independent of the paint device. You
can, for example, make the logical coordinates extend from (-50,
-50) to (50, 50) with (0, 0) in the center by calling the
QPainter::setWindow() function:
\snippet doc/src/snippets/code/doc_src_coordsys.qdoc 4
Now, the logical coordinates (-50,-50) correspond to the paint
device's physical coordinates (0, 0). Independent of the paint
device, your painting code will always operate on the specified
logical coordinates.
By setting the "window" or viewport rectangle, you perform a
linear transformation of the coordinates. Note that each corner of
the "window" maps to the corresponding corner of the viewport, and
vice versa. For that reason it normally is a good idea to let the
viewport and "window" maintain the same aspect ratio to prevent
deformation:
\snippet doc/src/snippets/code/doc_src_coordsys.qdoc 5
If we make the logical coordinate system a square, we should also
make the viewport a square using the QPainter::setViewport()
function. In the example above we make it equivalent to the
largest square that fit into the paint device's rectangle. By
taking the paint device's size into consideration when setting the
window or viewport, it is possible to keep the drawing code
independent of the paint device.
Note that the window-viewport conversion is only a linear
transformation, i.e. it does not perform clipping. This means that
if you paint outside the currently set "window", your painting is
still transformed to the viewport using the same linear algebraic
approach.
\image coordinatesystem-transformations.png
The viewport, "window" and transformation matrix determine how
logical QPainter coordinates map to the paint device's physical
coordinates. By default the world transformation matrix is the
identity matrix, and the "window" and viewport settings are
equivalent to the paint device's settings, i.e. the world,
"window" and device coordinate systems are equivalent, but as we
have seen, the systems can be manipulated using transformation
operations and window-viewport conversion. The illustration above
describes the process.
\omit
\section1 Related Classes
Qt's paint system, Arthur, is primarily based on the QPainter,
QPaintDevice, and QPaintEngine classes:
\table
\header \o Class \o Description
\row
\o QPainter
\o
The QPainter class performs low-level painting on widgets and
other paint devices. QPainter can operate on any object that
inherits the QPaintDevice class, using the same code.
\row
\o QPaintDevice
\o
The QPaintDevice class is the base class of objects that can be
painted. Qt provides several devices: QWidget, QImage, QPixmap,
QPrinter and QPicture, and other devices can also be defined by
subclassing QPaintDevice.
\row
\o QPaintEngine
\o
The QPaintEngine class provides an abstract definition of how
QPainter draws to a given device on a given platform. Qt 4
provides several premade implementations of QPaintEngine for the
different painter backends we support; it provides one paint
engine for each supported window system and painting
frameworkt. You normally don't need to use this class directly.
\endtable
The 2D transformations of the coordinate system are specified
using the QTransform class:
\table
\header \o Class \o Description
\row
\o QTransform
\o
A 3 x 3 transformation matrix. Use QTransform to rotate, shear,
scale, or translate the coordinate system.
\endtable
In addition Qt provides several graphics primitive classes. Some
of these classes exist in two versions: an \c{int}-based version
and a \c{qreal}-based version. For these, the \c qreal version's
name is suffixed with an \c F.
\table
\header \o Class \o Description
\row
\o \l{QPoint}(\l{QPointF}{F})
\o
A single 2D point in the coordinate system. Most functions in Qt
that deal with points can accept either a QPoint, a QPointF, two
\c{int}s, or two \c{qreal}s.
\row
\o \l{QSize}(\l{QSizeF}{F})
\o
A single 2D vector. Internally, QPoint and QSize are the same, but
a point is not the same as a size, so both classes exist. Again,
most functions accept either QSizeF, a QSize, two \c{int}s, or two
\c{qreal}s.
\row
\o \l{QRect}(\l{QRectF}{F})
\o
A 2D rectangle. Most functions accept either a QRectF, a QRect,
four \c{int}s, or four \c {qreal}s.
\row
\o \l{QLine}(\l{QLineF}{F})
\o
A 2D finite-length line, characterized by a start point and an end
point.
\row
\o \l{QPolygon}(\l{QPolygonF}{F})
\o
A 2D polygon. A polygon is a vector of \c{QPoint(F)}s. If the
first and last points are the same, the polygon is closed.
\row
\o QPainterPath
\o
A vectorial specification of a 2D shape. Painter paths are the
ultimate painting primitive, in the sense that any shape
(rectange, ellipse, spline) or combination of shapes can be
expressed as a path. A path specifies both an outline and an area.
\row
\o QRegion
\o
An area in a paint device, expressed as a list of
\l{QRect}s. In general, we recommend using the vectorial
QPainterPath class instead of QRegion for specifying areas,
because QPainterPath handles painter transformations much better.
\endtable
\endomit
\sa {Analog Clock Example}, {Transformations Example}
*/
|