qtcanvas.cpp
Go to the documentation of this file.
1/****************************************************************************
2**
3** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the Qt Solutions component.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** You may use this file under the terms of the BSD license as follows:
10**
11** "Redistribution and use in source and binary forms, with or without
12** modification, are permitted provided that the following conditions are
13** met:
14** * Redistributions of source code must retain the above copyright
15** notice, this list of conditions and the following disclaimer.
16** * Redistributions in binary form must reproduce the above copyright
17** notice, this list of conditions and the following disclaimer in
18** the documentation and/or other materials provided with the
19** distribution.
20** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
21** of its contributors may be used to endorse or promote products derived
22** from this software without specific prior written permission.
23**
24**
25** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qtcanvas.h"
42
43#include <stdlib.h>
44
45#include <QApplication>
46#include <QBitmap>
47#include <QDesktopWidget>
48#include <QImage>
49#include <QPainter>
50#include <QTimer>
51#include <qalgorithms.h>
52#include <qevent.h>
53#include <qhash.h>
54#include <qpainterpath.h>
55#include <qset.h>
56using namespace Qt;
57
58class QtCanvasData
59{
60public:
62 {
63 }
64
65 QList<QtCanvasView*> viewList;
66 QSet<QtCanvasItem*> itemDict;
67 QSet<QtCanvasItem*> animDict;
68};
69
71{
72public:
74 {
75 }
76
77 QMatrix xform;
78 QMatrix ixform;
79 bool highQuality;
80};
81
82// clusterizer
83
85{
86public:
87 QtCanvasClusterizer(int maxclusters);
89
90 void add(int x, int y); // 1x1 rectangle (point)
91 void add(int x, int y, int w, int h);
92 void add(const QRect& rect);
93
94 void clear();
95
96 int
97 clusters() const
98 {
99 return count;
100 }
101
102 const QRect& operator[](int i) const;
103
104private:
105 QRect* cluster;
106 int count;
107 const int maxcl;
108};
109
110static void
111include(QRect& r, const QRect& rect)
112{
113 if (rect.left() < r.left())
114 {
115 r.setLeft(rect.left());
116 }
117
118 if (rect.right() > r.right())
119 {
120 r.setRight(rect.right());
121 }
122
123 if (rect.top() < r.top())
124 {
125 r.setTop(rect.top());
126 }
127
128 if (rect.bottom() > r.bottom())
129 {
130 r.setBottom(rect.bottom());
131 }
132}
133
134/*
135A QtCanvasClusterizer groups rectangles (QRects) into non-overlapping rectangles
136by a merging heuristic.
137*/
139 cluster(new QRect[maxclusters]), count(0), maxcl(maxclusters)
140{
141}
142
144{
145 delete[] cluster;
146}
147
148void
150{
151 count = 0;
152}
153
154void
155QtCanvasClusterizer::add(int x, int y)
156{
157 add(QRect(x, y, 1, 1));
158}
159
160void
161QtCanvasClusterizer::add(int x, int y, int w, int h)
162{
163 add(QRect(x, y, w, h));
164}
165
166void
167QtCanvasClusterizer::add(const QRect& rect)
168{
169 QRect biggerrect(rect.x() - 1, rect.y() - 1, rect.width() + 2, rect.height() + 2);
170
171 //assert(rect.width()>0 && rect.height()>0);
172
173 int cursor;
174
175 for (cursor = 0; cursor < count; cursor++)
176 {
177 if (cluster[cursor].contains(rect))
178 {
179 // Wholly contained already.
180 return;
181 }
182 }
183
184 int lowestcost = 9999999;
185 int cheapest = -1;
186 cursor = 0;
187
188 while (cursor < count)
189 {
190 if (cluster[cursor].intersects(biggerrect))
191 {
192 QRect larger = cluster[cursor];
193 include(larger, rect);
194 int cost = larger.width() * larger.height() -
195 cluster[cursor].width() * cluster[cursor].height();
196
197 if (cost < lowestcost)
198 {
199 bool bad = false;
200
201 for (int c = 0; c < count && !bad; c++)
202 {
203 bad = cluster[c].intersects(larger) && c != cursor;
204 }
205
206 if (!bad)
207 {
208 cheapest = cursor;
209 lowestcost = cost;
210 }
211 }
212 }
213
214 cursor++;
215 }
216
217 if (cheapest >= 0)
218 {
219 include(cluster[cheapest], rect);
220 return;
221 }
222
223 if (count < maxcl)
224 {
225 cluster[count++] = rect;
226 return;
227 }
228
229 // Do cheapest of:
230 // add to closest cluster
231 // do cheapest cluster merge, add to new cluster
232
233 lowestcost = 9999999;
234 cheapest = -1;
235 cursor = 0;
236
237 while (cursor < count)
238 {
239 QRect larger = cluster[cursor];
240 include(larger, rect);
241 int cost =
242 larger.width() * larger.height() - cluster[cursor].width() * cluster[cursor].height();
243
244 if (cost < lowestcost)
245 {
246 bool bad = false;
247
248 for (int c = 0; c < count && !bad; c++)
249 {
250 bad = cluster[c].intersects(larger) && c != cursor;
251 }
252
253 if (!bad)
254 {
255 cheapest = cursor;
256 lowestcost = cost;
257 }
258 }
259
260 cursor++;
261 }
262
263 // ###
264 // could make an heuristic guess as to whether we need to bother
265 // looking for a cheap merge.
266
267 int cheapestmerge1 = -1;
268 int cheapestmerge2 = -1;
269
270 int merge1 = 0;
271
272 while (merge1 < count)
273 {
274 int merge2 = 0;
275
276 while (merge2 < count)
277 {
278 if (merge1 != merge2)
279 {
280 QRect larger = cluster[merge1];
281 include(larger, cluster[merge2]);
282 int cost = larger.width() * larger.height() -
283 cluster[merge1].width() * cluster[merge1].height() -
284 cluster[merge2].width() * cluster[merge2].height();
285
286 if (cost < lowestcost)
287 {
288 bool bad = false;
289
290 for (int c = 0; c < count && !bad; c++)
291 {
292 bad = cluster[c].intersects(larger) && c != cursor;
293 }
294
295 if (!bad)
296 {
297 cheapestmerge1 = merge1;
298 cheapestmerge2 = merge2;
299 lowestcost = cost;
300 }
301 }
302 }
303
304 merge2++;
305 }
306
307 merge1++;
308 }
309
310 if (cheapestmerge1 >= 0)
311 {
312 include(cluster[cheapestmerge1], cluster[cheapestmerge2]);
313 cluster[cheapestmerge2] = cluster[count--];
314 }
315 else
316 {
317 // if (!cheapest) debugRectangles(rect);
318 include(cluster[cheapest], rect);
319 }
320
321 // NB: clusters do not intersect (or intersection will
322 // overwrite). This is a result of the above algorithm,
323 // given the assumption that (x, y) are ordered topleft
324 // to bottomright.
325
326 // ###
327 //
328 // add explicit x/y ordering to that comment, move it to the top
329 // and rephrase it as pre-/post-conditions.
330}
331
332const QRect&
334{
335 return cluster[i];
336}
337
338// end of clusterizer
339
340
342{
343public:
344 inline bool
345 operator()(const QtCanvasItem* i1, const QtCanvasItem* i2) const
346 {
347 if (i1->z() == i2->z())
348 {
349 return i1 > i2;
350 }
351
352 return (i1->z() > i2->z());
353 }
354};
355
356class QtCanvasChunk
357{
358public:
359 QtCanvasChunk() : changed(true)
360 {
361 }
362
363 // Other code assumes lists are not deleted. Assignment is also
364 // done on ChunkRecs. So don't add that sort of thing here.
365
366 void
368 {
369 qSort(m_list.begin(), m_list.end(), QtCanvasItemLess());
370 }
371
372 const QtCanvasItemList&
373 list() const
374 {
375 return m_list;
376 }
377
378 void
380 {
381 m_list.prepend(item);
382 changed = true;
383 }
384
385 void
387 {
388 m_list.removeAll(item);
389 changed = true;
390 }
391
392 void
394 {
395 changed = true;
396 }
397
398 bool
400 {
401 return changed;
402 }
403
404 bool
406 {
407 bool y = changed;
408 changed = false;
409 return y;
410 }
411
412private:
413 QtCanvasItemList m_list;
414 bool changed;
415};
416
417static int
418gcd(int a, int b)
419{
420 int r;
421
422 while ((r = a % b))
423 {
424 a = b;
425 b = r;
426 }
427
428 return b;
429}
430
431static int
432scm(int a, int b)
433{
434 int g = gcd(a, b);
435 return a / g * b;
436}
437
438/*
439 \class QtCanvas qtcanvas.h
440 \brief The QtCanvas class provides a 2D area that can contain QtCanvasItem objects.
441
442 The QtCanvas class manages its 2D graphic area and all the canvas
443 items the area contains. The canvas has no visual appearance of
444 its own. Instead, it is displayed on screen using a QtCanvasView.
445 Multiple QtCanvasView widgets may be associated with a canvas to
446 provide multiple views of the same canvas.
447
448 The canvas is optimized for large numbers of items, particularly
449 where only a small percentage of the items change at any
450 one time. If the entire display changes very frequently, you should
451 consider using your own custom QtScrollView subclass.
452
453 Qt provides a rich
454 set of canvas item classes, e.g. QtCanvasEllipse, QtCanvasLine,
455 QtCanvasPolygon, QtCanvasPolygonalItem, QtCanvasRectangle, QtCanvasSpline,
456 QtCanvasSprite and QtCanvasText. You can subclass to create your own
457 canvas items; QtCanvasPolygonalItem is the most common base class used
458 for this purpose.
459
460 Items appear on the canvas after their \link QtCanvasItem::show()
461 show()\endlink function has been called (or \link
462 QtCanvasItem::setVisible() setVisible(true)\endlink), and \e after
463 update() has been called. The canvas only shows items that are
464 \link QtCanvasItem::setVisible() visible\endlink, and then only if
465 \l update() is called. (By default the canvas is white and so are
466 canvas items, so if nothing appears try changing colors.)
467
468 If you created the canvas without passing a width and height to
469 the constructor you must also call resize().
470
471 Although a canvas may appear to be similar to a widget with child
472 widgets, there are several notable differences:
473
474 \list
475 \i Canvas items are usually much faster to manipulate and redraw than
476 child widgets, with the speed advantage becoming especially great when
477 there are \e many canvas items and non-rectangular items. In most
478 situations canvas items are also a lot more memory efficient than child
479 widgets.
480
481 \i It's easy to detect overlapping items (collision detection).
482
483 \i The canvas can be larger than a widget. A million-by-million canvas
484 is perfectly possible. At such a size a widget might be very
485 inefficient, and some window systems might not support it at all,
486 whereas QtCanvas scales well. Even with a billion pixels and a million
487 items, finding a particular canvas item, detecting collisions, etc.,
488 is still fast (though the memory consumption may be prohibitive
489 at such extremes).
490
491 \i Two or more QtCanvasView objects can view the same canvas.
492
493 \i An arbitrary transformation matrix can be set on each QtCanvasView
494 which makes it easy to zoom, rotate or shear the viewed canvas.
495
496 \i Widgets provide a lot more functionality, such as input (QKeyEvent,
497 QMouseEvent etc.) and layout management (QGridLayout etc.).
498
499 \endlist
500
501 A canvas consists of a background, a number of canvas items organized by
502 x, y and z coordinates, and a foreground. A canvas item's z coordinate
503 can be treated as a layer number -- canvas items with a higher z
504 coordinate appear in front of canvas items with a lower z coordinate.
505
506 The background is white by default, but can be set to a different color
507 using setBackgroundColor(), or to a repeated pixmap using
508 setBackgroundPixmap() or to a mosaic of smaller pixmaps using
509 setTiles(). Individual tiles can be set with setTile(). There
510 are corresponding get functions, e.g. backgroundColor() and
511 backgroundPixmap().
512
513 Note that QtCanvas does not inherit from QWidget, even though it has some
514 functions which provide the same functionality as those in QWidget. One
515 of these is setBackgroundPixmap(); some others are resize(), size(),
516 width() and height(). \l QtCanvasView is the widget used to display a
517 canvas on the screen.
518
519 Canvas items are added to a canvas by constructing them and passing the
520 canvas to the canvas item's constructor. An item can be moved to a
521 different canvas using QtCanvasItem::setCanvas().
522
523 Canvas items are movable (and in the case of QtCanvasSprites, animated)
524 objects that inherit QtCanvasItem. Each canvas item has a position on the
525 canvas (x, y coordinates) and a height (z coordinate), all of which are
526 held as floating-point numbers. Moving canvas items also have x and y
527 velocities. It's possible for a canvas item to be outside the canvas
528 (for example QtCanvasItem::x() is greater than width()). When a canvas
529 item is off the canvas, onCanvas() returns false and the canvas
530 disregards the item. (Canvas items off the canvas do not slow down any
531 of the common operations on the canvas.)
532
533 Canvas items can be moved with QtCanvasItem::move(). The advance()
534 function moves all QtCanvasItem::animated() canvas items and
535 setAdvancePeriod() makes QtCanvas move them automatically on a periodic
536 basis. In the context of the QtCanvas classes, to `animate' a canvas item
537 is to set it in motion, i.e. using QtCanvasItem::setVelocity(). Animation
538 of a canvas item itself, i.e. items which change over time, is enabled
539 by calling QtCanvasSprite::setFrameAnimation(), or more generally by
540 subclassing and reimplementing QtCanvasItem::advance(). To detect collisions
541 use one of the QtCanvasItem::collisions() functions.
542
543 The changed parts of the canvas are redrawn (if they are visible in a
544 canvas view) whenever update() is called. You can either call update()
545 manually after having changed the contents of the canvas, or force
546 periodic updates using setUpdatePeriod(). If you have moving objects on
547 the canvas, you must call advance() every time the objects should
548 move one step further. Periodic calls to advance() can be forced using
549 setAdvancePeriod(). The advance() function will call
550 QtCanvasItem::advance() on every item that is \link
551 QtCanvasItem::animated() animated\endlink and trigger an update of the
552 affected areas afterwards. (A canvas item that is `animated' is simply
553 a canvas item that is in motion.)
554
555 QtCanvas organizes its canvas items into \e chunks; these are areas on
556 the canvas that are used to speed up most operations. Many operations
557 start by eliminating most chunks (i.e. those which haven't changed)
558 and then process only the canvas items that are in the few interesting
559 (i.e. changed) chunks. A valid chunk, validChunk(), is one which is on
560 the canvas.
561
562 The chunk size is a key factor to QtCanvas's speed: if there are too many
563 chunks, the speed benefit of grouping canvas items into chunks is
564 reduced. If the chunks are too large, it takes too long to process each
565 one. The QtCanvas constructor tries to pick a suitable size, but you
566 can call retune() to change it at any time. The chunkSize() function
567 returns the current chunk size. The canvas items always make sure
568 they're in the right chunks; all you need to make sure of is that
569 the canvas uses the right chunk size. A good rule of thumb is that
570 the size should be a bit smaller than the average canvas item
571 size. If you have moving objects, the chunk size should be a bit
572 smaller than the average size of the moving items.
573
574 The foreground is normally nothing, but if you reimplement
575 drawForeground(), you can draw things in front of all the canvas
576 items.
577
578 Areas can be set as changed with setChanged() and set unchanged with
579 setUnchanged(). The entire canvas can be set as changed with
580 setAllChanged(). A list of all the items on the canvas is returned by
581 allItems().
582
583 An area can be copied (painted) to a QPainter with drawArea().
584
585 If the canvas is resized it emits the resized() signal.
586
587 The examples/canvas application and the 2D graphics page of the
588 examples/demo application demonstrate many of QtCanvas's facilities.
589
590 \sa QtCanvasView QtCanvasItem
591*/
592void
593QtCanvas::init(int w, int h, int chunksze, int mxclusters)
594{
595 d = new QtCanvasData;
596 awidth = w;
597 aheight = h;
598 chunksize = chunksze;
599 maxclusters = mxclusters;
600 chwidth = (w + chunksize - 1) / chunksize;
601 chheight = (h + chunksize - 1) / chunksize;
602 chunks = new QtCanvasChunk[chwidth * chheight];
603 update_timer = 0;
604 bgcolor = white;
605 grid = 0;
606 htiles = 0;
607 vtiles = 0;
608 debug_redraw_areas = false;
609}
610
611/*
612 Create a QtCanvas with no size. \a parent is passed to the QObject
613 superclass.
614
615 \warning You \e must call resize() at some time after creation to
616 be able to use the canvas.
617*/
618QtCanvas::QtCanvas(QObject* parent) : QObject(parent)
619{
620 init(0, 0);
621}
622
623/*
624 Constructs a QtCanvas that is \a w pixels wide and \a h pixels high.
625*/
626QtCanvas::QtCanvas(int w, int h)
627{
628 init(w, h);
629}
630
631/*
632 Constructs a QtCanvas which will be composed of \a h tiles
633 horizontally and \a v tiles vertically. Each tile will be an image
634 \a tilewidth by \a tileheight pixels taken from pixmap \a p.
635
636 The pixmap \a p is a list of tiles, arranged left to right, (and
637 in the case of pixmaps that have multiple rows of tiles, top to
638 bottom), with tile 0 in the top-left corner, tile 1 next to the
639 right, and so on, e.g.
640
641 \table
642 \row \i 0 \i 1 \i 2 \i 3
643 \row \i 4 \i 5 \i 6 \i 7
644 \endtable
645
646 The QtCanvas is initially sized to show exactly the given number of
647 tiles horizontally and vertically. If it is resized to be larger,
648 the entire matrix of tiles will be repeated as often as necessary
649 to cover the area. If it is smaller, tiles to the right and bottom
650 will not be visible.
651
652 \sa setTiles()
653*/
654QtCanvas::QtCanvas(QPixmap p, int h, int v, int tilewidth, int tileheight)
655{
656 init(h * tilewidth, v * tileheight, scm(tilewidth, tileheight));
657 setTiles(p, h, v, tilewidth, tileheight);
658}
659
660/*
661 Destroys the canvas and all the canvas's canvas items.
662*/
664{
665 for (int i = 0; i < d->viewList.size(); ++i)
666 {
667 d->viewList[i]->viewing = 0;
668 }
669
671
672 for (QtCanvasItemList::Iterator it = all.begin(); it != all.end(); ++it)
673 {
674 delete *it;
675 }
676
677 delete[] chunks;
678 delete[] grid;
679 delete d;
680}
681
682/*
683\internal
684Returns the chunk at a chunk position \a i, \a j.
685*/
687QtCanvas::chunk(int i, int j) const
688{
689 return chunks[i + chwidth * j];
690}
691
692/*
693\internal
694Returns the chunk at a pixel position \a x, \a y.
695*/
697QtCanvas::chunkContaining(int x, int y) const
698{
699 return chunk(x / chunksize, y / chunksize);
700}
701
702/*
703 Returns a list of all the items in the canvas.
704*/
707{
708 return d->itemDict.toList();
709}
710
711/*
712 Changes the size of the canvas to have a width of \a w and a
713 height of \a h. This is a slow operation.
714*/
715void
716QtCanvas::resize(int w, int h)
717{
718 if (awidth == w && aheight == h)
719 {
720 return;
721 }
722
723 QList<QtCanvasItem*> hidden;
724
725 for (QSet<QtCanvasItem*>::const_iterator it = d->itemDict.begin(); it != d->itemDict.end();
726 ++it)
727 {
728 if ((*it)->isVisible())
729 {
730 (*it)->hide();
731 hidden.append(*it);
732 }
733 }
734
735 int nchwidth = (w + chunksize - 1) / chunksize;
736 int nchheight = (h + chunksize - 1) / chunksize;
737
738 QtCanvasChunk* newchunks = new QtCanvasChunk[nchwidth * nchheight];
739
740 // Commit the new values.
741 //
742 awidth = w;
743 aheight = h;
744 chwidth = nchwidth;
745 chheight = nchheight;
746 delete[] chunks;
747 chunks = newchunks;
748
749 for (int i = 0; i < hidden.size(); ++i)
750 {
751 hidden.at(i)->show();
752 }
753
755
756 emit resized();
757}
758
759/*
760 \fn void QtCanvas::resized()
761
762 This signal is emitted whenever the canvas is resized. Each
763 QtCanvasView connects to this signal to keep the scrollview's size
764 correct.
765*/
766
767/*
768 Change the efficiency tuning parameters to \a mxclusters clusters,
769 each of size \a chunksze. This is a slow operation if there are
770 many objects on the canvas.
771
772 The canvas is divided into chunks which are rectangular areas \a
773 chunksze wide by \a chunksze high. Use a chunk size which is about
774 the average size of the canvas items. If you choose a chunk size
775 which is too small it will increase the amount of calculation
776 required when drawing since each change will affect many chunks.
777 If you choose a chunk size which is too large the amount of
778 drawing required will increase because for each change, a lot of
779 drawing will be required since there will be many (unchanged)
780 canvas items which are in the same chunk as the changed canvas
781 items.
782
783 Internally, a canvas uses a low-resolution "chunk matrix" to keep
784 track of all the items in the canvas. A 64x64 chunk matrix is the
785 default for a 1024x1024 pixel canvas, where each chunk collects
786 canvas items in a 16x16 pixel square. This default is also
787 affected by setTiles(). You can tune this default using this
788 function. For example if you have a very large canvas and want to
789 trade off speed for memory then you might set the chunk size to 32
790 or 64.
791
792 The \a mxclusters argument is the number of rectangular groups of
793 chunks that will be separately drawn. If the canvas has a large
794 number of small, dispersed items, this should be about that
795 number. Our testing suggests that a large number of clusters is
796 almost always best.
797
798*/
799void
800QtCanvas::retune(int chunksze, int mxclusters)
801{
802 maxclusters = mxclusters;
803
804 if (chunksize != chunksze)
805 {
806 QList<QtCanvasItem*> hidden;
807
808 for (QSet<QtCanvasItem*>::const_iterator it = d->itemDict.begin(); it != d->itemDict.end();
809 ++it)
810 {
811 if ((*it)->isVisible())
812 {
813 (*it)->hide();
814 hidden.append(*it);
815 }
816 }
817
818 chunksize = chunksze;
819
820 int nchwidth = (awidth + chunksize - 1) / chunksize;
821 int nchheight = (aheight + chunksize - 1) / chunksize;
822
823 QtCanvasChunk* newchunks = new QtCanvasChunk[nchwidth * nchheight];
824
825 // Commit the new values.
826 //
827 chwidth = nchwidth;
828 chheight = nchheight;
829 delete[] chunks;
830 chunks = newchunks;
831
832 for (int i = 0; i < hidden.size(); ++i)
833 {
834 hidden.at(i)->show();
835 }
836 }
837}
838
839/*
840 \fn int QtCanvas::width() const
841
842 Returns the width of the canvas, in pixels.
843*/
844
845/*
846 \fn int QtCanvas::height() const
847
848 Returns the height of the canvas, in pixels.
849*/
850
851/*
852 \fn QSize QtCanvas::size() const
853
854 Returns the size of the canvas, in pixels.
855*/
856
857/*
858 \fn QRect QtCanvas::rect() const
859
860 Returns a rectangle the size of the canvas.
861*/
862
863
864/*
865 \fn bool QtCanvas::onCanvas(int x, int y) const
866
867 Returns true if the pixel position (\a x, \a y) is on the canvas;
868 otherwise returns false.
869
870 \sa validChunk()
871*/
872
873/*
874 \fn bool QtCanvas::onCanvas(const QPoint& p) const
875 \overload
876
877 Returns true if the pixel position \a p is on the canvas;
878 otherwise returns false.
879
880 \sa validChunk()
881*/
882
883/*
884 \fn bool QtCanvas::validChunk(int x, int y) const
885
886 Returns true if the chunk position (\a x, \a y) is on the canvas;
887 otherwise returns false.
888
889 \sa onCanvas()
890*/
891
892/*
893 \fn bool QtCanvas::validChunk(const QPoint& p) const
894 \overload
895
896 Returns true if the chunk position \a p is on the canvas; otherwise
897 returns false.
898
899 \sa onCanvas()
900*/
901
902/*
903 \fn int QtCanvas::chunkSize() const
904
905 Returns the chunk size of the canvas.
906
907 \sa retune()
908*/
909
910/*
911\fn bool QtCanvas::sameChunk(int x1, int y1, int x2, int y2) const
912\internal
913Tells if the points (\a x1, \a y1) and (\a x2, \a y2) are within the same chunk.
914*/
915
916/*
917\internal
918This method adds an the item \a item to the list of QtCanvasItem objects
919in the QtCanvas. The QtCanvasItem class calls this.
920*/
921void
923{
924 d->itemDict.insert(item);
925}
926
927/*
928\internal
929This method adds the item \a item to the list of QtCanvasItem objects
930to be moved. The QtCanvasItem class calls this.
931*/
932void
934{
935 d->animDict.insert(item);
936}
937
938/*
939\internal
940This method adds the item \a item to the list of QtCanvasItem objects
941which are no longer to be moved. The QtCanvasItem class calls this.
942*/
943void
945{
946 d->animDict.remove(item);
947}
948
949/*
950\internal
951This method removes the item \a item from the list of QtCanvasItem objects
952in this QtCanvas. The QtCanvasItem class calls this.
953*/
954void
956{
957 d->itemDict.remove(item);
958}
959
960/*
961\internal
962This method adds the view \a view to the list of QtCanvasView objects
963viewing this QtCanvas. The QtCanvasView class calls this.
964*/
965void
967{
968 d->viewList.append(view);
969
970 if (htiles > 1 || vtiles > 1 || pm.isNull())
971 {
972 QPalette::ColorRole role = view->widget()->backgroundRole();
973 QPalette viewPalette = view->widget()->palette();
974 viewPalette.setColor(role, backgroundColor());
975 view->widget()->setPalette(viewPalette);
976 }
977}
978
979/*
980\internal
981This method removes the view \a view from the list of QtCanvasView objects
982viewing this QtCanvas. The QtCanvasView class calls this.
983*/
984void
986{
987 d->viewList.removeAll(view);
988}
989
990/*
991 Sets the canvas to call advance() every \a ms milliseconds. Any
992 previous setting by setAdvancePeriod() or setUpdatePeriod() is
993 overridden.
994
995 If \a ms is less than 0 advancing will be stopped.
996*/
997void
999{
1000 if (ms < 0)
1001 {
1002 if (update_timer)
1003 {
1004 update_timer->stop();
1005 }
1006 }
1007 else
1008 {
1009 if (update_timer)
1010 {
1011 delete update_timer;
1012 }
1013
1014 update_timer = new QTimer(this);
1015 connect(update_timer, SIGNAL(timeout()), this, SLOT(advance()));
1016 update_timer->start(ms);
1017 }
1018}
1019
1020/*
1021 Sets the canvas to call update() every \a ms milliseconds. Any
1022 previous setting by setAdvancePeriod() or setUpdatePeriod() is
1023 overridden.
1024
1025 If \a ms is less than 0 automatic updating will be stopped.
1026*/
1027void
1029{
1030 if (ms < 0)
1031 {
1032 if (update_timer)
1033 {
1034 update_timer->stop();
1035 }
1036 }
1037 else
1038 {
1039 if (update_timer)
1040 {
1041 delete update_timer;
1042 }
1043
1044 update_timer = new QTimer(this);
1045 connect(update_timer, SIGNAL(timeout()), this, SLOT(update()));
1046 update_timer->start(ms);
1047 }
1048}
1049
1050/*
1051 Moves all QtCanvasItem::animated() canvas items on the canvas and
1052 refreshes all changes to all views of the canvas. (An `animated'
1053 item is an item that is in motion; see setVelocity().)
1054
1055 The advance takes place in two phases. In phase 0, the
1056 QtCanvasItem::advance() function of each QtCanvasItem::animated()
1057 canvas item is called with paramater 0. Then all these canvas
1058 items are called again, with parameter 1. In phase 0, the canvas
1059 items should not change position, merely examine other items on
1060 the canvas for which special processing is required, such as
1061 collisions between items. In phase 1, all canvas items should
1062 change positions, ignoring any other items on the canvas. This
1063 two-phase approach allows for considerations of "fairness",
1064 although no QtCanvasItem subclasses supplied with Qt do anything
1065 interesting in phase 0.
1066
1067 The canvas can be configured to call this function periodically
1068 with setAdvancePeriod().
1069
1070 \sa update()
1071*/
1072void
1074{
1075 QSetIterator<QtCanvasItem*> it = d->animDict;
1076
1077 while (it.hasNext())
1078 {
1079 QtCanvasItem* i = it.next();
1080
1081 if (i)
1082 {
1083 i->advance(0);
1084 }
1085 }
1086
1087 // we expect the dict contains the exact same items as in the
1088 // first pass.
1089 it.toFront();
1090
1091 while (it.hasNext())
1092 {
1093 QtCanvasItem* i = it.next();
1094
1095 if (i)
1096 {
1097 i->advance(1);
1098 }
1099 }
1100
1101 update();
1102}
1103
1104// Don't call this unless you know what you're doing.
1105// p is in the content's co-ordinate example.
1106/*
1107 \internal
1108*/
1109void
1110QtCanvas::drawViewArea(QtCanvasView* view, QPainter* p, const QRect& vr, bool)
1111{
1112 QMatrix wm = view->worldMatrix();
1113 QMatrix iwm = wm.inverted();
1114 // ivr = covers all chunks in vr
1115 QRect ivr = iwm.mapRect(vr);
1116
1117 p->setMatrix(wm);
1118 drawCanvasArea(ivr, p, false);
1119}
1120
1121/*
1122 Repaints changed areas in all views of the canvas.
1123
1124 \sa advance()
1125*/
1126void
1128{
1129 QRect r = changeBounds();
1130
1131 for (int i = 0; i < d->viewList.size(); ++i)
1132 {
1133 QtCanvasView* view = d->viewList.at(i);
1134
1135 if (!r.isEmpty())
1136 {
1137 QRect tr = view->worldMatrix().mapRect(r);
1138 view->widget()->update(tr);
1139 }
1140 }
1141
1142 setUnchanged(r);
1143}
1144
1145/*
1146 Marks the whole canvas as changed.
1147 All views of the canvas will be entirely redrawn when
1148 update() is called next.
1149*/
1150void
1152{
1153 setChanged(QRect(0, 0, width(), height()));
1154}
1155
1156/*
1157 Marks \a area as changed. This \a area will be redrawn in all
1158 views that are showing it when update() is called next.
1159*/
1160void
1161QtCanvas::setChanged(const QRect& area)
1162{
1163 QRect thearea = area.intersected(QRect(0, 0, width(), height()));
1164
1165 int mx = (thearea.x() + thearea.width() + chunksize) / chunksize;
1166 int my = (thearea.y() + thearea.height() + chunksize) / chunksize;
1167
1168 if (mx > chwidth)
1169 {
1170 mx = chwidth;
1171 }
1172
1173 if (my > chheight)
1174 {
1175 my = chheight;
1176 }
1177
1178 int x = thearea.x() / chunksize;
1179
1180 while (x < mx)
1181 {
1182 int y = thearea.y() / chunksize;
1183
1184 while (y < my)
1185 {
1186 chunk(x, y).change();
1187 y++;
1188 }
1189
1190 x++;
1191 }
1192}
1193
1194/*
1195 Marks \a area as \e unchanged. The area will \e not be redrawn in
1196 the views for the next update(), unless it is marked or changed
1197 again before the next call to update().
1198*/
1199void
1200QtCanvas::setUnchanged(const QRect& area)
1201{
1202 QRect thearea = area.intersected(QRect(0, 0, width(), height()));
1203
1204 int mx = (thearea.x() + thearea.width() + chunksize) / chunksize;
1205 int my = (thearea.y() + thearea.height() + chunksize) / chunksize;
1206
1207 if (mx > chwidth)
1208 {
1209 mx = chwidth;
1210 }
1211
1212 if (my > chheight)
1213 {
1214 my = chheight;
1215 }
1216
1217 int x = thearea.x() / chunksize;
1218
1219 while (x < mx)
1220 {
1221 int y = thearea.y() / chunksize;
1222
1223 while (y < my)
1224 {
1225 chunk(x, y).takeChange();
1226 y++;
1227 }
1228
1229 x++;
1230 }
1231}
1232
1233/*
1234 \internal
1235*/
1236QRect
1237QtCanvas::changeBounds()
1238{
1239 QRect area = QRect(0, 0, width(), height());
1240
1241 int mx = (area.x() + area.width() + chunksize) / chunksize;
1242 int my = (area.y() + area.height() + chunksize) / chunksize;
1243
1244 if (mx > chwidth)
1245 {
1246 mx = chwidth;
1247 }
1248
1249 if (my > chheight)
1250 {
1251 my = chheight;
1252 }
1253
1254 QRect result;
1255
1256 int x = area.x() / chunksize;
1257
1258 while (x < mx)
1259 {
1260 int y = area.y() / chunksize;
1261
1262 while (y < my)
1263 {
1264 QtCanvasChunk& ch = chunk(x, y);
1265
1266 if (ch.hasChanged())
1267 {
1268 result |= QRect(x * chunksize, y * chunksize, chunksize + 1, chunksize + 1);
1269 }
1270
1271 y++;
1272 }
1273
1274 x++;
1275 }
1276
1277 return result;
1278}
1279
1280/*
1281 Paints all canvas items that are in the area \a clip to \a
1282 painter, using double-buffering if \a dbuf is true.
1283
1284 e.g. to print the canvas to a printer:
1285 \code
1286 QPrinter pr;
1287 if (pr.setup()) {
1288 QPainter p(&pr);
1289 canvas.drawArea(canvas.rect(), &p);
1290 }
1291 \endcode
1292*/
1293void
1294QtCanvas::drawArea(const QRect& clip, QPainter* painter, bool dbuf)
1295{
1296 if (painter)
1297 {
1298 drawCanvasArea(clip, painter, dbuf);
1299 }
1300}
1301
1302#include <QDebug>
1303
1304/*
1305 \internal
1306*/
1307void
1308QtCanvas::drawCanvasArea(const QRect& inarea, QPainter* p, bool /*double_buffer*/)
1309{
1310 QRect area = inarea.intersected(QRect(0, 0, width(), height()));
1311
1312 if (!p)
1313 {
1314 return; // Nothing to do.
1315 }
1316
1317 int lx = area.x() / chunksize;
1318 int ly = area.y() / chunksize;
1319 int mx = area.right() / chunksize;
1320 int my = area.bottom() / chunksize;
1321
1322 if (mx >= chwidth)
1323 {
1324 mx = chwidth - 1;
1325 }
1326
1327 if (my >= chheight)
1328 {
1329 my = chheight - 1;
1330 }
1331
1332 QtCanvasItemList allvisible;
1333
1334 // Stores the region within area that need to be drawn. It is relative
1335 // to area.topLeft() (so as to keep within bounds of 16-bit XRegions)
1336 QRegion rgn;
1337
1338 for (int x = lx; x <= mx; x++)
1339 {
1340 for (int y = ly; y <= my; y++)
1341 {
1342 // Only reset change if all views updating, and
1343 // wholy within area. (conservative: ignore entire boundary)
1344 //
1345 // Disable this to help debugging.
1346 //
1347 if (!p)
1348 {
1349 if (chunk(x, y).takeChange())
1350 {
1351 // ### should at least make bands
1352 rgn |= QRegion(
1353 x * chunksize - area.x(), y * chunksize - area.y(), chunksize, chunksize);
1354 allvisible += chunk(x, y).list();
1355 }
1356 }
1357 else
1358 {
1359 allvisible += chunk(x, y).list();
1360 }
1361 }
1362 }
1363
1364 qSort(allvisible.begin(), allvisible.end(), QtCanvasItemLess());
1365
1366 drawBackground(*p, area);
1367
1368 if (!allvisible.isEmpty())
1369 {
1370 QtCanvasItem* prev = 0;
1371
1372 for (int i = allvisible.size() - 1; i >= 0; --i)
1373 {
1374 QtCanvasItem* g = allvisible[i];
1375
1376 if (g != prev)
1377 {
1378 g->draw(*p);
1379 prev = g;
1380 }
1381 }
1382 }
1383
1384 drawForeground(*p, area);
1385}
1386
1387/*
1388\internal
1389This method to informs the QtCanvas that a given chunk is
1390`dirty' and needs to be redrawn in the next Update.
1391
1392(\a x, \a y) is a chunk location.
1393
1394The sprite classes call this. Any new derived class of QtCanvasItem
1395must do so too. SetChangedChunkContaining can be used instead.
1396*/
1397void
1398QtCanvas::setChangedChunk(int x, int y)
1399{
1400 if (validChunk(x, y))
1401 {
1402 QtCanvasChunk& ch = chunk(x, y);
1403 ch.change();
1404 }
1405}
1406
1407/*
1408\internal
1409This method to informs the QtCanvas that the chunk containing a given
1410pixel is `dirty' and needs to be redrawn in the next Update.
1411
1412(\a x, \a y) is a pixel location.
1413
1414The item classes call this. Any new derived class of QtCanvasItem must
1415do so too. SetChangedChunk can be used instead.
1416*/
1417void
1419{
1420 if (x >= 0 && x < width() && y >= 0 && y < height())
1421 {
1422 QtCanvasChunk& chunk = chunkContaining(x, y);
1423 chunk.change();
1424 }
1425}
1426
1427/*
1428\internal
1429This method adds the QtCanvasItem \a g to the list of those which need to be
1430drawn if the given chunk at location (\a x, \a y) is redrawn. Like
1431SetChangedChunk and SetChangedChunkContaining, this method marks the
1432chunk as `dirty'.
1433*/
1434void
1436{
1437 if (validChunk(x, y))
1438 {
1439 chunk(x, y).add(g);
1440 }
1441}
1442
1443/*
1444\internal
1445This method removes the QtCanvasItem \a g from the list of those which need to
1446be drawn if the given chunk at location (\a x, \a y) is redrawn. Like
1447SetChangedChunk and SetChangedChunkContaining, this method marks the chunk
1448as `dirty'.
1449*/
1450void
1452{
1453 if (validChunk(x, y))
1454 {
1455 chunk(x, y).remove(g);
1456 }
1457}
1458
1459/*
1460\internal
1461This method adds the QtCanvasItem \a g to the list of those which need to be
1462drawn if the chunk containing the given pixel (\a x, \a y) is redrawn. Like
1463SetChangedChunk and SetChangedChunkContaining, this method marks the
1464chunk as `dirty'.
1465*/
1466void
1468{
1469 if (x >= 0 && x < width() && y >= 0 && y < height())
1470 {
1471 chunkContaining(x, y).add(g);
1472 }
1473}
1474
1475/*
1476\internal
1477This method removes the QtCanvasItem \a g from the list of those which need to
1478be drawn if the chunk containing the given pixel (\a x, \a y) is redrawn.
1479Like SetChangedChunk and SetChangedChunkContaining, this method
1480marks the chunk as `dirty'.
1481*/
1482void
1484{
1485 if (x >= 0 && x < width() && y >= 0 && y < height())
1486 {
1487 chunkContaining(x, y).remove(g);
1488 }
1489}
1490
1491/*
1492 Returns the color set by setBackgroundColor(). By default, this is
1493 white.
1494
1495 This function is not a reimplementation of
1496 QWidget::backgroundColor() (QtCanvas is not a subclass of QWidget),
1497 but all QtCanvasViews that are viewing the canvas will set their
1498 backgrounds to this color.
1499
1500 \sa setBackgroundColor(), backgroundPixmap()
1501*/
1502QColor
1504{
1505 return bgcolor;
1506}
1507
1508/*
1509 Sets the solid background to be the color \a c.
1510
1511 \sa backgroundColor(), setBackgroundPixmap(), setTiles()
1512*/
1513void
1514QtCanvas::setBackgroundColor(const QColor& c)
1515{
1516 if (bgcolor != c)
1517 {
1518 bgcolor = c;
1519
1520 for (int i = 0; i < d->viewList.size(); ++i)
1521 {
1522 QtCanvasView* view = d->viewList.at(i);
1523 QPalette::ColorRole role = view->widget()->backgroundRole();
1524 QPalette viewPalette = view->widget()->palette();
1525 viewPalette.setColor(role, bgcolor);
1526 view->widget()->setPalette(viewPalette);
1527 }
1528
1529 setAllChanged();
1530 }
1531}
1532
1533/*
1534 Returns the pixmap set by setBackgroundPixmap(). By default,
1535 this is a null pixmap.
1536
1537 \sa setBackgroundPixmap(), backgroundColor()
1538*/
1539QPixmap
1541{
1542 return pm;
1543}
1544
1545/*
1546 Sets the solid background to be the pixmap \a p repeated as
1547 necessary to cover the entire canvas.
1548
1549 \sa backgroundPixmap(), setBackgroundColor(), setTiles()
1550*/
1551void
1552QtCanvas::setBackgroundPixmap(const QPixmap& p)
1553{
1554 setTiles(p, 1, 1, p.width(), p.height());
1555
1556 for (int i = 0; i < d->viewList.size(); ++i)
1557 {
1558 QtCanvasView* view = d->viewList.at(i);
1559 view->widget()->update();
1560 }
1561}
1562
1563/*
1564 This virtual function is called for all updates of the canvas. It
1565 renders any background graphics using the painter \a painter, in
1566 the area \a clip. If the canvas has a background pixmap or a tiled
1567 background, that graphic is used, otherwise the canvas is cleared
1568 using the background color.
1569
1570 If the graphics for an area change, you must explicitly call
1571 setChanged(const QRect&) for the result to be visible when
1572 update() is next called.
1573
1574 \sa setBackgroundColor(), setBackgroundPixmap(), setTiles()
1575*/
1576void
1577QtCanvas::drawBackground(QPainter& painter, const QRect& clip)
1578{
1579 if (pm.isNull())
1580 {
1581 painter.fillRect(clip, bgcolor);
1582 }
1583 else if (!grid)
1584 {
1585 for (int x = clip.x() / pm.width();
1586 x < (clip.x() + clip.width() + pm.width() - 1) / pm.width();
1587 x++)
1588 {
1589 for (int y = clip.y() / pm.height();
1590 y < (clip.y() + clip.height() + pm.height() - 1) / pm.height();
1591 y++)
1592 {
1593 painter.drawPixmap(x * pm.width(), y * pm.height(), pm);
1594 }
1595 }
1596 }
1597 else
1598 {
1599 const int x1 = clip.left() / tilew;
1600 int x2 = clip.right() / tilew;
1601 const int y1 = clip.top() / tileh;
1602 int y2 = clip.bottom() / tileh;
1603
1604 const int roww = pm.width() / tilew;
1605
1606 for (int j = y1; j <= y2; j++)
1607 {
1608 int jj = j % tilesVertically();
1609
1610 for (int i = x1; i <= x2; i++)
1611 {
1612 int t = tile(i % tilesHorizontally(), jj);
1613 int tx = t % roww;
1614 int ty = t / roww;
1615 painter.drawPixmap(i * tilew, j * tileh, pm, tx * tilew, ty * tileh, tilew, tileh);
1616 }
1617 }
1618 }
1619}
1620
1621/*
1622 This virtual function is called for all updates of the canvas. It
1623 renders any foreground graphics using the painter \a painter, in
1624 the area \a clip.
1625
1626 If the graphics for an area change, you must explicitly call
1627 setChanged(const QRect&) for the result to be visible when
1628 update() is next called.
1629
1630 The default is to draw nothing.
1631*/
1632void
1633QtCanvas::drawForeground(QPainter& painter, const QRect& clip)
1634{
1635 if (debug_redraw_areas)
1636 {
1637 painter.setPen(red);
1638 painter.setBrush(NoBrush);
1639 painter.drawRect(clip);
1640 }
1641}
1642
1643/*
1644 Sets the QtCanvas to be composed of \a h tiles horizontally and \a
1645 v tiles vertically. Each tile will be an image \a tilewidth by \a
1646 tileheight pixels from pixmap \a p.
1647
1648 The pixmap \a p is a list of tiles, arranged left to right, (and
1649 in the case of pixmaps that have multiple rows of tiles, top to
1650 bottom), with tile 0 in the top-left corner, tile 1 next to the
1651 right, and so on, e.g.
1652
1653 \table
1654 \row \i 0 \i 1 \i 2 \i 3
1655 \row \i 4 \i 5 \i 6 \i 7
1656 \endtable
1657
1658 If the canvas is larger than the matrix of tiles, the entire
1659 matrix is repeated as necessary to cover the whole canvas. If it
1660 is smaller, tiles to the right and bottom are not visible.
1661
1662 The width and height of \a p must be a multiple of \a tilewidth
1663 and \a tileheight. If they are not the function will do nothing.
1664
1665 If you want to unset any tiling set, then just pass in a null
1666 pixmap and 0 for \a h, \a v, \a tilewidth, and
1667 \a tileheight.
1668*/
1669void
1670QtCanvas::setTiles(QPixmap p, int h, int v, int tilewidth, int tileheight)
1671{
1672 if (!p.isNull() &&
1673 (!tilewidth || !tileheight || p.width() % tilewidth != 0 || p.height() % tileheight != 0))
1674 {
1675 return;
1676 }
1677
1678 htiles = h;
1679 vtiles = v;
1680 delete[] grid;
1681 pm = p;
1682
1683 if (h && v && !p.isNull())
1684 {
1685 grid = new ushort[h * v];
1686 memset(grid, 0, h * v * sizeof(ushort));
1687 tilew = tilewidth;
1688 tileh = tileheight;
1689 }
1690 else
1691 {
1692 grid = 0;
1693 }
1694
1695 if (h + v > 10)
1696 {
1697 int s = scm(tilewidth, tileheight);
1698 retune(s < 128 ? s : qMax(tilewidth, tileheight));
1699 }
1700
1701 setAllChanged();
1702}
1703
1704/*
1705 \fn int QtCanvas::tile(int x, int y) const
1706
1707 Returns the tile at position (\a x, \a y). Initially, all tiles
1708 are 0.
1709
1710 The parameters must be within range, i.e.
1711 0 < \a x < tilesHorizontally() and
1712 0 < \a y < tilesVertically().
1713
1714 \sa setTile()
1715*/
1716
1717/*
1718 \fn int QtCanvas::tilesHorizontally() const
1719
1720 Returns the number of tiles horizontally.
1721*/
1722
1723/*
1724 \fn int QtCanvas::tilesVertically() const
1725
1726 Returns the number of tiles vertically.
1727*/
1728
1729/*
1730 \fn int QtCanvas::tileWidth() const
1731
1732 Returns the width of each tile.
1733*/
1734
1735/*
1736 \fn int QtCanvas::tileHeight() const
1737
1738 Returns the height of each tile.
1739*/
1740
1741
1742/*
1743 Sets the tile at (\a x, \a y) to use tile number \a tilenum, which
1744 is an index into the tile pixmaps. The canvas will update
1745 appropriately when update() is next called.
1746
1747 The images are taken from the pixmap set by setTiles() and are
1748 arranged left to right, (and in the case of pixmaps that have
1749 multiple rows of tiles, top to bottom), with tile 0 in the
1750 top-left corner, tile 1 next to the right, and so on, e.g.
1751
1752 \table
1753 \row \i 0 \i 1 \i 2 \i 3
1754 \row \i 4 \i 5 \i 6 \i 7
1755 \endtable
1756
1757 \sa tile() setTiles()
1758*/
1759void
1760QtCanvas::setTile(int x, int y, int tilenum)
1761{
1762 ushort& t = grid[x + y * htiles];
1763
1764 if (t != tilenum)
1765 {
1766 t = tilenum;
1767
1768 if (tilew == tileh && tilew == chunksize)
1769 {
1770 setChangedChunk(x, y); // common case
1771 }
1772 else
1773 {
1774 setChanged(QRect(x * tilew, y * tileh, tilew, tileh));
1775 }
1776 }
1777}
1778
1779// lesser-used data in canvas item, plus room for extension.
1780// Be careful adding to this - check all usages.
1782{
1783 QtCanvasItemExtra() : vx(0.0), vy(0.0)
1784 {
1785 }
1786
1787 double vx, vy;
1788 friend class QtCanvasItem;
1789};
1790
1791/*
1792 \class QtCanvasItem qtcanvas.h
1793 \brief The QtCanvasItem class provides an abstract graphic object on a QtCanvas.
1794
1795 A variety of QtCanvasItem subclasses provide immediately usable
1796 behaviour. This class is a pure abstract superclass providing the
1797 behaviour that is shared among all the concrete canvas item classes.
1798 QtCanvasItem is not intended for direct subclassing. It is much easier
1799 to subclass one of its subclasses, e.g. QtCanvasPolygonalItem (the
1800 commonest base class), QtCanvasRectangle, QtCanvasSprite, QtCanvasEllipse
1801 or QtCanvasText.
1802
1803 Canvas items are added to a canvas by constructing them and passing the
1804 canvas to the canvas item's constructor. An item can be moved to a
1805 different canvas using setCanvas().
1806
1807 Items appear on the canvas after their \link show() show()\endlink
1808 function has been called (or \link setVisible()
1809 setVisible(true)\endlink), and \e after update() has been called. The
1810 canvas only shows items that are \link setVisible() visible\endlink,
1811 and then only if \l update() is called. If you created the canvas
1812 without passing a width and height to the constructor you'll also need
1813 to call \link QtCanvas::resize() resize()\endlink. Since the canvas
1814 background defaults to white and canvas items default to white,
1815 you may need to change colors to see your items.
1816
1817 A QtCanvasItem object can be moved in the x(), y() and z() dimensions
1818 using functions such as move(), moveBy(), setX(), setY() and setZ(). A
1819 canvas item can be set in motion, `animated', using setAnimated() and
1820 given a velocity in the x and y directions with setXVelocity() and
1821 setYVelocity() -- the same effect can be achieved by calling
1822 setVelocity(). Use the collidesWith() function to see if the canvas item
1823 will collide on the \e next advance(1) and use collisions() to see what
1824 collisions have occurred.
1825
1826 Use QtCanvasSprite or your own subclass of QtCanvasSprite to create canvas
1827 items which are animated, i.e. which change over time.
1828
1829 The size of a canvas item is given by boundingRect(). Use
1830 boundingRectAdvanced() to see what the size of the canvas item will be
1831 \e after the next advance(1) call.
1832
1833 The rtti() function is used for identifying subclasses of QtCanvasItem.
1834 The canvas() function returns a pointer to the canvas which contains the
1835 canvas item.
1836
1837 QtCanvasItem provides the show() and isVisible() functions like those in
1838 QWidget.
1839
1840 QtCanvasItem also provides the setEnabled(), setActive() and
1841 setSelected() functions; these functions set the relevant boolean and
1842 cause a repaint but the boolean values they set are not used in
1843 QtCanvasItem itself. You can make use of these booleans in your subclasses.
1844
1845 By default, canvas items have no velocity, no size, and are not in
1846 motion. The subclasses provided in Qt do not change these defaults
1847 except where noted.
1848
1849*/
1850
1851/*
1852 \enum QtCanvasItem::RttiValues
1853
1854 This enum is used to name the different types of canvas item.
1855
1856 \value Rtti_Item Canvas item abstract base class
1857 \value Rtti_Ellipse
1858 \value Rtti_Line
1859 \value Rtti_Polygon
1860 \value Rtti_PolygonalItem
1861 \value Rtti_Rectangle
1862 \value Rtti_Spline
1863 \value Rtti_Sprite
1864 \value Rtti_Text
1865
1866*/
1867
1868/*
1869 \fn void QtCanvasItem::update()
1870
1871 Call this function to repaint the canvas's changed chunks.
1872*/
1873
1874/*
1875 Constructs a QtCanvasItem on canvas \a canvas.
1876
1877 \sa setCanvas()
1878*/
1879QtCanvasItem::QtCanvasItem(QtCanvas* canvas) : cnv(canvas), myx(0), myy(0), myz(0)
1880{
1881 ani = 0;
1882 vis = 0;
1883 val = 0;
1884 sel = 0;
1885 ena = 0;
1886 act = 0;
1887
1888 ext = 0;
1889
1890 if (cnv)
1891 {
1892 cnv->addItem(this);
1893 }
1894}
1895
1896/*
1897 Destroys the QtCanvasItem and removes it from its canvas.
1898*/
1900{
1901 if (cnv)
1902 {
1903 cnv->removeItem(this);
1904 cnv->removeAnimation(this);
1905 }
1906
1907 delete ext;
1908}
1909
1911QtCanvasItem::extra()
1912{
1913 if (!ext)
1914 {
1915 ext = new QtCanvasItemExtra;
1916 }
1917
1918 return *ext;
1919}
1920
1921/*
1922 \fn double QtCanvasItem::x() const
1923
1924 Returns the horizontal position of the canvas item. Note that
1925 subclasses often have an origin other than the top-left corner.
1926*/
1927
1928/*
1929 \fn double QtCanvasItem::y() const
1930
1931 Returns the vertical position of the canvas item. Note that
1932 subclasses often have an origin other than the top-left corner.
1933*/
1934
1935/*
1936 \fn double QtCanvasItem::z() const
1937
1938 Returns the z index of the canvas item, which is used for visual
1939 order: higher-z items obscure (are in front of) lower-z items.
1940*/
1941
1942/*
1943 \fn void QtCanvasItem::setX(double x)
1944
1945 Moves the canvas item so that its x-position is \a x.
1946
1947 \sa x(), move()
1948*/
1949
1950/*
1951 \fn void QtCanvasItem::setY(double y)
1952
1953 Moves the canvas item so that its y-position is \a y.
1954
1955 \sa y(), move()
1956*/
1957
1958/*
1959 \fn void QtCanvasItem::setZ(double z)
1960
1961 Sets the z index of the canvas item to \a z. Higher-z items
1962 obscure (are in front of) lower-z items.
1963
1964 \sa z(), move()
1965*/
1966
1967
1968/*
1969 Moves the canvas item relative to its current position by (\a dx,
1970 \a dy).
1971*/
1972void
1973QtCanvasItem::moveBy(double dx, double dy)
1974{
1975 if (dx || dy)
1976 {
1977 removeFromChunks();
1978 myx += dx;
1979 myy += dy;
1980 addToChunks();
1981 }
1982}
1983
1984/*
1985 Moves the canvas item to the absolute position (\a x, \a y).
1986*/
1987void
1988QtCanvasItem::move(double x, double y)
1989{
1990 moveBy(x - myx, y - myy);
1991}
1992
1993/*
1994 Returns true if the canvas item is in motion; otherwise returns
1995 false.
1996
1997 \sa setVelocity(), setAnimated()
1998*/
1999bool
2001{
2002 return (bool)ani;
2003}
2004
2005/*
2006 Sets the canvas item to be in motion if \a y is true, or not if \a
2007 y is false. The speed and direction of the motion is set with
2008 setVelocity(), or with setXVelocity() and setYVelocity().
2009
2010 \sa advance(), QtCanvas::advance()
2011*/
2012void
2014{
2015 if (y != (bool)ani)
2016 {
2017 ani = (uint)y;
2018
2019 if (y)
2020 {
2021 cnv->addAnimation(this);
2022 }
2023 else
2024 {
2025 cnv->removeAnimation(this);
2026 }
2027 }
2028}
2029
2030/*
2031 \fn void QtCanvasItem::setXVelocity(double vx)
2032
2033 Sets the horizontal component of the canvas item's velocity to \a vx.
2034
2035 \sa setYVelocity() setVelocity()
2036*/
2037
2038/*
2039 \fn void QtCanvasItem::setYVelocity(double vy)
2040
2041 Sets the vertical component of the canvas item's velocity to \a vy.
2042
2043 \sa setXVelocity() setVelocity()
2044*/
2045
2046/*
2047 Sets the canvas item to be in motion, moving by \a vx and \a vy
2048 pixels in the horizontal and vertical directions respectively.
2049
2050 \sa advance() setXVelocity() setYVelocity()
2051*/
2052void
2053QtCanvasItem::setVelocity(double vx, double vy)
2054{
2055 if (ext || vx != 0.0 || vy != 0.0)
2056 {
2057 if (!ani)
2058 {
2059 setAnimated(true);
2060 }
2061
2062 extra().vx = vx;
2063 extra().vy = vy;
2064 }
2065}
2066
2067/*
2068 Returns the horizontal velocity component of the canvas item.
2069*/
2070double
2072{
2073 return ext ? ext->vx : 0;
2074}
2075
2076/*
2077 Returns the vertical velocity component of the canvas item.
2078*/
2079double
2081{
2082 return ext ? ext->vy : 0;
2083}
2084
2085/*
2086 The default implementation moves the canvas item, if it is
2087 animated(), by the preset velocity if \a phase is 1, and does
2088 nothing if \a phase is 0.
2089
2090 Note that if you reimplement this function, the reimplementation
2091 must not change the canvas in any way, for example it must not add
2092 or remove items.
2093
2094 \sa QtCanvas::advance() setVelocity()
2095*/
2096void
2097QtCanvasItem::advance(int phase)
2098{
2099 if (ext && phase == 1)
2100 {
2101 moveBy(ext->vx, ext->vy);
2102 }
2103}
2104
2105/*
2106 \fn void QtCanvasItem::draw(QPainter& painter)
2107
2108 This abstract virtual function draws the canvas item using \a painter.
2109*/
2110
2111/*
2112 Sets the QtCanvas upon which the canvas item is to be drawn to \a c.
2113
2114 \sa canvas()
2115*/
2116void
2118{
2119 bool v = isVisible();
2120 setVisible(false);
2121
2122 if (cnv)
2123 {
2124 if (ext)
2125 {
2126 cnv->removeAnimation(this);
2127 }
2128
2129 cnv->removeItem(this);
2130 }
2131
2132 cnv = c;
2133
2134 if (cnv)
2135 {
2136 cnv->addItem(this);
2137
2138 if (ext)
2139 {
2140 cnv->addAnimation(this);
2141 }
2142 }
2143
2144 setVisible(v);
2145}
2146
2147/*
2148 \fn QtCanvas* QtCanvasItem::canvas() const
2149
2150 Returns the canvas containing the canvas item.
2151*/
2152
2153/* Shorthand for setVisible(true). */
2154void
2156{
2157 setVisible(true);
2158}
2159
2160/* Shorthand for setVisible(false). */
2161void
2163{
2164 setVisible(false);
2165}
2166
2167/*
2168 Makes the canvas item visible if \a yes is true, or invisible if
2169 \a yes is false. The change takes effect when QtCanvas::update() is
2170 next called.
2171*/
2172void
2174{
2175 if ((bool)vis != yes)
2176 {
2177 if (yes)
2178 {
2179 vis = (uint)yes;
2180 addToChunks();
2181 }
2182 else
2183 {
2184 removeFromChunks();
2185 vis = (uint)yes;
2186 }
2187 }
2188}
2189
2190/*
2191 \obsolete
2192 \fn bool QtCanvasItem::visible() const
2193 Use isVisible() instead.
2194*/
2195
2196/*
2197 \fn bool QtCanvasItem::isVisible() const
2198
2199 Returns true if the canvas item is visible; otherwise returns
2200 false.
2201
2202 Note that in this context true does \e not mean that the canvas
2203 item is currently in a view, merely that if a view is showing the
2204 area where the canvas item is positioned, and the item is not
2205 obscured by items with higher z values, and the view is not
2206 obscured by overlaying windows, it would be visible.
2207
2208 \sa setVisible(), z()
2209*/
2210
2211/*
2212 \obsolete
2213 \fn bool QtCanvasItem::selected() const
2214 Use isSelected() instead.
2215*/
2216
2217/*
2218 \fn bool QtCanvasItem::isSelected() const
2219
2220 Returns true if the canvas item is selected; otherwise returns false.
2221*/
2222
2223/*
2224 Sets the selected flag of the item to \a yes. If this changes the
2225 item's selected state the item will be redrawn when
2226 QtCanvas::update() is next called.
2227
2228 The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem
2229 subclasses do not make use of this value. The setSelected()
2230 function is supplied because many applications need it, but it is
2231 up to you how you use the isSelected() value.
2232*/
2233void
2235{
2236 if ((bool)sel != yes)
2237 {
2238 sel = (uint)yes;
2239 changeChunks();
2240 }
2241}
2242
2243/*
2244 \obsolete
2245 \fn bool QtCanvasItem::enabled() const
2246 Use isEnabled() instead.
2247*/
2248
2249/*
2250 \fn bool QtCanvasItem::isEnabled() const
2251
2252 Returns true if the QtCanvasItem is enabled; otherwise returns false.
2253*/
2254
2255/*
2256 Sets the enabled flag of the item to \a yes. If this changes the
2257 item's enabled state the item will be redrawn when
2258 QtCanvas::update() is next called.
2259
2260 The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem
2261 subclasses do not make use of this value. The setEnabled()
2262 function is supplied because many applications need it, but it is
2263 up to you how you use the isEnabled() value.
2264*/
2265void
2267{
2268 if (ena != (uint)yes)
2269 {
2270 ena = (uint)yes;
2271 changeChunks();
2272 }
2273}
2274
2275/*
2276 \obsolete
2277 \fn bool QtCanvasItem::active() const
2278 Use isActive() instead.
2279*/
2280
2281/*
2282 \fn bool QtCanvasItem::isActive() const
2283
2284 Returns true if the QtCanvasItem is active; otherwise returns false.
2285*/
2286
2287/*
2288 Sets the active flag of the item to \a yes. If this changes the
2289 item's active state the item will be redrawn when
2290 QtCanvas::update() is next called.
2291
2292 The QtCanvas, QtCanvasItem and the Qt-supplied QtCanvasItem
2293 subclasses do not make use of this value. The setActive() function
2294 is supplied because many applications need it, but it is up to you
2295 how you use the isActive() value.
2296*/
2297void
2299{
2300 if (act != (uint)yes)
2301 {
2302 act = (uint)yes;
2303 changeChunks();
2304 }
2305}
2306
2307bool
2309{
2310 const QImage* s2image = s2->imageAdvanced()->collision_mask;
2311 QRect s2area = s2->boundingRectAdvanced();
2312
2313 QRect cyourarea(s2area.x(), s2area.y(), s2area.width(), s2area.height());
2314
2315 QImage* s1image = s1->imageAdvanced()->collision_mask;
2316
2317 QRect s1area = s1->boundingRectAdvanced();
2318
2319 QRect ourarea = s1area.intersected(cyourarea);
2320
2321 if (ourarea.isEmpty())
2322 {
2323 return false;
2324 }
2325
2326 int x2 = ourarea.x() - cyourarea.x();
2327 int y2 = ourarea.y() - cyourarea.y();
2328 int x1 = ourarea.x() - s1area.x();
2329 int y1 = ourarea.y() - s1area.y();
2330 int w = ourarea.width();
2331 int h = ourarea.height();
2332
2333 if (!s2image)
2334 {
2335 if (!s1image)
2336 {
2337 return w > 0 && h > 0;
2338 }
2339
2340 // swap everything around
2341 int t;
2342 t = x1;
2343 x1 = x2;
2344 x2 = t;
2345 t = y1;
2346 x1 = y2;
2347 y2 = t;
2348 s2image = s1image;
2349 s1image = 0;
2350 }
2351
2352 // s2image != 0
2353
2354 // A non-linear search may be more efficient.
2355 // Perhaps spiralling out from the center, or a simpler
2356 // vertical expansion from the centreline.
2357
2358 // We assume that sprite masks don't have
2359 // different bit orders.
2360 //
2361 // Q_ASSERT(s1image->bitOrder() == s2image->bitOrder());
2362
2363 if (s1image)
2364 {
2365 if (s1image->format() == QImage::Format_MonoLSB)
2366 {
2367 for (int j = 0; j < h; j++)
2368 {
2369 uchar* ml = s1image->scanLine(y1 + j);
2370 const uchar* yl = s2image->scanLine(y2 + j);
2371
2372 for (int i = 0; i < w; i++)
2373 {
2374 if (*(yl + ((x2 + i) >> 3)) & (1 << ((x2 + i) & 7)) &&
2375 *(ml + ((x1 + i) >> 3)) & (1 << ((x1 + i) & 7)))
2376 {
2377 return true;
2378 }
2379 }
2380 }
2381 }
2382 else
2383 {
2384 for (int j = 0; j < h; j++)
2385 {
2386 uchar* ml = s1image->scanLine(y1 + j);
2387 const uchar* yl = s2image->scanLine(y2 + j);
2388
2389 for (int i = 0; i < w; i++)
2390 {
2391 if (*(yl + ((x2 + i) >> 3)) & (1 << (7 - ((x2 + i) & 7))) &&
2392 *(ml + ((x1 + i) >> 3)) & (1 << (7 - ((x1 + i) & 7))))
2393 {
2394 return true;
2395 }
2396 }
2397 }
2398 }
2399 }
2400 else
2401 {
2402 if (s2image->format() == QImage::Format_MonoLSB)
2403 {
2404 for (int j = 0; j < h; j++)
2405 {
2406 const uchar* yl = s2image->scanLine(y2 + j);
2407
2408 for (int i = 0; i < w; i++)
2409 {
2410 if (*(yl + ((x2 + i) >> 3)) & (1 << ((x2 + i) & 7)))
2411 {
2412 return true;
2413 }
2414 }
2415 }
2416 }
2417 else
2418 {
2419 for (int j = 0; j < h; j++)
2420 {
2421 const uchar* yl = s2image->scanLine(y2 + j);
2422
2423 for (int i = 0; i < w; i++)
2424 {
2425 if (*(yl + ((x2 + i) >> 3)) & (1 << (7 - ((x2 + i) & 7))))
2426 {
2427 return true;
2428 }
2429 }
2430 }
2431 }
2432 }
2433
2434 return false;
2435}
2436
2437static bool
2438collision_double_dispatch(const QtCanvasSprite* s1,
2439 const QtCanvasPolygonalItem* p1,
2440 const QtCanvasRectangle* r1,
2441 const QtCanvasEllipse* e1,
2442 const QtCanvasText* t1,
2443 const QtCanvasSprite* s2,
2444 const QtCanvasPolygonalItem* p2,
2445 const QtCanvasRectangle* r2,
2446 const QtCanvasEllipse* e2,
2447 const QtCanvasText* t2)
2448{
2449 const QtCanvasItem* i1 = s1 ? (const QtCanvasItem*)s1
2450 : p1 ? (const QtCanvasItem*)p1
2451 : r1 ? (const QtCanvasItem*)r1
2452 : e1 ? (const QtCanvasItem*)e1
2453 : (const QtCanvasItem*)t1;
2454 const QtCanvasItem* i2 = s2 ? (const QtCanvasItem*)s2
2455 : p2 ? (const QtCanvasItem*)p2
2456 : r2 ? (const QtCanvasItem*)r2
2457 : e2 ? (const QtCanvasItem*)e2
2458 : (const QtCanvasItem*)t2;
2459
2460 if (s1 && s2)
2461 {
2462 // a
2463 return qt_testCollision(s1, s2);
2464 }
2465 else if ((r1 || t1 || s1) && (r2 || t2 || s2))
2466 {
2467 // b
2468 QRect rc1 = i1->boundingRectAdvanced();
2469 QRect rc2 = i2->boundingRectAdvanced();
2470 return rc1.intersects(rc2);
2471 }
2472 else if (e1 && e2 && e1->angleLength() >= 360 * 16 && e2->angleLength() >= 360 * 16 &&
2473 e1->width() == e1->height() && e2->width() == e2->height())
2474 {
2475 // c
2476 double xd = (e1->x() + e1->xVelocity()) - (e2->x() + e1->xVelocity());
2477 double yd = (e1->y() + e1->yVelocity()) - (e2->y() + e1->yVelocity());
2478 double rd = (e1->width() + e2->width()) / 2;
2479 return xd * xd + yd * yd <= rd * rd;
2480 }
2481 else if (p1 && (p2 || s2 || t2))
2482 {
2483 // d
2484 QPolygon pa1 = p1->areaPointsAdvanced();
2485 QPolygon pa2 = p2 ? p2->areaPointsAdvanced() : QPolygon(i2->boundingRectAdvanced());
2486 bool col = !(QRegion(pa1) & QRegion(pa2, Qt::WindingFill)).isEmpty();
2487
2488 return col;
2489 }
2490 else
2491 {
2492 return collision_double_dispatch(s2, p2, r2, e2, t2, s1, p1, r1, e1, t1);
2493 }
2494}
2495
2496/*
2497 \fn bool QtCanvasItem::collidesWith(const QtCanvasItem* other) const
2498
2499 Returns true if the canvas item will collide with the \a other
2500 item \e after they have moved by their current velocities;
2501 otherwise returns false.
2502
2503 \sa collisions()
2504*/
2505
2506
2507/*
2508 \class QtCanvasSprite qtcanvas.h
2509 \brief The QtCanvasSprite class provides an animated canvas item on a QtCanvas.
2510
2511 A canvas sprite is an object which can contain any number of images
2512 (referred to as frames), only one of which is current, i.e.
2513 displayed, at any one time. The images can be passed in the
2514 constructor or set or changed later with setSequence(). If you
2515 subclass QtCanvasSprite you can change the frame that is displayed
2516 periodically, e.g. whenever QtCanvasItem::advance(1) is called to
2517 create the effect of animation.
2518
2519 The current frame can be set with setFrame() or with move(). The
2520 number of frames available is given by frameCount(). The bounding
2521 rectangle of the current frame is returned by boundingRect().
2522
2523 The current frame's image can be retrieved with image(); use
2524 imageAdvanced() to retrieve the image for the frame that will be
2525 shown after advance(1) is called. Use the image() overload passing
2526 it an integer index to retrieve a particular image from the list of
2527 frames.
2528
2529 Use width() and height() to retrieve the dimensions of the current
2530 frame.
2531
2532 Use leftEdge() and rightEdge() to retrieve the current frame's
2533 left-hand and right-hand x-coordinates respectively. Use
2534 bottomEdge() and topEdge() to retrieve the current frame's bottom
2535 and top y-coordinates respectively. These functions have an overload
2536 which will accept an integer frame number to retrieve the
2537 coordinates of a particular frame.
2538
2539 QtCanvasSprite draws very quickly, at the expense of memory.
2540
2541 The current frame's image can be drawn on a painter with draw().
2542
2543 Like any other canvas item, canvas sprites can be moved with
2544 move() which sets the x and y coordinates and the frame number, as
2545 well as with QtCanvasItem::move() and QtCanvasItem::moveBy(), or by
2546 setting coordinates with QtCanvasItem::setX(), QtCanvasItem::setY()
2547 and QtCanvasItem::setZ().
2548
2549*/
2550
2551
2552/*
2553 \reimp
2554*/
2555bool
2557{
2558 return i->collidesWith(this, 0, 0, 0, 0);
2559}
2560
2561/*
2562 Returns true if the canvas item collides with any of the given
2563 items; otherwise returns false. The parameters, \a s, \a p, \a r,
2564 \a e and \a t, are all the same object, this is just a type
2565 resolution trick.
2566*/
2567bool
2569 const QtCanvasPolygonalItem* p,
2570 const QtCanvasRectangle* r,
2571 const QtCanvasEllipse* e,
2572 const QtCanvasText* t) const
2573{
2574 return collision_double_dispatch(s, p, r, e, t, this, 0, 0, 0, 0);
2575}
2576
2577/*
2578 \reimp
2579*/
2580bool
2582{
2583 return i->collidesWith(0, this, 0, 0, 0);
2584}
2585
2586bool
2588 const QtCanvasPolygonalItem* p,
2589 const QtCanvasRectangle* r,
2590 const QtCanvasEllipse* e,
2591 const QtCanvasText* t) const
2592{
2593 return collision_double_dispatch(s, p, r, e, t, 0, this, 0, 0, 0);
2594}
2595
2596/*
2597 \reimp
2598*/
2599bool
2601{
2602 return i->collidesWith(0, this, this, 0, 0);
2603}
2604
2605bool
2607 const QtCanvasPolygonalItem* p,
2608 const QtCanvasRectangle* r,
2609 const QtCanvasEllipse* e,
2610 const QtCanvasText* t) const
2611{
2612 return collision_double_dispatch(s, p, r, e, t, 0, this, this, 0, 0);
2613}
2614
2615/*
2616 \reimp
2617*/
2618bool
2620{
2621 return i->collidesWith(0, this, 0, this, 0);
2622}
2623
2624bool
2626 const QtCanvasPolygonalItem* p,
2627 const QtCanvasRectangle* r,
2628 const QtCanvasEllipse* e,
2629 const QtCanvasText* t) const
2630{
2631 return collision_double_dispatch(s, p, r, e, t, 0, this, 0, this, 0);
2632}
2633
2634/*
2635 \reimp
2636*/
2637bool
2639{
2640 return i->collidesWith(0, 0, 0, 0, this);
2641}
2642
2643bool
2645 const QtCanvasPolygonalItem* p,
2646 const QtCanvasRectangle* r,
2647 const QtCanvasEllipse* e,
2648 const QtCanvasText* t) const
2649{
2650 return collision_double_dispatch(s, p, r, e, t, 0, 0, 0, 0, this);
2651}
2652
2653/*
2654 Returns the list of canvas items that this canvas item has
2655 collided with.
2656
2657 A collision is generally defined as occurring when the pixels of
2658 one item draw on the pixels of another item, but not all
2659 subclasses are so precise. Also, since pixel-wise collision
2660 detection can be slow, this function works in either exact or
2661 inexact mode, according to the \a exact parameter.
2662
2663 If \a exact is true, the canvas items returned have been
2664 accurately tested for collision with the canvas item.
2665
2666 If \a exact is false, the canvas items returned are \e near the
2667 canvas item. You can test the canvas items returned using
2668 collidesWith() if any are interesting collision candidates. By
2669 using this approach, you can ignore some canvas items for which
2670 collisions are not relevant.
2671
2672 The returned list is a list of QtCanvasItems, but often you will
2673 need to cast the items to their subclass types. The safe way to do
2674 this is to use rtti() before casting. This provides some of the
2675 functionality of the standard C++ dynamic cast operation even on
2676 compilers where dynamic casts are not available.
2677
2678 Note that a canvas item may be `on' a canvas, e.g. it was created
2679 with the canvas as parameter, even though its coordinates place it
2680 beyond the edge of the canvas's area. Collision detection only
2681 works for canvas items which are wholly or partly within the
2682 canvas's area.
2683
2684 Note that if items have a velocity (see \l setVelocity()), then
2685 collision testing is done based on where the item \e will be when
2686 it moves, not its current location. For example, a "ball" item
2687 doesn't need to actually embed into a "wall" item before a
2688 collision is detected. For items without velocity, plain
2689 intersection is used.
2690*/
2692QtCanvasItem::collisions(bool exact) const
2693{
2694 return canvas()->collisions(chunks(), this, exact);
2695}
2696
2697/*
2698 Returns a list of canvas items that collide with the point \a p.
2699 The list is ordered by z coordinates, from highest z coordinate
2700 (front-most item) to lowest z coordinate (rear-most item).
2701*/
2703QtCanvas::collisions(const QPoint& p) const
2704{
2705 return collisions(QRect(p, QSize(1, 1)));
2706}
2707
2708/*
2709 \overload
2710
2711 Returns a list of items which collide with the rectangle \a r. The
2712 list is ordered by z coordinates, from highest z coordinate
2713 (front-most item) to lowest z coordinate (rear-most item).
2714*/
2716QtCanvas::collisions(const QRect& r) const
2717{
2718 QtCanvasRectangle i(r, (QtCanvas*)this);
2719 i.setPen(NoPen);
2720 i.show(); // doesn't actually show, since we destroy it
2721 QtCanvasItemList l = i.collisions(true);
2722 qSort(l.begin(), l.end(), QtCanvasItemLess());
2723 return l;
2724}
2725
2726/*
2727 \overload
2728
2729 Returns a list of canvas items which intersect with the chunks
2730 listed in \a chunklist, excluding \a item. If \a exact is true,
2731 only those which actually \link QtCanvasItem::collidesWith()
2732 collide with\endlink \a item are returned; otherwise canvas items
2733 are included just for being in the chunks.
2734
2735 This is a utility function mainly used to implement the simpler
2736 QtCanvasItem::collisions() function.
2737*/
2739QtCanvas::collisions(const QPolygon& chunklist, const QtCanvasItem* item, bool exact) const
2740{
2741 QSet<QtCanvasItem*> seen;
2742 QtCanvasItemList result;
2743
2744 for (int i = 0; i < (int)chunklist.count(); i++)
2745 {
2746 int x = chunklist[i].x();
2747 int y = chunklist[i].y();
2748
2749 if (validChunk(x, y))
2750 {
2751 const QtCanvasItemList& l = chunk(x, y).list();
2752
2753 for (int i = 0; i < l.size(); ++i)
2754 {
2755 QtCanvasItem* g = l.at(i);
2756
2757 if (g != item)
2758 {
2759 if (!seen.contains(g))
2760 {
2761 seen.insert(g);
2762
2763 if (!exact || item->collidesWith(g))
2764 {
2765 result.append(g);
2766 }
2767 }
2768 }
2769 }
2770 }
2771 }
2772
2773 return result;
2774}
2775
2776/*
2777 \internal
2778 Adds the item to all the chunks it covers.
2779*/
2780void
2781QtCanvasItem::addToChunks()
2782{
2783 if (isVisible() && canvas())
2784 {
2785 QPolygon pa = chunks();
2786
2787 for (int i = 0; i < (int)pa.count(); i++)
2788 {
2789 canvas()->addItemToChunk(this, pa[i].x(), pa[i].y());
2790 }
2791
2792 val = (uint) true;
2793 }
2794}
2795
2796/*
2797 \internal
2798 Removes the item from all the chunks it covers.
2799*/
2800void
2801QtCanvasItem::removeFromChunks()
2802{
2803 if (isVisible() && canvas())
2804 {
2805 QPolygon pa = chunks();
2806
2807 for (int i = 0; i < (int)pa.count(); i++)
2808 {
2809 canvas()->removeItemFromChunk(this, pa[i].x(), pa[i].y());
2810 }
2811 }
2812}
2813
2814/*
2815 \internal
2816 Sets all the chunks covered by the item to be refreshed with QtCanvas::update()
2817 is next called.
2818*/
2819void
2820QtCanvasItem::changeChunks()
2821{
2822 if (isVisible() && canvas())
2823 {
2824 if (!val)
2825 {
2826 addToChunks();
2827 }
2828
2829 QPolygon pa = chunks();
2830
2831 for (int i = 0; i < (int)pa.count(); i++)
2832 {
2833 canvas()->setChangedChunk(pa[i].x(), pa[i].y());
2834 }
2835 }
2836}
2837
2838/*
2839 \fn QRect QtCanvasItem::boundingRect() const
2840
2841 Returns the bounding rectangle in pixels that the canvas item covers.
2842
2843 \sa boundingRectAdvanced()
2844*/
2845
2846/*
2847 Returns the bounding rectangle of pixels that the canvas item \e
2848 will cover after advance(1) is called.
2849
2850 \sa boundingRect()
2851*/
2852QRect
2854{
2855 int dx = int(x() + xVelocity()) - int(x());
2856 int dy = int(y() + yVelocity()) - int(y());
2857 QRect r = boundingRect();
2858 r.translate(dx, dy);
2859 return r;
2860}
2861
2862/*
2863 \class QtCanvasPixmap qtcanvas.h
2864 \brief The QtCanvasPixmap class provides pixmaps for QtCanvasSprites.
2865
2866 If you want to show a single pixmap on a QtCanvas use a
2867 QtCanvasSprite with just one pixmap.
2868
2869 When pixmaps are inserted into a QtCanvasPixmapArray they are held
2870 as QtCanvasPixmaps. \l{QtCanvasSprite}s are used to show pixmaps on
2871 \l{QtCanvas}es and hold their pixmaps in a QtCanvasPixmapArray. If
2872 you retrieve a frame (pixmap) from a QtCanvasSprite it will be
2873 returned as a QtCanvasPixmap.
2874
2875 The pixmap is a QPixmap and can only be set in the constructor.
2876 There are three different constructors, one taking a QPixmap, one
2877 a QImage and one a file name that refers to a file in any
2878 supported file format (see QImageReader).
2879
2880 QtCanvasPixmap can have a hotspot which is defined in terms of an (x,
2881 y) offset. When you create a QtCanvasPixmap from a PNG file or from
2882 a QImage that has a QImage::offset(), the offset() is initialized
2883 appropriately, otherwise the constructor leaves it at (0, 0). You
2884 can set it later using setOffset(). When the QtCanvasPixmap is used
2885 in a QtCanvasSprite, the offset position is the point at
2886 QtCanvasItem::x() and QtCanvasItem::y(), not the top-left corner of
2887 the pixmap.
2888
2889 Note that for QtCanvasPixmap objects created by a QtCanvasSprite, the
2890 position of each QtCanvasPixmap object is set so that the hotspot
2891 stays in the same position.
2892
2893 \sa QtCanvasPixmapArray QtCanvasItem QtCanvasSprite
2894*/
2895
2896
2897/*
2898 Constructs a QtCanvasPixmap that uses the image stored in \a
2899 datafilename.
2900*/
2901QtCanvasPixmap::QtCanvasPixmap(const QString& datafilename)
2902{
2903 QImage image(datafilename);
2904 init(image);
2905}
2906
2907/*
2908 Constructs a QtCanvasPixmap from the image \a image.
2909*/
2910QtCanvasPixmap::QtCanvasPixmap(const QImage& image)
2911{
2912 init(image);
2913}
2914
2915/*
2916 Constructs a QtCanvasPixmap from the pixmap \a pm using the offset
2917 \a offset.
2918*/
2919QtCanvasPixmap::QtCanvasPixmap(const QPixmap& pm, const QPoint& offset)
2920{
2921 init(pm, offset.x(), offset.y());
2922}
2923
2924void
2925QtCanvasPixmap::init(const QImage& image)
2926{
2927 this->QPixmap::operator=(QPixmap::fromImage(image));
2928 hotx = image.offset().x();
2929 hoty = image.offset().y();
2930#ifndef QT_NO_IMAGE_DITHER_TO_1
2931
2932 if (image.hasAlphaChannel())
2933 {
2934 QImage i = image.createAlphaMask();
2935 collision_mask = new QImage(i);
2936 }
2937 else
2938#endif
2939 collision_mask = 0;
2940}
2941
2942void
2943QtCanvasPixmap::init(const QPixmap& pixmap, int hx, int hy)
2944{
2945 (QPixmap&)* this = pixmap;
2946 hotx = hx;
2947 hoty = hy;
2948
2949 if (pixmap.hasAlphaChannel())
2950 {
2951 QImage i = mask().toImage();
2952 collision_mask = new QImage(i);
2953 }
2954 else
2955 {
2956 collision_mask = 0;
2957 }
2958}
2959
2960/*
2961 Destroys the pixmap.
2962*/
2964{
2965 delete collision_mask;
2966}
2967
2968/*
2969 \fn int QtCanvasPixmap::offsetX() const
2970
2971 Returns the x-offset of the pixmap's hotspot.
2972
2973 \sa setOffset()
2974*/
2975
2976/*
2977 \fn int QtCanvasPixmap::offsetY() const
2978
2979 Returns the y-offset of the pixmap's hotspot.
2980
2981 \sa setOffset()
2982*/
2983
2984/*
2985 \fn void QtCanvasPixmap::setOffset(int x, int y)
2986
2987 Sets the offset of the pixmap's hotspot to (\a x, \a y).
2988
2989 \warning Do not call this function if any QtCanvasSprites are
2990 currently showing this pixmap.
2991*/
2992
2993/*
2994 \class QtCanvasPixmapArray qtcanvas.h
2995 \brief The QtCanvasPixmapArray class provides an array of QtCanvasPixmaps.
2996
2997 This class is used by QtCanvasSprite to hold an array of pixmaps.
2998 It is used to implement animated sprites, i.e. images that change
2999 over time, with each pixmap in the array holding one frame.
3000
3001 Depending on the constructor you use you can load multiple pixmaps
3002 into the array either from a directory (specifying a wildcard
3003 pattern for the files), or from a list of QPixmaps. You can also
3004 read in a set of pixmaps after construction using readPixmaps().
3005
3006 Individual pixmaps can be set with setImage() and retrieved with
3007 image(). The number of pixmaps in the array is returned by
3008 count().
3009
3010 QtCanvasSprite uses an image's mask for collision detection. You
3011 can change this by reading in a separate set of image masks using
3012 readCollisionMasks().
3013
3014*/
3015
3016/*
3017 Constructs an invalid array (i.e. isValid() will return false).
3018 You must call readPixmaps() before being able to use this
3019 QtCanvasPixmapArray.
3020*/
3021QtCanvasPixmapArray::QtCanvasPixmapArray() : framecount(0), img(0)
3022{
3023}
3024
3025/*
3026 Constructs a QtCanvasPixmapArray from files.
3027
3028 The \a fc parameter sets the number of frames to be loaded for
3029 this image.
3030
3031 If \a fc is not 0, \a datafilenamepattern should contain "%1",
3032 e.g. "foo%1.png". The actual filenames are formed by replacing the
3033 %1 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
3034 foo0001.png, foo0002.png, etc.
3035
3036 If \a fc is 0, \a datafilenamepattern is asssumed to be a
3037 filename, and the image contained in this file will be loaded as
3038 the first (and only) frame.
3039
3040 If \a datafilenamepattern does not exist, is not readable, isn't
3041 an image, or some other error occurs, the array ends up empty and
3042 isValid() returns false.
3043*/
3044
3045QtCanvasPixmapArray::QtCanvasPixmapArray(const QString& datafilenamepattern, int fc) :
3046 framecount(0), img(0)
3047{
3048 readPixmaps(datafilenamepattern, fc);
3049}
3050
3051/*
3052 \obsolete
3053 Use QtCanvasPixmapArray::QtCanvasPixmapArray(QtValueList<QPixmap>, QPolygon)
3054 instead.
3055
3056 Constructs a QtCanvasPixmapArray from the list of QPixmaps \a
3057 list. The \a hotspots list has to be of the same size as \a list.
3058*/
3059QtCanvasPixmapArray::QtCanvasPixmapArray(const QList<QPixmap>& list, const QPolygon& hotspots) :
3060 framecount(list.count()), img(new QtCanvasPixmap*[list.count()])
3061{
3062 if (list.count() != hotspots.count())
3063 {
3064 qWarning("QtCanvasPixmapArray: lists have different lengths");
3065 reset();
3066 img = 0;
3067 }
3068 else
3069 {
3070 for (int i = 0; i < framecount; i++)
3071 {
3072 img[i] = new QtCanvasPixmap(list.at(i), hotspots.at(i));
3073 }
3074 }
3075}
3076
3077/*
3078 Destroys the pixmap array and all the pixmaps it contains.
3079*/
3081{
3082 reset();
3083}
3084
3085void
3086QtCanvasPixmapArray::reset()
3087{
3088 for (int i = 0; i < framecount; i++)
3089 {
3090 delete img[i];
3091 }
3092
3093 delete[] img;
3094 img = 0;
3095 framecount = 0;
3096}
3097
3098/*
3099 Reads one or more pixmaps into the pixmap array.
3100
3101 If \a fc is not 0, \a filenamepattern should contain "%1", e.g.
3102 "foo%1.png". The actual filenames are formed by replacing the %1
3103 with four-digit integers from 0 to (fc - 1), e.g. foo0000.png,
3104 foo0001.png, foo0002.png, etc.
3105
3106 If \a fc is 0, \a filenamepattern is asssumed to be a filename,
3107 and the image contained in this file will be loaded as the first
3108 (and only) frame.
3109
3110 If \a filenamepattern does not exist, is not readable, isn't an
3111 image, or some other error occurs, this function will return
3112 false, and isValid() will return false; otherwise this function
3113 will return true.
3114
3115 \sa isValid()
3116*/
3117bool
3118QtCanvasPixmapArray::readPixmaps(const QString& filenamepattern, int fc)
3119{
3120 return readPixmaps(filenamepattern, fc, false);
3121}
3122
3123/*
3124 Reads new collision masks for the array.
3125
3126 By default, QtCanvasSprite uses the image mask of a sprite to
3127 detect collisions. Use this function to set your own collision
3128 image masks.
3129
3130 If count() is 1 \a filename must specify a real filename to read
3131 the mask from. If count() is greater than 1, the \a filename must
3132 contain a "%1" that will get replaced by the number of the mask to
3133 be loaded, just like QtCanvasPixmapArray::readPixmaps().
3134
3135 All collision masks must be 1-bit images or this function call
3136 will fail.
3137
3138 If the file isn't readable, contains the wrong number of images,
3139 or there is some other error, this function will return false, and
3140 the array will be flagged as invalid; otherwise this function
3141 returns true.
3142
3143 \sa isValid()
3144*/
3145bool
3146QtCanvasPixmapArray::readCollisionMasks(const QString& filename)
3147{
3148 return readPixmaps(filename, framecount, true);
3149}
3150
3151bool
3152QtCanvasPixmapArray::readPixmaps(const QString& datafilenamepattern, int fc, bool maskonly)
3153{
3154 if (!maskonly)
3155 {
3156 reset();
3157 framecount = fc;
3158
3159 if (!framecount)
3160 {
3161 framecount = 1;
3162 }
3163
3164 img = new QtCanvasPixmap*[framecount];
3165 }
3166
3167 if (!img)
3168 {
3169 return false;
3170 }
3171
3172 bool ok = true;
3173 bool arg = fc > 1;
3174
3175 if (!arg)
3176 {
3177 framecount = 1;
3178 }
3179
3180 for (int i = 0; i < framecount; i++)
3181 {
3182 QString r;
3183 r.sprintf("%04d", i);
3184
3185 if (maskonly)
3186 {
3187 if (!img[i]->collision_mask)
3188 {
3189 img[i]->collision_mask = new QImage();
3190 }
3191
3192 img[i]->collision_mask->load(arg ? datafilenamepattern.arg(r) : datafilenamepattern);
3193 ok = ok && !img[i]->collision_mask->isNull() && img[i]->collision_mask->depth() == 1;
3194 }
3195 else
3196 {
3197 img[i] = new QtCanvasPixmap(arg ? datafilenamepattern.arg(r) : datafilenamepattern);
3198 ok = ok && !img[i]->isNull();
3199 }
3200 }
3201
3202 if (!ok)
3203 {
3204 reset();
3205 }
3206
3207 return ok;
3208}
3209
3210/*
3211 \obsolete
3212
3213 Use isValid() instead.
3214
3215 This returns false if the array is valid, and true if it is not.
3216*/
3217bool
3219{
3220 return img == 0;
3221}
3222
3223/*
3224 Returns true if the pixmap array is valid; otherwise returns
3225 false.
3226*/
3227bool
3229{
3230 return (img != 0);
3231}
3232
3233/*
3234 \fn QtCanvasPixmap* QtCanvasPixmapArray::image(int i) const
3235
3236 Returns pixmap \a i in the array, if \a i is non-negative and less
3237 than than count(), and returns an unspecified value otherwise.
3238*/
3239
3240// ### wouldn't it be better to put empty QtCanvasPixmaps in there instead of
3241// initializing the additional elements in the array to 0? Lars
3242/*
3243 Replaces the pixmap at index \a i with pixmap \a p.
3244
3245 The array takes ownership of \a p and will delete \a p when the
3246 array itself is deleted.
3247
3248 If \a i is beyond the end of the array the array is extended to at
3249 least i+1 elements, with elements count() to i-1 being initialized
3250 to 0.
3251*/
3252void
3254{
3255 if (i >= framecount)
3256 {
3257 QtCanvasPixmap** newimg = new QtCanvasPixmap*[i + 1];
3258 memcpy(newimg, img, sizeof(QtCanvasPixmap*) * framecount);
3259 memset(newimg + framecount, 0, sizeof(QtCanvasPixmap*) * (i + 1 - framecount));
3260 framecount = i + 1;
3261 delete[] img;
3262 img = newimg;
3263 }
3264
3265 delete img[i];
3266 img[i] = p;
3267}
3268
3269/*
3270 \fn uint QtCanvasPixmapArray::count() const
3271
3272 Returns the number of pixmaps in the array.
3273*/
3274
3275/*
3276 Returns the x-coordinate of the current left edge of the sprite.
3277 (This may change as the sprite animates since different frames may
3278 have different left edges.)
3279
3280 \sa rightEdge() bottomEdge() topEdge()
3281*/
3282int
3284{
3285 return int(x()) - image()->hotx;
3286}
3287
3288/*
3289 \overload
3290
3291 Returns what the x-coordinate of the left edge of the sprite would
3292 be if the sprite (actually its hotspot) were moved to x-position
3293 \a nx.
3294
3295 \sa rightEdge() bottomEdge() topEdge()
3296*/
3297int
3298QtCanvasSprite::leftEdge(int nx) const
3299{
3300 return nx - image()->hotx;
3301}
3302
3303/*
3304 Returns the y-coordinate of the top edge of the sprite. (This may
3305 change as the sprite animates since different frames may have
3306 different top edges.)
3307
3308 \sa leftEdge() rightEdge() bottomEdge()
3309*/
3310int
3312{
3313 return int(y()) - image()->hoty;
3314}
3315
3316/*
3317 \overload
3318
3319 Returns what the y-coordinate of the top edge of the sprite would
3320 be if the sprite (actually its hotspot) were moved to y-position
3321 \a ny.
3322
3323 \sa leftEdge() rightEdge() bottomEdge()
3324*/
3325int
3326QtCanvasSprite::topEdge(int ny) const
3327{
3328 return ny - image()->hoty;
3329}
3330
3331/*
3332 Returns the x-coordinate of the current right edge of the sprite.
3333 (This may change as the sprite animates since different frames may
3334 have different right edges.)
3335
3336 \sa leftEdge() bottomEdge() topEdge()
3337*/
3338int
3340{
3341 return leftEdge() + image()->width() - 1;
3342}
3343
3344/*
3345 \overload
3346
3347 Returns what the x-coordinate of the right edge of the sprite
3348 would be if the sprite (actually its hotspot) were moved to
3349 x-position \a nx.
3350
3351 \sa leftEdge() bottomEdge() topEdge()
3352*/
3353int
3354QtCanvasSprite::rightEdge(int nx) const
3355{
3356 return leftEdge(nx) + image()->width() - 1;
3357}
3358
3359/*
3360 Returns the y-coordinate of the current bottom edge of the sprite.
3361 (This may change as the sprite animates since different frames may
3362 have different bottom edges.)
3363
3364 \sa leftEdge() rightEdge() topEdge()
3365*/
3366int
3368{
3369 return topEdge() + image()->height() - 1;
3370}
3371
3372/*
3373 \overload
3374
3375 Returns what the y-coordinate of the top edge of the sprite would
3376 be if the sprite (actually its hotspot) were moved to y-position
3377 \a ny.
3378
3379 \sa leftEdge() rightEdge() topEdge()
3380*/
3381int
3382QtCanvasSprite::bottomEdge(int ny) const
3383{
3384 return topEdge(ny) + image()->height() - 1;
3385}
3386
3387/*
3388 \fn QtCanvasPixmap* QtCanvasSprite::image() const
3389
3390 Returns the current frame's image.
3391
3392 \sa frame(), setFrame()
3393*/
3394
3395/*
3396 \fn QtCanvasPixmap* QtCanvasSprite::image(int f) const
3397 \overload
3398
3399 Returns the image for frame \a f. Does not do any bounds checking on \a f.
3400*/
3401
3402/*
3403 Returns the image the sprite \e will have after advance(1) is
3404 called. By default this is the same as image().
3405*/
3408{
3409 return image();
3410}
3411
3412/*
3413 Returns the bounding rectangle for the image in the sprite's
3414 current frame. This assumes that the images are tightly cropped
3415 (i.e. do not have transparent pixels all along a side).
3416*/
3417QRect
3419{
3420 return QRect(leftEdge(), topEdge(), width(), height());
3421}
3422
3423/*
3424 \internal
3425 Returns the chunks covered by the item.
3426*/
3427QPolygon
3428QtCanvasItem::chunks() const
3429{
3430 QPolygon r;
3431 int n = 0;
3432 QRect br = boundingRect();
3433
3434 if (isVisible() && canvas())
3435 {
3436 int chunksize = canvas()->chunkSize();
3437 br &= QRect(0, 0, canvas()->width(), canvas()->height());
3438
3439 if (br.isValid())
3440 {
3441 r.resize((br.width() / chunksize + 2) * (br.height() / chunksize + 2));
3442
3443 for (int j = br.top() / chunksize; j <= br.bottom() / chunksize; j++)
3444 {
3445 for (int i = br.left() / chunksize; i <= br.right() / chunksize; i++)
3446 {
3447 r[n++] = QPoint(i, j);
3448 }
3449 }
3450 }
3451 }
3452
3453 r.resize(n);
3454 return r;
3455}
3456
3457/*
3458 \internal
3459 Add the sprite to the chunks in its QtCanvas which it overlaps.
3460*/
3461void
3462QtCanvasSprite::addToChunks()
3463{
3464 if (isVisible() && canvas())
3465 {
3466 int chunksize = canvas()->chunkSize();
3467
3468 for (int j = topEdge() / chunksize; j <= bottomEdge() / chunksize; j++)
3469 {
3470 for (int i = leftEdge() / chunksize; i <= rightEdge() / chunksize; i++)
3471 {
3472 canvas()->addItemToChunk(this, i, j);
3473 }
3474 }
3475 }
3476}
3477
3478/*
3479 \internal
3480 Remove the sprite from the chunks in its QtCanvas which it overlaps.
3481
3482 \sa addToChunks()
3483*/
3484void
3485QtCanvasSprite::removeFromChunks()
3486{
3487 if (isVisible() && canvas())
3488 {
3489 int chunksize = canvas()->chunkSize();
3490
3491 for (int j = topEdge() / chunksize; j <= bottomEdge() / chunksize; j++)
3492 {
3493 for (int i = leftEdge() / chunksize; i <= rightEdge() / chunksize; i++)
3494 {
3495 canvas()->removeItemFromChunk(this, i, j);
3496 }
3497 }
3498 }
3499}
3500
3501/*
3502 The width of the sprite for the current frame's image.
3503
3504 \sa frame()
3505*/
3506//### mark: Why don't we have width(int) and height(int) to be
3507//consistent with leftEdge() and leftEdge(int)?
3508int
3510{
3511 return image()->width();
3512}
3513
3514/*
3515 The height of the sprite for the current frame's image.
3516
3517 \sa frame()
3518*/
3519int
3521{
3522 return image()->height();
3523}
3524
3525/*
3526 Draws the current frame's image at the sprite's current position
3527 on painter \a painter.
3528*/
3529void
3530QtCanvasSprite::draw(QPainter& painter)
3531{
3532 painter.drawPixmap(leftEdge(), topEdge(), *image());
3533}
3534
3535/*
3536 \class QtCanvasView qtcanvas.h
3537 \brief The QtCanvasView class provides an on-screen view of a QtCanvas.
3538
3539 A QtCanvasView is widget which provides a view of a QtCanvas.
3540
3541 If you want users to be able to interact with a canvas view,
3542 subclass QtCanvasView. You might then reimplement
3543 QtScrollView::contentsMousePressEvent(). For example:
3544
3545 \code
3546 void MyCanvasView::contentsMousePressEvent(QMouseEvent* e)
3547 {
3548 QtCanvasItemList l = canvas()->collisions(e->pos());
3549 for (QtCanvasItemList::Iterator it = l.begin(); it!= l.end(); ++it) {
3550 if ((*it)->rtti() == QtCanvasRectangle::RTTI)
3551 qDebug("A QtCanvasRectangle lies somewhere at this point");
3552 }
3553 }
3554 \endcode
3555
3556 The canvas view shows canvas canvas(); this can be changed using
3557 setCanvas().
3558
3559 A transformation matrix can be used to transform the view of the
3560 canvas in various ways, for example, zooming in or out or rotating.
3561 For example:
3562
3563 \code
3564 QMatrix wm;
3565 wm.scale(2, 2); // Zooms in by 2 times
3566 wm.rotate(90); // Rotates 90 degrees counter clockwise
3567 // around the origin.
3568 wm.translate(0, -canvas->height());
3569 // moves the canvas down so what was visible
3570 // before is still visible.
3571 myCanvasView->setWorldMatrix(wm);
3572 \endcode
3573
3574 Use setWorldMatrix() to set the canvas view's world matrix: you must
3575 ensure that the world matrix is invertible. The current world matrix
3576 is retrievable with worldMatrix(), and its inversion is retrievable
3577 with inverseWorldMatrix().
3578
3579 Example:
3580
3581 The following code finds the part of the canvas that is visible in
3582 this view, i.e. the bounding rectangle of the view in canvas coordinates.
3583
3584 \code
3585 QRect rc = QRect(myCanvasView->contentsX(), myCanvasView->contentsY(),
3586 myCanvasView->visibleWidth(), myCanvasView->visibleHeight());
3587 QRect canvasRect = myCanvasView->inverseWorldMatrix().mapRect(rc);
3588 \endcode
3589
3590 \sa QMatrix QPainter::setWorldMatrix()
3591
3592*/
3593
3594class QtCanvasWidget : public QWidget
3595{
3596public:
3597 QtCanvasWidget(QtCanvasView* view) : QWidget(view)
3598 {
3599 m_view = view;
3600 }
3601
3602protected:
3603 void paintEvent(QPaintEvent* e);
3604
3605 void
3606 mousePressEvent(QMouseEvent* e)
3607 {
3608 m_view->contentsMousePressEvent(e);
3609 }
3610
3611 void
3612 mouseMoveEvent(QMouseEvent* e)
3613 {
3614 m_view->contentsMouseMoveEvent(e);
3615 }
3616
3617 void
3618 mouseReleaseEvent(QMouseEvent* e)
3619 {
3620 m_view->contentsMouseReleaseEvent(e);
3621 }
3622
3623 void
3625 {
3626 m_view->contentsMouseDoubleClickEvent(e);
3627 }
3628
3629 void
3630 dragEnterEvent(QDragEnterEvent* e)
3631 {
3632 m_view->contentsDragEnterEvent(e);
3633 }
3634
3635 void
3636 dragMoveEvent(QDragMoveEvent* e)
3637 {
3638 m_view->contentsDragMoveEvent(e);
3639 }
3640
3641 void
3642 dragLeaveEvent(QDragLeaveEvent* e)
3643 {
3644 m_view->contentsDragLeaveEvent(e);
3645 }
3646
3647 void
3648 dropEvent(QDropEvent* e)
3649 {
3650 m_view->contentsDropEvent(e);
3651 }
3652
3653 void
3654 wheelEvent(QWheelEvent* e)
3655 {
3656 m_view->contentsWheelEvent(e);
3657 }
3658
3659 void
3660 contextMenuEvent(QContextMenuEvent* e)
3661 {
3662 m_view->contentsContextMenuEvent(e);
3663 }
3664
3666};
3667
3668void
3669QtCanvasWidget::paintEvent(QPaintEvent* e)
3670{
3671 QPainter p(this);
3672
3673 if (m_view->d->highQuality)
3674 {
3675 p.setRenderHint(QPainter::Antialiasing);
3676 p.setRenderHint(QPainter::SmoothPixmapTransform);
3677 }
3678
3679 m_view->drawContents(&p, e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height());
3680}
3681
3682/*
3683 Constructs a QtCanvasView with parent \a parent. The canvas view
3684 is not associated with a canvas, so you must to call setCanvas()
3685 to view a canvas.
3686*/
3687QtCanvasView::QtCanvasView(QWidget* parent) : QScrollArea(parent)
3688{
3689 d = new QtCanvasViewData;
3690 setWidget(new QtCanvasWidget(this));
3691 d->highQuality = false;
3692 viewing = 0;
3693 setCanvas(0);
3694}
3695
3696/*
3697 \overload
3698
3699 Constructs a QtCanvasView which views canvas \a canvas, with parent
3700 \a parent.
3701*/
3702QtCanvasView::QtCanvasView(QtCanvas* canvas, QWidget* parent) : QScrollArea(parent)
3703{
3704 d = new QtCanvasViewData;
3705 d->highQuality = false;
3706 setWidget(new QtCanvasWidget(this));
3707 viewing = 0;
3708 setCanvas(canvas);
3709}
3710
3711/*
3712 Destroys the canvas view. The associated canvas is \e not deleted.
3713*/
3715{
3716 delete d;
3717 d = 0;
3718 setCanvas(0);
3719}
3720
3721/*
3722 \property QtCanvasView::highQualityRendering
3723 \brief whether high quality rendering is turned on
3724
3725 If high quality rendering is turned on, the canvas view will paint itself
3726 using the QPainter::Antialiasing and QPainter::SmoothPixmapTransform
3727 rendering flags.
3728
3729 Enabling these flag will usually improve the visual appearance on the screen
3730 at the cost of rendering speed.
3731*/
3732bool
3734{
3735 return d->highQuality;
3736}
3737
3738void
3740{
3741 d->highQuality = enable;
3742 widget()->update();
3743}
3744
3745void
3747{
3748 e->ignore();
3749}
3750
3751void
3753{
3754 e->ignore();
3755}
3756
3757void
3759{
3760 e->ignore();
3761}
3762
3763void
3765{
3766 e->ignore();
3767}
3768
3769void
3771{
3772}
3773
3774void
3776{
3777}
3778
3779void
3781{
3782}
3783
3784void
3786{
3787}
3788
3789void
3791{
3792 e->ignore();
3793}
3794
3795void
3796QtCanvasView::contentsContextMenuEvent(QContextMenuEvent* e)
3797{
3798 e->ignore();
3799}
3800
3801/*
3802 \fn QtCanvas* QtCanvasView::canvas() const
3803
3804 Returns a pointer to the canvas which the QtCanvasView is currently
3805 showing.
3806*/
3807
3808
3809/*
3810 Sets the canvas that the QtCanvasView is showing to the canvas \a
3811 canvas.
3812*/
3813void
3815{
3816 if (viewing == canvas)
3817 {
3818 return;
3819 }
3820
3821 if (viewing)
3822 {
3823 disconnect(viewing);
3824 viewing->removeView(this);
3825 }
3826
3827 viewing = canvas;
3828
3829 if (viewing)
3830 {
3831 connect(viewing, SIGNAL(resized()), this, SLOT(updateContentsSize()));
3832 viewing->addView(this);
3833 }
3834
3835 if (d) // called by d'tor
3836 {
3837 updateContentsSize();
3838 }
3839
3840 update();
3841}
3842
3843/*
3844 Returns a reference to the canvas view's current transformation matrix.
3845
3846 \sa setWorldMatrix() inverseWorldMatrix()
3847*/
3848const QMatrix&
3850{
3851 return d->xform;
3852}
3853
3854/*
3855 Returns a reference to the inverse of the canvas view's current
3856 transformation matrix.
3857
3858 \sa setWorldMatrix() worldMatrix()
3859*/
3860const QMatrix&
3862{
3863 return d->ixform;
3864}
3865
3866/*
3867 Sets the transformation matrix of the QtCanvasView to \a wm. The
3868 matrix must be invertible (i.e. if you create a world matrix that
3869 zooms out by 2 times, then the inverse of this matrix is one that
3870 will zoom in by 2 times).
3871
3872 When you use this, you should note that the performance of the
3873 QtCanvasView will decrease considerably.
3874
3875 Returns false if \a wm is not invertable; otherwise returns true.
3876
3877 \sa worldMatrix() inverseWorldMatrix() QMatrix::isInvertible()
3878*/
3879bool
3880QtCanvasView::setWorldMatrix(const QMatrix& wm)
3881{
3882 bool ok = wm.isInvertible();
3883
3884 if (ok)
3885 {
3886 d->xform = wm;
3887 d->ixform = wm.inverted();
3888 updateContentsSize();
3889 widget()->update();
3890 }
3891
3892 return ok;
3893}
3894
3895void
3896QtCanvasView::updateContentsSize()
3897{
3898 if (viewing)
3899 {
3900 QRect br;
3901 br = d->xform.mapRect(QRect(0, 0, viewing->width(), viewing->height()));
3902
3903 widget()->resize(br.size());
3904 }
3905 else
3906 {
3907 widget()->resize(size());
3908 }
3909}
3910
3911/*
3912 Repaints part of the QtCanvas that the canvas view is showing
3913 starting at \a cx by \a cy, with a width of \a cw and a height of \a
3914 ch using the painter \a p.
3915*/
3916void
3917QtCanvasView::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
3918{
3919 if (!viewing)
3920 {
3921 return;
3922 }
3923
3924 QPainterPath clipPath;
3925 clipPath.addRect(viewing->rect());
3926 p->setClipPath(d->xform.map(clipPath), Qt::IntersectClip);
3927 viewing->drawViewArea(this, p, QRect(cx, cy, cw, ch), false);
3928}
3929
3930/*
3931 Suggests a size sufficient to view the entire canvas.
3932*/
3933QSize
3935{
3936 if (!canvas())
3937 {
3938 return QScrollArea::sizeHint();
3939 }
3940
3941 // should maybe take transformations into account
3942 return (canvas()->size() + 2 * QSize(frameWidth(), frameWidth()))
3943 .boundedTo(3 * QApplication::desktop()->size() / 4);
3944}
3945
3946/*
3947 \class QtCanvasPolygonalItem qtcanvas.h
3948 \brief The QtCanvasPolygonalItem class provides a polygonal canvas item
3949 on a QtCanvas.
3950
3951 The mostly rectangular classes, such as QtCanvasSprite and
3952 QtCanvasText, use the object's bounding rectangle for movement,
3953 repainting and collision calculations. For most other items, the
3954 bounding rectangle can be far too large -- a diagonal line being
3955 the worst case, and there are many other cases which are also bad.
3956 QtCanvasPolygonalItem provides polygon-based bounding rectangle
3957 handling, etc., which is much faster for non-rectangular items.
3958
3959 Derived classes should try to define as small an area as possible
3960 to maximize efficiency, but the polygon must \e definitely be
3961 contained completely within the polygonal area. Calculating the
3962 exact requirements is usually difficult, but if you allow a small
3963 overestimate it can be easy and quick, while still getting almost
3964 all of QtCanvasPolygonalItem's speed.
3965
3966 Note that all subclasses \e must call hide() in their destructor
3967 since hide() needs to be able to access areaPoints().
3968
3969 Normally, QtCanvasPolygonalItem uses the odd-even algorithm for
3970 determining whether an object intersects this object. You can
3971 change this to the winding algorithm using setWinding().
3972
3973 The bounding rectangle is available using boundingRect(). The
3974 points bounding the polygonal item are retrieved with
3975 areaPoints(). Use areaPointsAdvanced() to retrieve the bounding
3976 points the polygonal item \e will have after
3977 QtCanvasItem::advance(1) has been called.
3978
3979 If the shape of the polygonal item is about to change while the
3980 item is visible, call invalidate() before updating with a
3981 different result from \l areaPoints().
3982
3983 By default, QtCanvasPolygonalItem objects have a black pen and no
3984 brush (the default QPen and QBrush constructors). You can change
3985 this with setPen() and setBrush(), but note that some
3986 QtCanvasPolygonalItem subclasses only use the brush, ignoring the
3987 pen setting.
3988
3989 The polygonal item can be drawn on a painter with draw().
3990 Subclasses must reimplement drawShape() to draw themselves.
3991
3992 Like any other canvas item polygonal items can be moved with
3993 QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting coordinates
3994 with QtCanvasItem::setX(), QtCanvasItem::setY() and QtCanvasItem::setZ().
3995
3996*/
3997
3998
3999/*
4000 Since most polygonal items don't have a pen, the default is
4001 NoPen and a black brush.
4002*/
4003static const QPen&
4004defaultPolygonPen()
4005{
4006 static QPen* dp = 0;
4007
4008 if (!dp)
4009 {
4010 dp = new QPen;
4011 }
4012
4013 return *dp;
4014}
4015
4016static const QBrush&
4017defaultPolygonBrush()
4018{
4019 static QBrush* db = 0;
4020
4021 if (!db)
4022 {
4023 db = new QBrush;
4024 }
4025
4026 return *db;
4027}
4028
4029/*
4030 Constructs a QtCanvasPolygonalItem on the canvas \a canvas.
4031*/
4033 QtCanvasItem(canvas), br(defaultPolygonBrush()), pn(defaultPolygonPen())
4034{
4035 wind = 0;
4036}
4037
4038/*
4039 Note that all subclasses \e must call hide() in their destructor
4040 since hide() needs to be able to access areaPoints().
4041*/
4043{
4044}
4045
4046/*
4047 Returns true if the polygonal item uses the winding algorithm to
4048 determine the "inside" of the polygon. Returns false if it uses
4049 the odd-even algorithm.
4050
4051 The default is to use the odd-even algorithm.
4052
4053 \sa setWinding()
4054*/
4055bool
4057{
4058 return wind;
4059}
4060
4061/*
4062 If \a enable is true, the polygonal item will use the winding
4063 algorithm to determine the "inside" of the polygon; otherwise the
4064 odd-even algorithm will be used.
4065
4066 The default is to use the odd-even algorithm.
4067
4068 \sa winding()
4069*/
4070void
4072{
4073 wind = enable;
4074}
4075
4076/*
4077 Invalidates all information about the area covered by the canvas
4078 item. The item will be updated automatically on the next call that
4079 changes the item's status, for example, move() or update(). Call
4080 this function if you are going to change the shape of the item (as
4081 returned by areaPoints()) while the item is visible.
4082*/
4083void
4085{
4086 val = (uint) false;
4087 removeFromChunks();
4088}
4089
4090/*
4091 \fn QtCanvasPolygonalItem::isValid() const
4092
4093 Returns true if the polygonal item's area information has not been
4094 invalidated; otherwise returns false.
4095
4096 \sa invalidate()
4097*/
4098
4099/*
4100 Returns the points the polygonal item \e will have after
4101 QtCanvasItem::advance(1) is called, i.e. what the points are when
4102 advanced by the current xVelocity() and yVelocity().
4103*/
4104QPolygon
4106{
4107 int dx = int(x() + xVelocity()) - int(x());
4108 int dy = int(y() + yVelocity()) - int(y());
4109 QPolygon r = areaPoints();
4110 r.detach(); // Explicit sharing is stupid.
4111
4112 if (dx || dy)
4113 {
4114 r.translate(dx, dy);
4115 }
4116
4117 return r;
4118}
4119
4120//#define QCANVAS_POLYGONS_DEBUG
4121#ifdef QCANVAS_POLYGONS_DEBUG
4122static QWidget* dbg_wid = 0;
4123static QPainter* dbg_ptr = 0;
4124#endif
4125
4127{
4128public:
4129 QPolygonalProcessor(QtCanvas* c, const QPolygon& pa) : canvas(c)
4130 {
4131 QRect pixelbounds = pa.boundingRect();
4132 int cs = canvas->chunkSize();
4133 QRect canvasbounds = pixelbounds.intersected(canvas->rect());
4134 bounds.setLeft(canvasbounds.left() / cs);
4135 bounds.setRight(canvasbounds.right() / cs);
4136 bounds.setTop(canvasbounds.top() / cs);
4137 bounds.setBottom(canvasbounds.bottom() / cs);
4138 bitmap = QImage(bounds.width(), bounds.height(), QImage::Format_MonoLSB);
4139 pnt = 0;
4140 bitmap.fill(0);
4141#ifdef QCANVAS_POLYGONS_DEBUG
4142 dbg_start();
4143#endif
4144 }
4145
4146 inline void
4147 add(int x, int y)
4148 {
4149 if (pnt >= (int)result.size())
4150 {
4151 result.resize(pnt * 2 + 10);
4152 }
4153
4154 result[pnt++] = QPoint(x + bounds.x(), y + bounds.y());
4155#ifdef QCANVAS_POLYGONS_DEBUG
4156
4157 if (dbg_ptr)
4158 {
4159 int cs = canvas->chunkSize();
4160 QRect r(x * cs + bounds.x() * cs, y * cs + bounds.y() * cs, cs - 1, cs - 1);
4161 dbg_ptr->setPen(Qt::blue);
4162 dbg_ptr->drawRect(r);
4163 }
4164
4165#endif
4166 }
4167
4168 inline void
4169 addBits(int x1, int x2, uchar newbits, int xo, int yo)
4170 {
4171 for (int i = x1; i <= x2; i++)
4172 if (newbits & (1 << i))
4173 {
4174 add(xo + i, yo);
4175 }
4176 }
4177
4178#ifdef QCANVAS_POLYGONS_DEBUG
4179 void
4180 dbg_start()
4181 {
4182 if (!dbg_wid)
4183 {
4184 dbg_wid = new QWidget;
4185 dbg_wid->resize(800, 600);
4186 dbg_wid->show();
4187 dbg_ptr = new QPainter(dbg_wid);
4188 dbg_ptr->setBrush(Qt::NoBrush);
4189 }
4190
4191 dbg_ptr->fillRect(dbg_wid->rect(), Qt::white);
4192 }
4193#endif
4194
4195 void
4196 doSpans(int n, QPoint* pt, int* w)
4197 {
4198 int cs = canvas->chunkSize();
4199
4200 for (int j = 0; j < n; j++)
4201 {
4202 int y = pt[j].y() / cs - bounds.y();
4203
4204 if (y >= bitmap.height() || y < 0)
4205 {
4206 continue;
4207 }
4208
4209 uchar* l = bitmap.scanLine(y);
4210 int x = pt[j].x();
4211 int x1 = x / cs - bounds.x();
4212
4213 if (x1 > bounds.width())
4214 {
4215 continue;
4216 }
4217
4218 x1 = qMax(0, x1);
4219 int x2 = (x + w[j]) / cs - bounds.x();
4220
4221 if (x2 < 0)
4222 {
4223 continue;
4224 }
4225
4226 x2 = qMin(bounds.width(), x2);
4227 int x1q = x1 / 8;
4228 int x1r = x1 % 8;
4229 int x2q = x2 / 8;
4230 int x2r = x2 % 8;
4231#ifdef QCANVAS_POLYGONS_DEBUG
4232
4233 if (dbg_ptr)
4234 {
4235 dbg_ptr->setPen(Qt::yellow);
4236 }
4237
4238#endif
4239
4240 if (x1q == x2q)
4241 {
4242 uchar newbits = (~l[x1q]) & (((2 << (x2r - x1r)) - 1) << x1r);
4243
4244 if (newbits)
4245 {
4246#ifdef QCANVAS_POLYGONS_DEBUG
4247
4248 if (dbg_ptr)
4249 {
4250 dbg_ptr->setPen(Qt::darkGreen);
4251 }
4252
4253#endif
4254 addBits(x1r, x2r, newbits, x1q * 8, y);
4255 l[x1q] |= newbits;
4256 }
4257 }
4258 else
4259 {
4260#ifdef QCANVAS_POLYGONS_DEBUG
4261
4262 if (dbg_ptr)
4263 {
4264 dbg_ptr->setPen(Qt::blue);
4265 }
4266
4267#endif
4268 uchar newbits1 = (~l[x1q]) & (0xff << x1r);
4269
4270 if (newbits1)
4271 {
4272#ifdef QCANVAS_POLYGONS_DEBUG
4273
4274 if (dbg_ptr)
4275 {
4276 dbg_ptr->setPen(Qt::green);
4277 }
4278
4279#endif
4280 addBits(x1r, 7, newbits1, x1q * 8, y);
4281 l[x1q] |= newbits1;
4282 }
4283
4284 for (int i = x1q + 1; i < x2q; i++)
4285 {
4286 if (l[i] != 0xff)
4287 {
4288 addBits(0, 7, ~l[i], i * 8, y);
4289 l[i] = 0xff;
4290 }
4291 }
4292
4293 uchar newbits2 = (~l[x2q]) & (0xff >> (7 - x2r));
4294
4295 if (newbits2)
4296 {
4297#ifdef QCANVAS_POLYGONS_DEBUG
4298
4299 if (dbg_ptr)
4300 {
4301 dbg_ptr->setPen(Qt::red);
4302 }
4303
4304#endif
4305 addBits(0, x2r, newbits2, x2q * 8, y);
4306 l[x2q] |= newbits2;
4307 }
4308 }
4309
4310#ifdef QCANVAS_POLYGONS_DEBUG
4311
4312 if (dbg_ptr)
4313 {
4314 dbg_ptr->drawLine(pt[j], pt[j] + QPoint(w[j], 0));
4315 }
4316
4317#endif
4318 }
4319
4320 result.resize(pnt);
4321 }
4322
4323 int pnt;
4324 QPolygon result;
4326 QRect bounds;
4327 QImage bitmap;
4328};
4329
4330QPolygon
4331QtCanvasPolygonalItem::chunks() const
4332{
4333 QPolygon pa = areaPoints();
4334
4335 if (!pa.size())
4336 {
4337 pa.detach(); // Explicit sharing is stupid.
4338 return pa;
4339 }
4340
4341 QPolygonalProcessor processor(canvas(), pa);
4342
4343 scanPolygon(pa, wind, processor);
4344
4345 return processor.result;
4346}
4347
4348/*
4349 Simply calls QtCanvasItem::chunks().
4350*/
4351QPolygon
4353{
4354 // No need to do a polygon scan!
4355 return QtCanvasItem::chunks();
4356}
4357
4358/*
4359 Returns the bounding rectangle of the polygonal item, based on
4360 areaPoints().
4361*/
4362QRect
4364{
4365 return areaPoints().boundingRect();
4366}
4367
4368/*
4369 Reimplemented from QtCanvasItem, this draws the polygonal item by
4370 setting the pen and brush for the item on the painter \a p and
4371 calling drawShape().
4372*/
4373void
4374QtCanvasPolygonalItem::draw(QPainter& p)
4375{
4376 p.setPen(pn);
4377 p.setBrush(br);
4378 drawShape(p);
4379}
4380
4381/*
4382 \fn void QtCanvasPolygonalItem::drawShape(QPainter & p)
4383
4384 Subclasses must reimplement this function to draw their shape. The
4385 pen and brush of \a p are already set to pen() and brush() prior
4386 to calling this function.
4387
4388 \sa draw()
4389*/
4390
4391/*
4392 \fn QPen QtCanvasPolygonalItem::pen() const
4393
4394 Returns the QPen used to draw the outline of the item, if any.
4395
4396 \sa setPen()
4397*/
4398
4399/*
4400 \fn QBrush QtCanvasPolygonalItem::brush() const
4401
4402 Returns the QBrush used to fill the item, if filled.
4403
4404 \sa setBrush()
4405*/
4406
4407/*
4408 Sets the QPen used when drawing the item to the pen \a p.
4409 Note that many QtCanvasPolygonalItems do not use the pen value.
4410
4411 \sa setBrush(), pen(), drawShape()
4412*/
4413void
4415{
4416 if (pn != p)
4417 {
4418 removeFromChunks();
4419 pn = p;
4420 addToChunks();
4421 }
4422}
4423
4424/*
4425 Sets the QBrush used when drawing the polygonal item to the brush \a b.
4426
4427 \sa setPen(), brush(), drawShape()
4428*/
4429void
4431{
4432 if (br != b)
4433 {
4434 br = b;
4435 changeChunks();
4436 }
4437}
4438
4439/*
4440 \class QtCanvasPolygon qtcanvas.h
4441 \brief The QtCanvasPolygon class provides a polygon on a QtCanvas.
4442
4443 Paints a polygon with a QBrush. The polygon's points can be set in
4444 the constructor or set or changed later using setPoints(). Use
4445 points() to retrieve the points, or areaPoints() to retrieve the
4446 points relative to the canvas's origin.
4447
4448 The polygon can be drawn on a painter with drawShape().
4449
4450 Like any other canvas item polygons can be moved with
4451 QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting
4452 coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and
4453 QtCanvasItem::setZ().
4454
4455 Note: QtCanvasPolygon does not use the pen.
4456*/
4457
4458/*
4459 Constructs a point-less polygon on the canvas \a canvas. You
4460 should call setPoints() before using it further.
4461*/
4463{
4464}
4465
4466/*
4467 Destroys the polygon.
4468*/
4470{
4471 hide();
4472}
4473
4474/*
4475 Draws the polygon using the painter \a p.
4476
4477 Note that QtCanvasPolygon does not support an outline (the pen is
4478 always NoPen).
4479*/
4480void
4481QtCanvasPolygon::drawShape(QPainter& p)
4482{
4483 // ### why can't we draw outlines? We could use drawPolyline for it. Lars
4484 // ### see other message. Warwick
4485
4486 p.setPen(NoPen); // since QRegion(QPolygon) excludes outline :-()-:
4487 p.drawPolygon(poly);
4488}
4489
4490/*
4491 Sets the points of the polygon to be \a pa. These points will have
4492 their x and y coordinates automatically translated by x(), y() as
4493 the polygon is moved.
4494*/
4495void
4496QtCanvasPolygon::setPoints(QPolygon pa)
4497{
4498 removeFromChunks();
4499 poly = pa;
4500 poly.detach(); // Explicit sharing is stupid.
4501 poly.translate((int)x(), (int)y());
4502 addToChunks();
4503}
4504
4505/*
4506 \reimp
4507*/
4508void
4509QtCanvasPolygon::moveBy(double dx, double dy)
4510{
4511 // Note: does NOT call QtCanvasPolygonalItem::moveBy(), since that
4512 // only does half this work.
4513 //
4514 int idx = int(x() + dx) - int(x());
4515 int idy = int(y() + dy) - int(y());
4516
4517 if (idx || idy)
4518 {
4519 removeFromChunks();
4520 poly.translate(idx, idy);
4521 }
4522
4523 myx += dx;
4524 myy += dy;
4525
4526 if (idx || idy)
4527 {
4528 addToChunks();
4529 }
4530}
4531
4532/*
4533 \class QtCanvasSpline qtcanvas.h
4534 \brief The QtCanvasSpline class provides multi-bezier splines on a QtCanvas.
4535
4536 A QtCanvasSpline is a sequence of 4-point bezier curves joined
4537 together to make a curved shape.
4538
4539 You set the control points of the spline with setControlPoints().
4540
4541 If the bezier is closed(), then the first control point will be
4542 re-used as the last control point. Therefore, a closed bezier must
4543 have a multiple of 3 control points and an open bezier must have
4544 one extra point.
4545
4546 The beziers are not necessarily joined "smoothly". To ensure this,
4547 set control points appropriately (general reference texts about
4548 beziers will explain this in detail).
4549
4550 Like any other canvas item splines can be moved with
4551 QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting
4552 coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and
4553 QtCanvasItem::setZ().
4554
4555*/
4556
4557/*
4558 Create a spline with no control points on the canvas \a canvas.
4559
4560 \sa setControlPoints()
4561*/
4562QtCanvasSpline::QtCanvasSpline(QtCanvas* canvas) : QtCanvasPolygon(canvas), cl(true)
4563{
4564}
4565
4566/*
4567 Destroy the spline.
4568*/
4570{
4571}
4572
4573/*
4574 Set the spline control points to \a ctrl.
4575
4576 If \a close is true, then the first point in \a ctrl will be
4577 re-used as the last point, and the number of control points must
4578 be a multiple of 3. If \a close is false, one additional control
4579 point is required, and the number of control points must be one of
4580 (4, 7, 10, 13, ...).
4581
4582 If the number of control points doesn't meet the above conditions,
4583 the number of points will be truncated to the largest number of
4584 points that do meet the requirement.
4585*/
4586void
4587QtCanvasSpline::setControlPoints(QPolygon ctrl, bool close)
4588{
4589 if ((int)ctrl.count() % 3 != (close ? 0 : 1))
4590 {
4591 qWarning("QtCanvasSpline::setControlPoints(): Number of points doesn't fit.");
4592 int numCurves = (ctrl.count() - (close ? 0 : 1)) / 3;
4593 ctrl.resize(numCurves * 3 + (close ? 0 : 1));
4594 }
4595
4596 cl = close;
4597 bez = ctrl;
4598 recalcPoly();
4599}
4600
4601/*
4602 Returns the current set of control points.
4603
4604 \sa setControlPoints(), closed()
4605*/
4606QPolygon
4608{
4609 return bez;
4610}
4611
4612/*
4613 Returns true if the control points are a closed set; otherwise
4614 returns false.
4615*/
4616bool
4618{
4619 return cl;
4620}
4621
4622void
4623QtCanvasSpline::recalcPoly()
4624{
4625 if (bez.count() == 0)
4626 {
4627 return;
4628 }
4629
4630 QPainterPath path;
4631 path.moveTo(bez[0]);
4632
4633 for (int i = 1; i < (int)bez.count() - 1; i += 3)
4634 {
4635 path.cubicTo(bez[i], bez[i + 1], cl ? bez[(i + 2) % bez.size()] : bez[i + 2]);
4636 }
4637
4638 QPolygon p = path.toFillPolygon().toPolygon();
4640}
4641
4642/*
4643 \fn QPolygon QtCanvasPolygonalItem::areaPoints() const
4644
4645 This function must be reimplemented by subclasses. It \e must
4646 return the points bounding (i.e. outside and not touching) the
4647 shape or drawing errors will occur.
4648*/
4649
4650/*
4651 \fn QPolygon QtCanvasPolygon::points() const
4652
4653 Returns the vertices of the polygon, not translated by the position.
4654
4655 \sa setPoints(), areaPoints()
4656*/
4657QPolygon
4659{
4660 QPolygon pa = areaPoints();
4661 pa.translate(int(-x()), int(-y()));
4662 return pa;
4663}
4664
4665/*
4666 Returns the vertices of the polygon translated by the polygon's
4667 current x(), y() position, i.e. relative to the canvas's origin.
4668
4669 \sa setPoints(), points()
4670*/
4671QPolygon
4673{
4674 return poly;
4675}
4676
4677/*
4678 \class QtCanvasLine qtcanvas.h
4679 \brief The QtCanvasLine class provides a line on a QtCanvas.
4680
4681 The line inherits functionality from QtCanvasPolygonalItem, for
4682 example the setPen() function. The start and end points of the
4683 line are set with setPoints().
4684
4685 Like any other canvas item lines can be moved with
4686 QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting
4687 coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and
4688 QtCanvasItem::setZ().
4689*/
4690
4691/*
4692 Constructs a line from (0, 0) to (0, 0) on \a canvas.
4693
4694 \sa setPoints()
4695*/
4697{
4698 x1 = y1 = x2 = y2 = 0;
4699}
4700
4701/*
4702 Destroys the line.
4703*/
4705{
4706 hide();
4707}
4708
4709/*
4710 \reimp
4711*/
4712void
4714{
4716}
4717
4718/*
4719 \fn QPoint QtCanvasLine::startPoint () const
4720
4721 Returns the start point of the line.
4722
4723 \sa setPoints(), endPoint()
4724*/
4725
4726/*
4727 \fn QPoint QtCanvasLine::endPoint () const
4728
4729 Returns the end point of the line.
4730
4731 \sa setPoints(), startPoint()
4732*/
4733
4734/*
4735 Sets the line's start point to (\a xa, \a ya) and its end point to
4736 (\a xb, \a yb).
4737*/
4738void
4739QtCanvasLine::setPoints(int xa, int ya, int xb, int yb)
4740{
4741 if (x1 != xa || x2 != xb || y1 != ya || y2 != yb)
4742 {
4743 removeFromChunks();
4744 x1 = xa;
4745 y1 = ya;
4746 x2 = xb;
4747 y2 = yb;
4748 addToChunks();
4749 }
4750}
4751
4752/*
4753 \reimp
4754*/
4755void
4756QtCanvasLine::drawShape(QPainter& p)
4757{
4758 p.drawLine((int)(x() + x1), (int)(y() + y1), (int)(x() + x2), (int)(y() + y2));
4759}
4760
4761/*
4762 \reimp
4763
4764 Note that the area defined by the line is somewhat thicker than
4765 the line that is actually drawn.
4766*/
4767QPolygon
4769{
4770 QPolygon p(4);
4771 int xi = int(x());
4772 int yi = int(y());
4773 int pw = pen().width();
4774 int dx = qAbs(x1 - x2);
4775 int dy = qAbs(y1 - y2);
4776 pw = pw * 4 / 3 + 2; // approx pw*sqrt(2)
4777 int px = x1 < x2 ? -pw : pw;
4778 int py = y1 < y2 ? -pw : pw;
4779
4780 if (dx && dy && (dx > dy ? (dx * 2 / dy <= 2) : (dy * 2 / dx <= 2)))
4781 {
4782 // steep
4783 if (px == py)
4784 {
4785 p[0] = QPoint(x1 + xi, y1 + yi + py);
4786 p[1] = QPoint(x2 + xi - px, y2 + yi);
4787 p[2] = QPoint(x2 + xi, y2 + yi - py);
4788 p[3] = QPoint(x1 + xi + px, y1 + yi);
4789 }
4790 else
4791 {
4792 p[0] = QPoint(x1 + xi + px, y1 + yi);
4793 p[1] = QPoint(x2 + xi, y2 + yi - py);
4794 p[2] = QPoint(x2 + xi - px, y2 + yi);
4795 p[3] = QPoint(x1 + xi, y1 + yi + py);
4796 }
4797 }
4798 else if (dx > dy)
4799 {
4800 // horizontal
4801 p[0] = QPoint(x1 + xi + px, y1 + yi + py);
4802 p[1] = QPoint(x2 + xi - px, y2 + yi + py);
4803 p[2] = QPoint(x2 + xi - px, y2 + yi - py);
4804 p[3] = QPoint(x1 + xi + px, y1 + yi - py);
4805 }
4806 else
4807 {
4808 // vertical
4809 p[0] = QPoint(x1 + xi + px, y1 + yi + py);
4810 p[1] = QPoint(x2 + xi + px, y2 + yi - py);
4811 p[2] = QPoint(x2 + xi - px, y2 + yi - py);
4812 p[3] = QPoint(x1 + xi - px, y1 + yi + py);
4813 }
4814
4815 return p;
4816}
4817
4818/*
4819 \reimp
4820
4821*/
4822
4823void
4824QtCanvasLine::moveBy(double dx, double dy)
4825{
4827}
4828
4829/*
4830 \class QtCanvasRectangle qtcanvas.h
4831 \brief The QtCanvasRectangle class provides a rectangle on a QtCanvas.
4832
4833 This item paints a single rectangle which may have any pen() and
4834 brush(), but may not be tilted/rotated. For rotated rectangles,
4835 use QtCanvasPolygon.
4836
4837 The rectangle's size and initial position can be set in the
4838 constructor. The size can be set or changed later using setSize().
4839 Use height() and width() to retrieve the rectangle's dimensions.
4840
4841 The rectangle can be drawn on a painter with drawShape().
4842
4843 Like any other canvas item rectangles can be moved with
4844 QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting
4845 coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and
4846 QtCanvasItem::setZ().
4847
4848*/
4849
4850/*
4851 Constructs a rectangle at position (0,0) with both width and
4852 height set to 32 pixels on \a canvas.
4853*/
4855{
4856}
4857
4858/*
4859 Constructs a rectangle positioned and sized by \a r on \a canvas.
4860*/
4861QtCanvasRectangle::QtCanvasRectangle(const QRect& r, QtCanvas* canvas) :
4862 QtCanvasPolygonalItem(canvas), w(r.width()), h(r.height())
4863{
4864 move(r.x(), r.y());
4865}
4866
4867/*
4868 Constructs a rectangle at position (\a x, \a y) and size \a width
4869 by \a height, on \a canvas.
4870*/
4871QtCanvasRectangle::QtCanvasRectangle(int x, int y, int width, int height, QtCanvas* canvas) :
4872 QtCanvasPolygonalItem(canvas), w(width), h(height)
4873{
4874 move(x, y);
4875}
4876
4877/*
4878 Destroys the rectangle.
4879*/
4881{
4882 hide();
4883}
4884
4885/*
4886 Returns the width of the rectangle.
4887*/
4888int
4890{
4891 return w;
4892}
4893
4894/*
4895 Returns the height of the rectangle.
4896*/
4897int
4899{
4900 return h;
4901}
4902
4903/*
4904 Sets the \a width and \a height of the rectangle.
4905*/
4906void
4907QtCanvasRectangle::setSize(int width, int height)
4908{
4909 if (w != width || h != height)
4910 {
4911 removeFromChunks();
4912 w = width;
4913 h = height;
4914 addToChunks();
4915 }
4916}
4917
4918/*
4919 \fn QSize QtCanvasRectangle::size() const
4920
4921 Returns the width() and height() of the rectangle.
4922
4923 \sa rect(), setSize()
4924*/
4925
4926/*
4927 \fn QRect QtCanvasRectangle::rect() const
4928
4929 Returns the integer-converted x(), y() position and size() of the
4930 rectangle as a QRect.
4931*/
4932
4933/*
4934 \reimp
4935*/
4936QPolygon
4938{
4939 QPolygon pa(4);
4940 int pw = (pen().width() + 1) / 2;
4941
4942 if (pw < 1)
4943 {
4944 pw = 1;
4945 }
4946
4947 if (pen() == NoPen)
4948 {
4949 pw = 0;
4950 }
4951
4952 pa[0] = QPoint((int)x() - pw, (int)y() - pw);
4953 pa[1] = pa[0] + QPoint(w + pw * 2, 0);
4954 pa[2] = pa[1] + QPoint(0, h + pw * 2);
4955 pa[3] = pa[0] + QPoint(0, h + pw * 2);
4956 return pa;
4957}
4958
4959/*
4960 Draws the rectangle on painter \a p.
4961*/
4962void
4964{
4965 p.drawRect((int)x(), (int)y(), w, h);
4966}
4967
4968/*
4969 \class QtCanvasEllipse qtcanvas.h
4970 \brief The QtCanvasEllipse class provides an ellipse or ellipse segment on a QtCanvas.
4971
4972 A canvas item that paints an ellipse or ellipse segment with a QBrush.
4973 The ellipse's height, width, start angle and angle length can be set
4974 at construction time. The size can be changed at runtime with
4975 setSize(), and the angles can be changed (if you're displaying an
4976 ellipse segment rather than a whole ellipse) with setAngles().
4977
4978 Note that angles are specified in 16ths of a degree.
4979
4980 \target anglediagram
4981 \img qcanvasellipse.png Ellipse
4982
4983 If a start angle and length angle are set then an ellipse segment
4984 will be drawn. The start angle is the angle that goes from zero in a
4985 counter-clockwise direction (shown in green in the diagram). The
4986 length angle is the angle from the start angle in a
4987 counter-clockwise direction (shown in blue in the diagram). The blue
4988 segment is the segment of the ellipse that would be drawn. If no
4989 start angle and length angle are specified the entire ellipse is
4990 drawn.
4991
4992 The ellipse can be drawn on a painter with drawShape().
4993
4994 Like any other canvas item ellipses can be moved with move() and
4995 moveBy(), or by setting coordinates with setX(), setY() and setZ().
4996
4997 Note: QtCanvasEllipse does not use the pen.
4998*/
4999
5000/*
5001 Constructs a 32x32 ellipse, centered at (0, 0) on \a canvas.
5002*/
5004 QtCanvasPolygonalItem(canvas), w(32), h(32), a1(0), a2(360 * 16)
5005{
5006}
5007
5008/*
5009 Constructs a \a width by \a height pixel ellipse, centered at
5010 (0, 0) on \a canvas.
5011*/
5012QtCanvasEllipse::QtCanvasEllipse(int width, int height, QtCanvas* canvas) :
5013 QtCanvasPolygonalItem(canvas), w(width), h(height), a1(0), a2(360 * 16)
5014{
5015}
5016
5017// ### add a constructor taking degrees in float. 1/16 degrees is stupid. Lars
5018// ### it's how QPainter does it, so QtCanvas does too for consistency. If it's
5019// ### a good idea, it should be added to QPainter, not just to QtCanvas. Warwick
5020/*
5021 Constructs a \a width by \a height pixel ellipse, centered at
5022 (0, 0) on \a canvas. Only a segment of the ellipse is drawn,
5023 starting at angle \a startangle, and extending for angle \a angle
5024 (the angle length).
5025
5026 Note that angles are specified in sixteenths of a degree.
5027*/
5029 int height,
5030 int startangle,
5031 int angle,
5032 QtCanvas* canvas) :
5033 QtCanvasPolygonalItem(canvas), w(width), h(height), a1(startangle), a2(angle)
5034{
5035}
5036
5037/*
5038 Destroys the ellipse.
5039*/
5041{
5042 hide();
5043}
5044
5045/*
5046 Returns the width of the ellipse.
5047*/
5048int
5050{
5051 return w;
5052}
5053
5054/*
5055 Returns the height of the ellipse.
5056*/
5057int
5059{
5060 return h;
5061}
5062
5063/*
5064 Sets the \a width and \a height of the ellipse.
5065*/
5066void
5067QtCanvasEllipse::setSize(int width, int height)
5068{
5069 if (w != width || h != height)
5070 {
5071 removeFromChunks();
5072 w = width;
5073 h = height;
5074 addToChunks();
5075 }
5076}
5077
5078/*
5079 \fn int QtCanvasEllipse::angleStart() const
5080
5081 Returns the start angle in 16ths of a degree. Initially
5082 this will be 0.
5083
5084 \sa setAngles(), angleLength()
5085*/
5086
5087/*
5088 \fn int QtCanvasEllipse::angleLength() const
5089
5090 Returns the length angle (the extent of the ellipse segment) in
5091 16ths of a degree. Initially this will be 360 * 16 (a complete
5092 ellipse).
5093
5094 \sa setAngles(), angleStart()
5095*/
5096
5097/*
5098 Sets the angles for the ellipse. The start angle is \a start and
5099 the extent of the segment is \a length (the angle length) from the
5100 \a start. The angles are specified in 16ths of a degree. By
5101 default the ellipse will start at 0 and have an angle length of
5102 360 * 16 (a complete ellipse).
5103
5104 \sa angleStart(), angleLength()
5105*/
5106void
5107QtCanvasEllipse::setAngles(int start, int length)
5108{
5109 if (a1 != start || a2 != length)
5110 {
5111 removeFromChunks();
5112 a1 = start;
5113 a2 = length;
5114 addToChunks();
5115 }
5116}
5117
5118/*
5119 \reimp
5120*/
5121QPolygon
5123{
5124 QPainterPath path;
5125 path.arcTo(
5126 QRectF(x() - w / 2.0 + 0.5 - 1, y() - h / 2.0 + 0.5 - 1, w + 3, h + 3), a1 / 16., a2 / 16.);
5127 return path.toFillPolygon().toPolygon();
5128}
5129
5130/*
5131 Draws the ellipse, centered at x(), y() using the painter \a p.
5132
5133 Note that QtCanvasEllipse does not support an outline (the pen is
5134 always NoPen).
5135*/
5136void
5137QtCanvasEllipse::drawShape(QPainter& p)
5138{
5139 p.setPen(NoPen); // since QRegion(QPolygon) excludes outline :-()-:
5140
5141 if (!a1 && a2 == 360 * 16)
5142 {
5143 p.drawEllipse(int(x() - w / 2.0 + 0.5), int(y() - h / 2.0 + 0.5), w, h);
5144 }
5145 else
5146 {
5147 p.drawPie(int(x() - w / 2.0 + 0.5), int(y() - h / 2.0 + 0.5), w, h, a1, a2);
5148 }
5149}
5150
5151/*
5152 \class QtCanvasText
5153 \brief The QtCanvasText class provides a text object on a QtCanvas.
5154
5155 A canvas text item has text with font, color and alignment
5156 attributes. The text and font can be set in the constructor or set
5157 or changed later with setText() and setFont(). The color is set
5158 with setColor() and the alignment with setTextFlags(). The text
5159 item's bounding rectangle is retrieved with boundingRect().
5160
5161 The text can be drawn on a painter with draw().
5162
5163 Like any other canvas item text items can be moved with
5164 QtCanvasItem::move() and QtCanvasItem::moveBy(), or by setting
5165 coordinates with QtCanvasItem::setX(), QtCanvasItem::setY() and
5166 QtCanvasItem::setZ().
5167*/
5168
5169/*
5170 Constructs a QtCanvasText with the text "<text>", on \a canvas.
5171*/
5172QtCanvasText::QtCanvasText(QtCanvas* canvas) : QtCanvasItem(canvas), txt("<text>"), flags(0)
5173{
5174 setRect();
5175}
5176
5177// ### add textflags to the constructor? Lars
5178/*
5179 Constructs a QtCanvasText with the text \a t, on canvas \a canvas.
5180*/
5181QtCanvasText::QtCanvasText(const QString& t, QtCanvas* canvas) :
5182 QtCanvasItem(canvas), txt(t), flags(0)
5183{
5184 setRect();
5185}
5186
5187// ### see above
5188/*
5189 Constructs a QtCanvasText with the text \a t and font \a f, on the
5190 canvas \a canvas.
5191*/
5192QtCanvasText::QtCanvasText(const QString& t, QFont f, QtCanvas* canvas) :
5193 QtCanvasItem(canvas), txt(t), flags(0), fnt(f)
5194{
5195 setRect();
5196}
5197
5198/*
5199 Destroys the canvas text item.
5200*/
5202{
5203 removeFromChunks();
5204}
5205
5206/*
5207 Returns the bounding rectangle of the text.
5208*/
5209QRect
5211{
5212 return brect;
5213}
5214
5215void
5216QtCanvasText::setRect()
5217{
5218 brect = QFontMetrics(fnt).boundingRect(int(x()), int(y()), 0, 0, flags, txt);
5219}
5220
5221/*
5222 \fn int QtCanvasText::textFlags() const
5223
5224 Returns the currently set alignment flags.
5225
5226 \sa setTextFlags() Qt::AlignmentFlag Qt::TextFlag
5227*/
5228
5229
5230/*
5231 Sets the alignment flags to \a f. These are a bitwise OR of the
5232 flags available to QPainter::drawText() -- see the
5233 \l{Qt::AlignmentFlag}s and \l{Qt::TextFlag}s.
5234
5235 \sa setFont() setColor()
5236*/
5237void
5239{
5240 if (flags != f)
5241 {
5242 removeFromChunks();
5243 flags = f;
5244 setRect();
5245 addToChunks();
5246 }
5247}
5248
5249/*
5250 Returns the text item's text.
5251
5252 \sa setText()
5253*/
5254QString
5255QtCanvasText::text() const
5256{
5257 return txt;
5258}
5259
5260/*
5261 Sets the text item's text to \a t. The text may contain newlines.
5262
5263 \sa text(), setFont(), setColor() setTextFlags()
5264*/
5265void
5266QtCanvasText::setText(const QString& t)
5267{
5268 if (txt != t)
5269 {
5270 removeFromChunks();
5271 txt = t;
5272 setRect();
5273 addToChunks();
5274 }
5275}
5276
5277/*
5278 Returns the font in which the text is drawn.
5279
5280 \sa setFont()
5281*/
5282QFont
5283QtCanvasText::font() const
5284{
5285 return fnt;
5286}
5287
5288/*
5289 Sets the font in which the text is drawn to font \a f.
5290
5291 \sa font()
5292*/
5293void
5294QtCanvasText::setFont(const QFont& f)
5295{
5296 if (f != fnt)
5297 {
5298 removeFromChunks();
5299 fnt = f;
5300 setRect();
5301 addToChunks();
5302 }
5303}
5304
5305/*
5306 Returns the color of the text.
5307
5308 \sa setColor()
5309*/
5310QColor
5311QtCanvasText::color() const
5312{
5313 return col;
5314}
5315
5316/*
5317 Sets the color of the text to the color \a c.
5318
5319 \sa color(), setFont()
5320*/
5321void
5322QtCanvasText::setColor(const QColor& c)
5323{
5324 col = c;
5325 changeChunks();
5326}
5327
5328/*
5329 \reimp
5330*/
5331void
5332QtCanvasText::moveBy(double dx, double dy)
5333{
5334 int idx = int(x() + dx) - int(x());
5335 int idy = int(y() + dy) - int(y());
5336
5337 if (idx || idy)
5338 {
5339 removeFromChunks();
5340 }
5341
5342 myx += dx;
5343 myy += dy;
5344
5345 if (idx || idy)
5346 {
5347 brect.translate(idx, idy);
5348 addToChunks();
5349 }
5350}
5351
5352/*
5353 Draws the text using the painter \a painter.
5354*/
5355void
5356QtCanvasText::draw(QPainter& painter)
5357{
5358 painter.setFont(fnt);
5359 painter.setPen(col);
5360 painter.drawText(
5361 painter.fontMetrics().boundingRect(int(x()), int(y()), 0, 0, flags, txt), flags, txt);
5362}
5363
5364/*
5365 \reimp
5366*/
5367void
5368QtCanvasText::changeChunks()
5369{
5370 if (isVisible() && canvas())
5371 {
5372 int chunksize = canvas()->chunkSize();
5373
5374 for (int j = brect.top() / chunksize; j <= brect.bottom() / chunksize; j++)
5375 {
5376 for (int i = brect.left() / chunksize; i <= brect.right() / chunksize; i++)
5377 {
5378 canvas()->setChangedChunk(i, j);
5379 }
5380 }
5381 }
5382}
5383
5384/*
5385 Adds the text item to the appropriate chunks.
5386*/
5387void
5388QtCanvasText::addToChunks()
5389{
5390 if (isVisible() && canvas())
5391 {
5392 int chunksize = canvas()->chunkSize();
5393
5394 for (int j = brect.top() / chunksize; j <= brect.bottom() / chunksize; j++)
5395 {
5396 for (int i = brect.left() / chunksize; i <= brect.right() / chunksize; i++)
5397 {
5398 canvas()->addItemToChunk(this, i, j);
5399 }
5400 }
5401 }
5402}
5403
5404/*
5405 Removes the text item from the appropriate chunks.
5406*/
5407void
5408QtCanvasText::removeFromChunks()
5409{
5410 if (isVisible() && canvas())
5411 {
5412 int chunksize = canvas()->chunkSize();
5413
5414 for (int j = brect.top() / chunksize; j <= brect.bottom() / chunksize; j++)
5415 {
5416 for (int i = brect.left() / chunksize; i <= brect.right() / chunksize; i++)
5417 {
5418 canvas()->removeItemFromChunk(this, i, j);
5419 }
5420 }
5421 }
5422}
5423
5424/*
5425 Returns 0 (QtCanvasItem::Rtti_Item).
5426
5427 Make your derived classes return their own values for rtti(), so
5428 that you can distinguish between objects returned by
5429 QtCanvas::at(). You should use values greater than 1000 to allow
5430 for extensions to this class.
5431
5432 Overuse of this functionality can damage its extensibility. For
5433 example, once you have identified a base class of a QtCanvasItem
5434 found by QtCanvas::at(), cast it to that type and call meaningful
5435 methods rather than acting upon the object based on its rtti
5436 value.
5437
5438 For example:
5439
5440 \code
5441 QtCanvasItem* item;
5442 // Find an item, e.g. with QtCanvasItem::collisions().
5443 ...
5444 if (item->rtti() == MySprite::RTTI) {
5445 MySprite* s = (MySprite*)item;
5446 if (s->isDamagable()) s->loseHitPoints(1000);
5447 if (s->isHot()) myself->loseHitPoints(1000);
5448 ...
5449 }
5450 \endcode
5451*/
5452int
5453QtCanvasItem::rtti() const
5454{
5455 return RTTI;
5456}
5457
5458int QtCanvasItem::RTTI = Rtti_Item;
5459
5460/*
5461 Returns 1 (QtCanvasItem::Rtti_Sprite).
5462
5463 \sa QtCanvasItem::rtti()
5464*/
5465int
5467{
5468 return RTTI;
5469}
5470
5471int QtCanvasSprite::RTTI = Rtti_Sprite;
5472
5473/*
5474 Returns 2 (QtCanvasItem::Rtti_PolygonalItem).
5475
5476 \sa QtCanvasItem::rtti()
5477*/
5478int
5480{
5481 return RTTI;
5482}
5483
5484int QtCanvasPolygonalItem::RTTI = Rtti_PolygonalItem;
5485
5486/*
5487 Returns 3 (QtCanvasItem::Rtti_Text).
5488
5489 \sa QtCanvasItem::rtti()
5490*/
5491int
5492QtCanvasText::rtti() const
5493{
5494 return RTTI;
5495}
5496
5497int QtCanvasText::RTTI = Rtti_Text;
5498
5499/*
5500 Returns 4 (QtCanvasItem::Rtti_Polygon).
5501
5502 \sa QtCanvasItem::rtti()
5503*/
5504int
5506{
5507 return RTTI;
5508}
5509
5510int QtCanvasPolygon::RTTI = Rtti_Polygon;
5511
5512/*
5513 Returns 5 (QtCanvasItem::Rtti_Rectangle).
5514
5515 \sa QtCanvasItem::rtti()
5516*/
5517int
5519{
5520 return RTTI;
5521}
5522
5523int QtCanvasRectangle::RTTI = Rtti_Rectangle;
5524
5525/*
5526 Returns 6 (QtCanvasItem::Rtti_Ellipse).
5527
5528 \sa QtCanvasItem::rtti()
5529*/
5530int
5532{
5533 return RTTI;
5534}
5535
5536int QtCanvasEllipse::RTTI = Rtti_Ellipse;
5537
5538/*
5539 Returns 7 (QtCanvasItem::Rtti_Line).
5540
5541 \sa QtCanvasItem::rtti()
5542*/
5543int
5544QtCanvasLine::rtti() const
5545{
5546 return RTTI;
5547}
5548
5549int QtCanvasLine::RTTI = Rtti_Line;
5550
5551/*
5552 Returns 8 (QtCanvasItem::Rtti_Spline).
5553
5554 \sa QtCanvasItem::rtti()
5555*/
5556int
5558{
5559 return RTTI;
5560}
5561
5562int QtCanvasSpline::RTTI = Rtti_Spline;
5563
5564/*
5565 Constructs a QtCanvasSprite which uses images from the
5566 QtCanvasPixmapArray \a a.
5567
5568 The sprite in initially positioned at (0, 0) on \a canvas, using
5569 frame 0.
5570*/
5572 QtCanvasItem(canvas), frm(0), anim_val(0), anim_state(0), anim_type(0), images(a)
5573{
5574}
5575
5576/*
5577 Set the array of images used for displaying the sprite to the
5578 QtCanvasPixmapArray \a a.
5579
5580 If the current frame() is larger than the number of images in \a
5581 a, the current frame will be reset to 0.
5582*/
5583void
5585{
5586 bool isvisible = isVisible();
5587
5588 if (isvisible && images)
5589 {
5590 hide();
5591 }
5592
5593 images = a;
5594
5595 if (frm >= (int)images->count())
5596 {
5597 frm = 0;
5598 }
5599
5600 if (isvisible)
5601 {
5602 show();
5603 }
5604}
5605
5606/*
5607\internal
5608
5609Marks any chunks the sprite touches as changed.
5610*/
5611void
5612QtCanvasSprite::changeChunks()
5613{
5614 if (isVisible() && canvas())
5615 {
5616 int chunksize = canvas()->chunkSize();
5617
5618 for (int j = topEdge() / chunksize; j <= bottomEdge() / chunksize; j++)
5619 {
5620 for (int i = leftEdge() / chunksize; i <= rightEdge() / chunksize; i++)
5621 {
5622 canvas()->setChangedChunk(i, j);
5623 }
5624 }
5625 }
5626}
5627
5628/*
5629 Destroys the sprite and removes it from the canvas. Does \e not
5630 delete the images.
5631*/
5633{
5634 removeFromChunks();
5635}
5636
5637/*
5638 Sets the animation frame used for displaying the sprite to \a f,
5639 an index into the QtCanvasSprite's QtCanvasPixmapArray. The call
5640 will be ignored if \a f is larger than frameCount() or smaller
5641 than 0.
5642
5643 \sa frame() move()
5644*/
5645void
5647{
5648 move(x(), y(), f);
5649}
5650
5651/*
5652 \enum QtCanvasSprite::FrameAnimationType
5653
5654 This enum is used to identify the different types of frame
5655 animation offered by QtCanvasSprite.
5656
5657 \value Cycle at each advance the frame number will be incremented by
5658 1 (modulo the frame count).
5659 \value Oscillate at each advance the frame number will be
5660 incremented by 1 up to the frame count then decremented to by 1 to
5661 0, repeating this sequence forever.
5662*/
5663
5664/*
5665 Sets the animation characteristics for the sprite.
5666
5667 For \a type == \c Cycle, the frames will increase by \a step
5668 at each advance, modulo the frameCount().
5669
5670 For \a type == \c Oscillate, the frames will increase by \a step
5671 at each advance, up to the frameCount(), then decrease by \a step
5672 back to 0, repeating forever.
5673
5674 The \a state parameter is for internal use.
5675*/
5676void
5677QtCanvasSprite::setFrameAnimation(FrameAnimationType type, int step, int state)
5678{
5679 anim_val = step;
5680 anim_type = type;
5681 anim_state = state;
5682 setAnimated(true);
5683}
5684
5685/*
5686 Extends the default QtCanvasItem implementation to provide the
5687 functionality of setFrameAnimation().
5688
5689 The \a phase is 0 or 1: see QtCanvasItem::advance() for details.
5690
5691 \sa QtCanvasItem::advance() setVelocity()
5692*/
5693void
5694QtCanvasSprite::advance(int phase)
5695{
5696 if (phase == 1)
5697 {
5698 int nf = frame();
5699
5700 if (anim_type == Oscillate)
5701 {
5702 if (anim_state)
5703 {
5704 nf += anim_val;
5705 }
5706 else
5707 {
5708 nf -= anim_val;
5709 }
5710
5711 if (nf < 0)
5712 {
5713 nf = abs(anim_val);
5714 anim_state = !anim_state;
5715 }
5716 else if (nf >= frameCount())
5717 {
5718 nf = frameCount() - 1 - abs(anim_val);
5719 anim_state = !anim_state;
5720 }
5721 }
5722 else
5723 {
5724 nf = (nf + anim_val + frameCount()) % frameCount();
5725 }
5726
5727 move(x() + xVelocity(), y() + yVelocity(), nf);
5728 }
5729}
5730
5731/*
5732 \fn int QtCanvasSprite::frame() const
5733
5734 Returns the index of the current animation frame in the
5735 QtCanvasSprite's QtCanvasPixmapArray.
5736
5737 \sa setFrame(), move()
5738*/
5739
5740/*
5741 \fn int QtCanvasSprite::frameCount() const
5742
5743 Returns the number of frames in the QtCanvasSprite's
5744 QtCanvasPixmapArray.
5745*/
5746
5747
5748/*
5749 Moves the sprite to (\a x, \a y).
5750*/
5751void
5752QtCanvasSprite::move(double x, double y)
5753{
5755}
5756
5757/*
5758 \fn void QtCanvasSprite::move(double nx, double ny, int nf)
5759
5760 Moves the sprite to (\a nx, \a ny) and sets the current
5761 frame to \a nf. \a nf will be ignored if it is larger than
5762 frameCount() or smaller than 0.
5763*/
5764void
5765QtCanvasSprite::move(double nx, double ny, int nf)
5766{
5767 if (isVisible() && canvas())
5768 {
5769 hide();
5770 QtCanvasItem::move(nx, ny);
5771
5772 if (nf >= 0 && nf < frameCount())
5773 {
5774 frm = nf;
5775 }
5776
5777 show();
5778 }
5779 else
5780 {
5781 QtCanvasItem::move(nx, ny);
5782
5783 if (nf >= 0 && nf < frameCount())
5784 {
5785 frm = nf;
5786 }
5787 }
5788}
5789
5790
5791class QPoint;
5792
5793class QtPolygonScanner
5794{
5795public:
5797 {
5798 }
5799
5800 void scan(const QPolygon& pa, bool winding, int index = 0, int npoints = -1);
5801 void scan(const QPolygon& pa, bool winding, int index, int npoints, bool stitchable);
5802
5803 enum Edge
5804 {
5805 Left = 1,
5806 Right = 2,
5807 Top = 4,
5808 Bottom = 8
5809 };
5810
5811 void scan(const QPolygon& pa, bool winding, int index, int npoints, Edge edges);
5812 virtual void processSpans(int n, QPoint* point, int* width) = 0;
5813};
5814
5815// Based on Xserver code miFillGeneralPoly...
5816/*
5817 *
5818 * Written by Brian Kelleher; Oct. 1985
5819 *
5820 * Routine to fill a polygon. Two fill rules are
5821 * supported: frWINDING and frEVENODD.
5822 *
5823 * See fillpoly.h for a complete description of the algorithm.
5824 */
5825
5826/*
5827 * These are the data structures needed to scan
5828 * convert regions. Two different scan conversion
5829 * methods are available -- the even-odd method, and
5830 * the winding number method.
5831 * The even-odd rule states that a point is inside
5832 * the polygon if a ray drawn from that point in any
5833 * direction will pass through an odd number of
5834 * path segments.
5835 * By the winding number rule, a point is decided
5836 * to be inside the polygon if a ray drawn from that
5837 * point in any direction passes through a different
5838 * number of clockwise and counterclockwise path
5839 * segments.
5840 *
5841 * These data structures are adapted somewhat from
5842 * the algorithm in (Foley/Van Dam) for scan converting
5843 * polygons.
5844 * The basic algorithm is to start at the top (smallest y)
5845 * of the polygon, stepping down to the bottom of
5846 * the polygon by incrementing the y coordinate. We
5847 * keep a list of edges which the current scanline crosses,
5848 * sorted by x. This list is called the Active Edge Table (AET)
5849 * As we change the y-coordinate, we update each entry in
5850 * in the active edge table to reflect the edges new xcoord.
5851 * This list must be sorted at each scanline in case
5852 * two edges intersect.
5853 * We also keep a data structure known as the Edge Table (ET),
5854 * which keeps track of all the edges which the current
5855 * scanline has not yet reached. The ET is basically a
5856 * list of ScanLineList structures containing a list of
5857 * edges which are entered at a given scanline. There is one
5858 * ScanLineList per scanline at which an edge is entered.
5859 * When we enter a new edge, we move it from the ET to the AET.
5860 *
5861 * From the AET, we can implement the even-odd rule as in
5862 * (Foley/Van Dam).
5863 * The winding number rule is a little trickier. We also
5864 * keep the EdgeTableEntries in the AET linked by the
5865 * nextWETE (winding EdgeTableEntry) link. This allows
5866 * the edges to be linked just as before for updating
5867 * purposes, but only uses the edges linked by the nextWETE
5868 * link as edges representing spans of the polygon to
5869 * drawn (as with the even-odd rule).
5870 */
5871
5872/* $XConsortium: miscanfill.h, v 1.5 94/04/17 20:27:50 dpw Exp $ */
5873/*
5874
5875Copyright (c) 1987 X Consortium
5876
5877Permission is hereby granted, free of charge, to any person obtaining
5878a copy of this software and associated documentation files (the
5879"Software"), to deal in the Software without restriction, including
5880without limitation the rights to use, copy, modify, merge, publish,
5881distribute, sublicense, and/or sell copies of the Software, and to
5882permit persons to whom the Software is furnished to do so, subject to
5883the following conditions:
5884
5885The above copyright notice and this permission notice shall be included
5886in all copies or substantial portions of the Software.
5887
5888THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
5889OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
5890MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
5891IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
5892OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
5893ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
5894OTHER DEALINGS IN THE SOFTWARE.
5895
5896Except as contained in this notice, the name of the X Consortium shall
5897not be used in advertising or otherwise to promote the sale, use or
5898other dealings in this Software without prior written authorization
5899from the X Consortium.
5900
5901*/
5902
5903
5904/*
5905 * scanfill.h
5906 *
5907 * Written by Brian Kelleher; Jan 1985
5908 *
5909 * This file contains a few macros to help track
5910 * the edge of a filled object. The object is assumed
5911 * to be filled in scanline order, and thus the
5912 * algorithm used is an extension of Bresenham's line
5913 * drawing algorithm which assumes that y is always the
5914 * major axis.
5915 * Since these pieces of code are the same for any filled shape,
5916 * it is more convenient to gather the library in one
5917 * place, but since these pieces of code are also in
5918 * the inner loops of output primitives, procedure call
5919 * overhead is out of the question.
5920 * See the author for a derivation if needed.
5921 */
5922
5923/*
5924 * In scan converting polygons, we want to choose those pixels
5925 * which are inside the polygon. Thus, we add .5 to the starting
5926 * x coordinate for both left and right edges. Now we choose the
5927 * first pixel which is inside the pgon for the left edge and the
5928 * first pixel which is outside the pgon for the right edge.
5929 * Draw the left pixel, but not the right.
5930 *
5931 * How to add .5 to the starting x coordinate:
5932 * If the edge is moving to the right, then subtract dy from the
5933 * error term from the general form of the algorithm.
5934 * If the edge is moving to the left, then add dy to the error term.
5935 *
5936 * The reason for the difference between edges moving to the left
5937 * and edges moving to the right is simple: If an edge is moving
5938 * to the right, then we want the algorithm to flip immediately.
5939 * If it is moving to the left, then we don't want it to flip until
5940 * we traverse an entire pixel.
5941 */
5942#define BRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) \
5943 { \
5944 int dx; /* local storage */ \
5945 \
5946 /* \
5947 * if the edge is horizontal, then it is ignored \
5948 * and assumed not to be processed. Otherwise, do this stuff. \
5949 */ \
5950 if ((dy) != 0) \
5951 { \
5952 xStart = (x1); \
5953 dx = (x2)-xStart; \
5954 if (dx < 0) \
5955 { \
5956 m = dx / (dy); \
5957 m1 = m - 1; \
5958 incr1 = -2 * dx + 2 * (dy) * m1; \
5959 incr2 = -2 * dx + 2 * (dy) * m; \
5960 d = 2 * m * (dy)-2 * dx - 2 * (dy); \
5961 } \
5962 else \
5963 { \
5964 m = dx / (dy); \
5965 m1 = m + 1; \
5966 incr1 = 2 * dx - 2 * (dy) * m1; \
5967 incr2 = 2 * dx - 2 * (dy) * m; \
5968 d = -2 * m * (dy) + 2 * dx; \
5969 } \
5970 } \
5971 }
5972
5973#define BRESINCRPGON(d, minval, m, m1, incr1, incr2) \
5974 { \
5975 if (m1 > 0) \
5976 { \
5977 if (d > 0) \
5978 { \
5979 minval += m1; \
5980 d += incr1; \
5981 } \
5982 else \
5983 { \
5984 minval += m; \
5985 d += incr2; \
5986 } \
5987 } \
5988 else \
5989 { \
5990 if (d >= 0) \
5991 { \
5992 minval += m1; \
5993 d += incr1; \
5994 } \
5995 else \
5996 { \
5997 minval += m; \
5998 d += incr2; \
5999 } \
6000 } \
6001 }
6002
6003/*
6004 * This structure contains all of the information needed
6005 * to run the bresenham algorithm.
6006 * The variables may be hardcoded into the declarations
6007 * instead of using this structure to make use of
6008 * register declarations.
6009 */
6010typedef struct
6011{
6012 int minor; /* minor axis */
6013 int d; /* decision variable */
6014 int m, m1; /* slope and slope+1 */
6015 int incr1, incr2; /* error increments */
6016} BRESINFO;
6017
6018#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
6019 BRESINITPGON(dmaj, min1, min2, bres.minor, bres.d, bres.m, bres.m1, bres.incr1, bres.incr2)
6020
6021#define BRESINCRPGONSTRUCT(bres) \
6022 BRESINCRPGON(bres.d, bres.minor, bres.m, bres.m1, bres.incr1, bres.incr2)
6023
6024typedef struct _EdgeTableEntry
6025{
6026 int ymax; /* ycoord at which we exit this edge. */
6027 BRESINFO bres; /* Bresenham info to run the edge */
6028 struct _EdgeTableEntry* next; /* next in the list */
6029 struct _EdgeTableEntry* back; /* for insertion sort */
6030 struct _EdgeTableEntry* nextWETE; /* for winding num rule */
6031 int ClockWise; /* flag for winding number rule */
6033
6034typedef struct _ScanLineList
6035{
6036 int scanline; /* the scanline represented */
6037 EdgeTableEntry* edgelist; /* header node */
6038 struct _ScanLineList* next; /* next in the list */
6039} ScanLineList;
6040
6041typedef struct
6042{
6043 int ymax; /* ymax for the polygon */
6044 int ymin; /* ymin for the polygon */
6045 ScanLineList scanlines; /* header node */
6046} EdgeTable;
6047
6048/*
6049 * Here is a struct to help with storage allocation
6050 * so we can allocate a big chunk at a time, and then take
6051 * pieces from this heap when we need to.
6052 */
6053#define SLLSPERBLOCK 25
6054
6055typedef struct _ScanLineListBlock
6058 struct _ScanLineListBlock* next;
6060
6061/*
6062 * number of points to buffer before sending them off
6063 * to scanlines() : Must be an even number
6064 */
6065#define NUMPTSTOBUFFER 200
6066
6067/*
6068 *
6069 * a few macros for the inner loops of the fill code where
6070 * performance considerations don't allow a procedure call.
6071 *
6072 * Evaluate the given edge at the given scanline.
6073 * If the edge has expired, then we leave it and fix up
6074 * the active edge table; otherwise, we increment the
6075 * x value to be ready for the next scanline.
6076 * The winding number rule is in effect, so we must notify
6077 * the caller when the edge has been removed so he
6078 * can reorder the Winding Active Edge Table.
6079 */
6080#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) \
6081 { \
6082 if (pAET->ymax == y) \
6083 { /* leaving this edge */ \
6084 pPrevAET->next = pAET->next; \
6085 pAET = pPrevAET->next; \
6086 fixWAET = 1; \
6087 if (pAET) \
6088 pAET->back = pPrevAET; \
6089 } \
6090 else \
6091 { \
6092 BRESINCRPGONSTRUCT(pAET->bres); \
6093 pPrevAET = pAET; \
6094 pAET = pAET->next; \
6095 } \
6096 }
6097
6098
6099/*
6100 * Evaluate the given edge at the given scanline.
6101 * If the edge has expired, then we leave it and fix up
6102 * the active edge table; otherwise, we increment the
6103 * x value to be ready for the next scanline.
6104 * The even-odd rule is in effect.
6105 */
6106#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y) \
6107 { \
6108 if (pAET->ymax == y) \
6109 { /* leaving this edge */ \
6110 pPrevAET->next = pAET->next; \
6111 pAET = pPrevAET->next; \
6112 if (pAET) \
6113 pAET->back = pPrevAET; \
6114 } \
6115 else \
6116 { \
6117 BRESINCRPGONSTRUCT(pAET->bres) \
6118 pPrevAET = pAET; \
6119 pAET = pAET->next; \
6120 } \
6121 }
6122
6123/***********************************************************
6124
6125Copyright (c) 1987 X Consortium
6126
6127Permission is hereby granted, free of charge, to any person obtaining a copy
6128of this software and associated documentation files (the "Software"), to deal
6129in the Software without restriction, including without limitation the rights
6130to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
6131copies of the Software, and to permit persons to whom the Software is
6132furnished to do so, subject to the following conditions:
6133
6134The above copyright notice and this permission notice shall be included in
6135all copies or substantial portions of the Software.
6136
6137THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
6138IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6139FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
6140X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
6141AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
6142CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
6143
6144Except as contained in this notice, the name of the X Consortium shall not be
6145used in advertising or otherwise to promote the sale, use or other dealings
6146in this Software without prior written authorization from the X Consortium.
6147
6148
6149Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
6150
6151 All Rights Reserved
6152
6153Permission to use, copy, modify, and distribute this software and its
6154documentation for any purpose and without fee is hereby granted,
6155provided that the above copyright notice appear in all copies and that
6156both that copyright notice and this permission notice appear in
6157supporting documentation, and that the name of Digital not be
6158used in advertising or publicity pertaining to distribution of the
6159software without specific, written prior permission.
6160
6161DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
6162ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
6163DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
6164ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
6165WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
6166ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
6167SOFTWARE.
6169******************************************************************/
6170
6171#define MAXINT 0x7fffffff
6172#define MININT -MAXINT
6173
6174/*
6175 * fillUtils.c
6176 *
6177 * Written by Brian Kelleher; Oct. 1985
6178 *
6179 * This module contains all of the utility functions
6180 * needed to scan convert a polygon.
6181 *
6182 */
6183/*
6184 * InsertEdgeInET
6185 *
6186 * Insert the given edge into the edge table.
6187 * First we must find the correct bucket in the
6188 * Edge table, then find the right slot in the
6189 * bucket. Finally, we can insert it.
6190 *
6191 */
6192static bool
6193miInsertEdgeInET(EdgeTable* ET,
6194 EdgeTableEntry* ETE,
6195 int scanline,
6196 ScanLineListBlock** SLLBlock,
6197 int* iSLLBlock)
6198{
6199 register EdgeTableEntry *start, *prev;
6200 register ScanLineList *pSLL, *pPrevSLL;
6201 ScanLineListBlock* tmpSLLBlock;
6202
6203 /*
6204 * find the right bucket to put the edge into
6205 */
6206 pPrevSLL = &ET->scanlines;
6207 pSLL = pPrevSLL->next;
6208
6209 while (pSLL && (pSLL->scanline < scanline))
6210 {
6211 pPrevSLL = pSLL;
6212 pSLL = pSLL->next;
6213 }
6214
6215 /*
6216 * reassign pSLL (pointer to ScanLineList) if necessary
6217 */
6218 if ((!pSLL) || (pSLL->scanline > scanline))
6219 {
6220 if (*iSLLBlock > SLLSPERBLOCK - 1)
6221 {
6222 tmpSLLBlock = (ScanLineListBlock*)malloc(sizeof(ScanLineListBlock));
6223
6224 if (!tmpSLLBlock)
6225 {
6226 return false;
6227 }
6228
6229 (*SLLBlock)->next = tmpSLLBlock;
6230 tmpSLLBlock->next = 0;
6231 *SLLBlock = tmpSLLBlock;
6232 *iSLLBlock = 0;
6233 }
6234
6235 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
6236
6237 pSLL->next = pPrevSLL->next;
6238 pSLL->edgelist = 0;
6239 pPrevSLL->next = pSLL;
6240 }
6241
6242 pSLL->scanline = scanline;
6243
6244 /*
6245 * now insert the edge in the right bucket
6246 */
6247 prev = 0;
6248 start = pSLL->edgelist;
6249
6250 while (start && (start->bres.minor < ETE->bres.minor))
6251 {
6252 prev = start;
6253 start = start->next;
6254 }
6255
6256 ETE->next = start;
6257
6258 if (prev)
6259 {
6260 prev->next = ETE;
6261 }
6262 else
6263 {
6264 pSLL->edgelist = ETE;
6265 }
6266
6267 return true;
6268}
6269
6270/*
6271 * CreateEdgeTable
6272 *
6273 * This routine creates the edge table for
6274 * scan converting polygons.
6275 * The Edge Table (ET) looks like:
6276 *
6277 * EdgeTable
6278 * --------
6279 * | ymax | ScanLineLists
6280 * |scanline|-->------------>-------------->...
6281 * -------- |scanline| |scanline|
6282 * |edgelist| |edgelist|
6283 * --------- ---------
6284 * | |
6285 * | |
6286 * V V
6287 * list of ETEs list of ETEs
6288 *
6289 * where ETE is an EdgeTableEntry data structure,
6290 * and there is one ScanLineList per scanline at
6291 * which an edge is initially entered.
6292 *
6293 */
6294
6295typedef struct
6296{
6297#if defined(Q_OS_MAC)
6298 int y, x;
6299#else
6300 int x, y;
6301#endif
6302
6304
6305/*
6306 * Clean up our act.
6307 */
6308static void
6309miFreeStorage(ScanLineListBlock* pSLLBlock)
6310{
6311 register ScanLineListBlock* tmpSLLBlock;
6312
6313 while (pSLLBlock)
6314 {
6315 tmpSLLBlock = pSLLBlock->next;
6316 free(pSLLBlock);
6317 pSLLBlock = tmpSLLBlock;
6318 }
6319}
6320
6321static bool
6322miCreateETandAET(int count,
6323 DDXPointPtr pts,
6324 EdgeTable* ET,
6325 EdgeTableEntry* AET,
6326 EdgeTableEntry* pETEs,
6327 ScanLineListBlock* pSLLBlock)
6328{
6329 register DDXPointPtr top, bottom;
6330 register DDXPointPtr PrevPt, CurrPt;
6331 int iSLLBlock = 0;
6332
6333 int dy;
6334
6335 if (count < 2)
6336 {
6337 return true;
6338 }
6339
6340 /*
6341 * initialize the Active Edge Table
6342 */
6343 AET->next = 0;
6344 AET->back = 0;
6345 AET->nextWETE = 0;
6346 AET->bres.minor = MININT;
6347
6348 /*
6349 * initialize the Edge Table.
6350 */
6351 ET->scanlines.next = 0;
6352 ET->ymax = MININT;
6353 ET->ymin = MAXINT;
6354 pSLLBlock->next = 0;
6355
6356 PrevPt = &pts[count - 1];
6357
6358 /*
6359 * for each vertex in the array of points.
6360 * In this loop we are dealing with two vertices at
6361 * a time -- these make up one edge of the polygon.
6362 */
6363 while (count--)
6364 {
6365 CurrPt = pts++;
6366
6367 /*
6368 * find out which point is above and which is below.
6369 */
6370 if (PrevPt->y > CurrPt->y)
6371 {
6372 bottom = PrevPt, top = CurrPt;
6373 pETEs->ClockWise = 0;
6374 }
6375 else
6376 {
6377 bottom = CurrPt, top = PrevPt;
6378 pETEs->ClockWise = 1;
6379 }
6380
6381 /*
6382 * don't add horizontal edges to the Edge table.
6383 */
6384 if (bottom->y != top->y)
6385 {
6386 pETEs->ymax = bottom->y - 1; /* -1 so we don't get last scanline */
6387
6388 /*
6389 * initialize integer edge algorithm
6390 */
6391 dy = bottom->y - top->y;
6392 BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres)
6393
6394 if (!miInsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock))
6395 {
6396 miFreeStorage(pSLLBlock->next);
6397 return false;
6398 }
6399
6400 ET->ymax = qMax(ET->ymax, PrevPt->y);
6401 ET->ymin = qMin(ET->ymin, PrevPt->y);
6402 pETEs++;
6403 }
6404
6405 PrevPt = CurrPt;
6406 }
6407
6408 return true;
6409}
6410
6411/*
6412 * loadAET
6413 *
6414 * This routine moves EdgeTableEntries from the
6415 * EdgeTable into the Active Edge Table,
6416 * leaving them sorted by smaller x coordinate.
6417 *
6418 */
6419
6420static void
6421miloadAET(EdgeTableEntry* AET, EdgeTableEntry* ETEs)
6422{
6423 register EdgeTableEntry* pPrevAET;
6424 register EdgeTableEntry* tmp;
6425
6426 pPrevAET = AET;
6427 AET = AET->next;
6428
6429 while (ETEs)
6430 {
6431 while (AET && (AET->bres.minor < ETEs->bres.minor))
6432 {
6433 pPrevAET = AET;
6434 AET = AET->next;
6435 }
6436
6437 tmp = ETEs->next;
6438 ETEs->next = AET;
6439
6440 if (AET)
6441 {
6442 AET->back = ETEs;
6443 }
6444
6445 ETEs->back = pPrevAET;
6446 pPrevAET->next = ETEs;
6447 pPrevAET = ETEs;
6448
6449 ETEs = tmp;
6450 }
6451}
6452
6453/*
6454 * computeWAET
6455 *
6456 * This routine links the AET by the
6457 * nextWETE (winding EdgeTableEntry) link for
6458 * use by the winding number rule. The final
6459 * Active Edge Table (AET) might look something
6460 * like:
6461 *
6462 * AET
6463 * ---------- --------- ---------
6464 * |ymax | |ymax | |ymax |
6465 * | ... | |... | |... |
6466 * |next |->|next |->|next |->...
6467 * |nextWETE| |nextWETE| |nextWETE|
6468 * --------- --------- ^--------
6469 * | | |
6470 * V-------------------> V---> ...
6471 *
6472 */
6473static void
6474micomputeWAET(EdgeTableEntry* AET)
6475{
6476 register EdgeTableEntry* pWETE;
6477 register int inside = 1;
6478 register int isInside = 0;
6479
6480 AET->nextWETE = 0;
6481 pWETE = AET;
6482 AET = AET->next;
6483
6484 while (AET)
6485 {
6486 if (AET->ClockWise)
6487 {
6488 isInside++;
6489 }
6490 else
6491 {
6492 isInside--;
6493 }
6494
6495 if ((!inside && !isInside) || (inside && isInside))
6496 {
6497 pWETE->nextWETE = AET;
6498 pWETE = AET;
6499 inside = !inside;
6500 }
6501
6502 AET = AET->next;
6503 }
6504
6505 pWETE->nextWETE = 0;
6506}
6507
6508/*
6509 * InsertionSort
6510 *
6511 * Just a simple insertion sort using
6512 * pointers and back pointers to sort the Active
6513 * Edge Table.
6514 *
6515 */
6516
6517static int
6518miInsertionSort(EdgeTableEntry* AET)
6519{
6520 register EdgeTableEntry* pETEchase;
6521 register EdgeTableEntry* pETEinsert;
6522 register EdgeTableEntry* pETEchaseBackTMP;
6523 register int changed = 0;
6524
6525 AET = AET->next;
6526
6527 while (AET)
6528 {
6529 pETEinsert = AET;
6530 pETEchase = AET;
6531
6532 while (pETEchase->back->bres.minor > AET->bres.minor)
6533 {
6534 pETEchase = pETEchase->back;
6535 }
6536
6537 AET = AET->next;
6538
6539 if (pETEchase != pETEinsert)
6540 {
6541 pETEchaseBackTMP = pETEchase->back;
6542 pETEinsert->back->next = AET;
6543
6544 if (AET)
6545 {
6546 AET->back = pETEinsert->back;
6547 }
6548
6549 pETEinsert->next = pETEchase;
6550 pETEchase->back->next = pETEinsert;
6551 pETEchase->back = pETEinsert;
6552 pETEinsert->back = pETEchaseBackTMP;
6553 changed = 1;
6554 }
6555 }
6556
6557 return changed;
6558}
6559
6560/*
6561 \overload
6562*/
6563void
6564QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints)
6565{
6566 scan(pa, winding, index, npoints, true);
6567}
6568
6569/*
6570 \overload
6571
6572 If \a stitchable is false, the right and bottom edges of the
6573 polygon are included. This causes adjacent polygons to overlap.
6574*/
6575void
6576QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, bool stitchable)
6577{
6578 scan(pa,
6579 winding,
6580 index,
6581 npoints,
6582 stitchable ? Edge(Left + Top) : Edge(Left + Right + Top + Bottom));
6583}
6584
6585/*
6586 Calls processSpans() for all scanlines of the polygon defined by
6587 \a npoints starting at \a index in \a pa.
6588
6589 If \a winding is true, the Winding algorithm rather than the
6590 Odd-Even rule is used.
6591
6592 The \a edges is any bitwise combination of:
6593 \list
6594 \i QtPolygonScanner::Left
6595 \i QtPolygonScanner::Right
6596 \i QtPolygonScanner::Top
6597 \i QtPolygonScanner::Bottom
6598 \endlist
6599 \a edges determines which edges are included.
6600
6601 \warning The edges feature does not work properly.
6602
6603*/
6604void
6605QtPolygonScanner::scan(const QPolygon& pa, bool winding, int index, int npoints, Edge edges)
6606{
6607
6608
6609 DDXPointPtr ptsIn = (DDXPointPtr)pa.data();
6610 ptsIn += index;
6611 register EdgeTableEntry* pAET; /* the Active Edge Table */
6612 register int y; /* the current scanline */
6613 register int nPts = 0; /* number of pts in buffer */
6614 register EdgeTableEntry* pWETE; /* Winding Edge Table */
6615 register ScanLineList* pSLL; /* Current ScanLineList */
6616 register DDXPointPtr ptsOut; /* ptr to output buffers */
6617 int* width;
6618 DDXPointRec FirstPoint[NUMPTSTOBUFFER]; /* the output buffers */
6619 int FirstWidth[NUMPTSTOBUFFER];
6620 EdgeTableEntry* pPrevAET; /* previous AET entry */
6621 EdgeTable ET; /* Edge Table header node */
6622 EdgeTableEntry AET; /* Active ET header node */
6623 EdgeTableEntry* pETEs; /* Edge Table Entries buff */
6624 ScanLineListBlock SLLBlock; /* header for ScanLineList */
6625 int fixWAET = 0;
6626 int edge_l = (edges & Left) ? 1 : 0;
6627 int edge_r = (edges & Right) ? 1 : 0;
6628 int edge_t = 1; //#### (edges & Top) ? 1 : 0;
6629 int edge_b = (edges & Bottom) ? 1 : 0;
6630
6631 if (npoints == -1)
6632 {
6633 npoints = pa.size();
6634 }
6635
6636 if (npoints < 3)
6637 {
6638 return;
6639 }
6640
6641 if (!(pETEs = (EdgeTableEntry*)malloc(sizeof(EdgeTableEntry) * npoints)))
6642 {
6643 return;
6644 }
6645
6646 ptsOut = FirstPoint;
6647 width = FirstWidth;
6648
6649 if (!miCreateETandAET(npoints, ptsIn, &ET, &AET, pETEs, &SLLBlock))
6650 {
6651 free(pETEs);
6652 return;
6653 }
6654
6655 pSLL = ET.scanlines.next;
6656
6657 if (!winding)
6658 {
6659 /*
6660 * for each scanline
6661 */
6662 for (y = ET.ymin + 1 - edge_t; y < ET.ymax + edge_b; y++)
6663 {
6664 /*
6665 * Add a new edge to the active edge table when we
6666 * get to the next edge.
6667 */
6668 if (pSLL && y == pSLL->scanline)
6669 {
6670 miloadAET(&AET, pSLL->edgelist);
6671 pSLL = pSLL->next;
6672 }
6673
6674 pPrevAET = &AET;
6675 pAET = AET.next;
6676
6677 /*
6678 * for each active edge
6679 */
6680 while (pAET)
6681 {
6682 ptsOut->x = pAET->bres.minor + 1 - edge_l;
6683 ptsOut++->y = y;
6684 *width++ = pAET->next->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r;
6685 nPts++;
6686
6687 /*
6688 * send out the buffer when its full
6689 */
6690 if (nPts == NUMPTSTOBUFFER)
6691 {
6692 processSpans(nPts, (QPoint*)FirstPoint, FirstWidth);
6693 ptsOut = FirstPoint;
6694 width = FirstWidth;
6695 nPts = 0;
6696 }
6697
6698 EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
6699 EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
6700 }
6701
6702 miInsertionSort(&AET);
6703 }
6704 }
6705 else /* default to WindingNumber */
6706 {
6707 /*
6708 * for each scanline
6709 */
6710 for (y = ET.ymin + 1 - edge_t; y < ET.ymax + edge_b; y++)
6711 {
6712 /*
6713 * Add a new edge to the active edge table when we
6714 * get to the next edge.
6715 */
6716 if (pSLL && y == pSLL->scanline)
6717 {
6718 miloadAET(&AET, pSLL->edgelist);
6719 micomputeWAET(&AET);
6720 pSLL = pSLL->next;
6721 }
6722
6723 pPrevAET = &AET;
6724 pAET = AET.next;
6725 pWETE = pAET;
6726
6727 /*
6728 * for each active edge
6729 */
6730 while (pAET)
6731 {
6732 /*
6733 * if the next edge in the active edge table is
6734 * also the next edge in the winding active edge
6735 * table.
6736 */
6737 if (pWETE == pAET)
6738 {
6739 ptsOut->x = pAET->bres.minor + 1 - edge_l;
6740 ptsOut++->y = y;
6741 *width++ = pAET->nextWETE->bres.minor - pAET->bres.minor - 1 + edge_l + edge_r;
6742 nPts++;
6743
6744 /*
6745 * send out the buffer
6746 */
6747 if (nPts == NUMPTSTOBUFFER)
6748 {
6749 processSpans(nPts, (QPoint*)FirstPoint, FirstWidth);
6750 ptsOut = FirstPoint;
6751 width = FirstWidth;
6752 nPts = 0;
6753 }
6754
6755 pWETE = pWETE->nextWETE;
6756
6757 while (pWETE != pAET)
6758 {
6759 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
6760 }
6761
6762 pWETE = pWETE->nextWETE;
6763 }
6764
6765 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
6766 }
6767
6768 /*
6769 * reevaluate the Winding active edge table if we
6770 * just had to resort it or if we just exited an edge.
6771 */
6772 if (miInsertionSort(&AET) || fixWAET)
6773 {
6774 micomputeWAET(&AET);
6775 fixWAET = 0;
6776 }
6777 }
6778 }
6779
6780 /*
6781 * Get any spans that we missed by buffering
6782 */
6783
6784
6785 processSpans(nPts, (QPoint*)FirstPoint, FirstWidth);
6786 free(pETEs);
6787 miFreeStorage(SLLBlock.next);
6788}
6789
6790/***** END OF X11-based CODE *****/
6791
6792
6794{
6796
6797public:
6799 {
6801
6802 void
6803 processSpans(int n, QPoint* point, int* width)
6804 {
6805 processor.doSpans(n, point, width);
6806 }
6807};
6808
6809void
6810QtCanvasPolygonalItem::scanPolygon(const QPolygon& pa,
6811 int winding,
6812 QPolygonalProcessor& process) const
6813{
6814 QtCanvasPolygonScanner scanner(process);
6815 scanner.scan(pa, winding);
6816}
uint8_t index
if(!yyvaluep)
Definition Grammar.cpp:645
constexpr T c
struct DDXPointRec * DDXPointPtr
#define MININT
struct _EdgeTableEntry EdgeTableEntry
struct _ScanLineListBlock ScanLineListBlock
struct _ScanLineList ScanLineList
#define SLLSPERBLOCK
#define MAXINT
QList< QtCanvasItem * > QtCanvasItemList
Definition qtcanvas.h:62
struct DDXPointRec * DDXPointPtr
struct _EdgeTableEntry EdgeTableEntry
struct _ScanLineListBlock ScanLineListBlock
struct _ScanLineList ScanLineList
#define EVALUATEEDGEEVENODD(pAET, pPrevAET, y)
#define NUMPTSTOBUFFER
#define BRESINITPGONSTRUCT(dmaj, min1, min2, bres)
#define EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET)
bool qt_testCollision(const QtCanvasSprite *s1, const QtCanvasSprite *s2)
QList< QtCanvasItem * > QtCanvasItemList
Definition qtcanvas.h:62
void addBits(int x1, int x2, uchar newbits, int xo, int yo)
void doSpans(int n, QPoint *pt, int *w)
void add(int x, int y)
QPolygonalProcessor(QtCanvas *c, const QPolygon &pa)
bool hasChanged() const
Definition qtcanvas.cpp:399
const QtCanvasItemList & list() const
Definition qtcanvas.cpp:373
void remove(QtCanvasItem *item)
Definition qtcanvas.cpp:386
void add(QtCanvasItem *item)
Definition qtcanvas.cpp:379
bool takeChange()
Definition qtcanvas.cpp:405
void add(const QRect &rect)
QtCanvasClusterizer(int maxclusters)
int clusters() const
Definition qtcanvas.cpp:97
void add(int x, int y)
const QRect & operator[](int i) const
void add(int x, int y, int w, int h)
QSet< QtCanvasItem * > itemDict
Definition qtcanvas.cpp:66
QSet< QtCanvasItem * > animDict
Definition qtcanvas.cpp:67
QList< QtCanvasView * > viewList
Definition qtcanvas.cpp:65
void setSize(int w, int h)
void setAngles(int start, int length)
QPolygon areaPoints() const
static int RTTI
Definition qtcanvas.h:916
void drawShape(QPainter &)
int angleLength() const
Definition qtcanvas.h:906
bool collidesWith(const QtCanvasItem *) const
int rtti() const
int height() const
int width() const
friend class QtCanvasItem
bool operator()(const QtCanvasItem *i1, const QtCanvasItem *i2) const
Definition qtcanvas.cpp:345
friend class QtCanvasSprite
Definition qtcanvas.h:236
virtual void setActive(bool yes)
virtual void setVelocity(double vx, double vy)
static int RTTI
Definition qtcanvas.h:214
virtual QRect boundingRect() const =0
friend class QtCanvasRectangle
Definition qtcanvas.h:237
double xVelocity() const
virtual bool collidesWith(const QtCanvasItem *) const =0
virtual void setEnabled(bool yes)
friend class QtCanvasPolygon
Definition qtcanvas.h:238
friend class QtCanvasPolygonalItem
Definition qtcanvas.h:235
friend class QtCanvasText
Definition qtcanvas.h:240
friend class QtCanvasEllipse
Definition qtcanvas.h:239
QtCanvas * canvas() const
Definition qtcanvas.h:220
double x() const
Definition qtcanvas.h:74
friend class QtCanvasLine
Definition qtcanvas.h:241
virtual int rtti() const
QtCanvasItem(QtCanvas *canvas)
bool isVisible() const
Definition qtcanvas.h:147
virtual ~QtCanvasItem()
virtual void setVisible(bool yes)
virtual void setCanvas(QtCanvas *)
virtual void draw(QPainter &)=0
virtual void moveBy(double dx, double dy)
virtual QRect boundingRectAdvanced() const
virtual void advance(int stage)
double y() const
Definition qtcanvas.h:80
virtual void setAnimated(bool y)
QtCanvasItemList collisions(bool exact) const
virtual void setSelected(bool yes)
double z() const
Definition qtcanvas.h:86
bool animated() const
void move(double x, double y)
double yVelocity() const
QPolygon areaPoints() const
static int RTTI
Definition qtcanvas.h:871
void drawShape(QPainter &)
void setPoints(int x1, int y1, int x2, int y2)
int rtti() const
void moveBy(double dx, double dy)
void setPen(QPen p)
bool isValid() const
bool readPixmaps(const QString &datafilenamepattern, int framecount=0)
bool readCollisionMasks(const QString &filenamepattern)
void setImage(int i, QtCanvasPixmap *p)
QtCanvasPixmap(const QString &datafilename)
void processSpans(int n, QPoint *point, int *width)
QtCanvasPolygonScanner(QPolygonalProcessor &p)
QPolygon areaPoints() const
static int RTTI
Definition qtcanvas.h:825
void drawShape(QPainter &)
int rtti() const
QPolygon points() const
void setPoints(QPolygon)
void moveBy(double dx, double dy)
QPolygon poly
Definition qtcanvas.h:829
virtual void setBrush(QBrush b)
virtual ~QtCanvasPolygonalItem()
virtual QPolygon areaPointsAdvanced() const
void draw(QPainter &)
bool collidesWith(const QtCanvasItem *) const
QPen pen() const
Definition qtcanvas.h:719
virtual void drawShape(QPainter &)=0
virtual QPolygon areaPoints() const =0
QRect boundingRect() const
virtual void setPen(QPen p)
void setSize(int w, int h)
QPolygon areaPoints() const
static int RTTI
Definition qtcanvas.h:797
QPolygon chunks() const
void drawShape(QPainter &)
bool collidesWith(const QtCanvasItem *) const
int rtti() const
int height() const
int width() const
void setControlPoints(QPolygon, bool closed=true)
static int RTTI
Definition qtcanvas.h:843
QtCanvasSpline(QtCanvas *canvas)
int rtti() const
QPolygon controlPoints() const
bool closed() const
static int RTTI
Definition qtcanvas.h:642
int bottomEdge() const
virtual ~QtCanvasSprite()
int frame() const
Definition qtcanvas.h:630
bool collidesWith(const QtCanvasItem *) const
int rtti() const
virtual void setFrameAnimation(FrameAnimationType=Cycle, int step=1, int state=0)
void setFrame(int)
void setSequence(QtCanvasPixmapArray *seq)
QtCanvasPixmap * image() const
Definition qtcanvas.h:665
int topEdge() const
virtual void advance(int stage)
virtual QtCanvasPixmap * imageAdvanced() const
int height() const
int width() const
int rightEdge() const
QRect boundingRect() const
int frameCount() const
Definition qtcanvas.h:636
void draw(QPainter &painter)
void move(double x, double y)
int leftEdge() const
static int RTTI
Definition qtcanvas.h:965
QColor color() const
void setText(const QString &)
virtual void draw(QPainter &)
bool collidesWith(const QtCanvasItem *) const
int rtti() const
virtual ~QtCanvasText()
QString text() const
void moveBy(double dx, double dy)
void setTextFlags(int)
QFont font() const
QRect boundingRect() const
void setFont(const QFont &)
void setColor(const QColor &)
virtual void drawContents(QPainter *p, int cx, int cy, int cw, int ch)
const QMatrix & worldMatrix() const
virtual void contentsMouseDoubleClickEvent(QMouseEvent *)
void setCanvas(QtCanvas *v)
QtCanvasView(QWidget *parent=0)
bool setWorldMatrix(const QMatrix &)
virtual void contentsMouseReleaseEvent(QMouseEvent *)
virtual void contentsDragEnterEvent(QDragEnterEvent *)
QtCanvas * canvas() const
Definition qtcanvas.h:474
void setHighQualityRendering(bool enable)
virtual void contentsWheelEvent(QWheelEvent *)
const QMatrix & inverseWorldMatrix() const
virtual void contentsDropEvent(QDropEvent *)
virtual void contentsMouseMoveEvent(QMouseEvent *)
virtual void contentsMousePressEvent(QMouseEvent *)
virtual void contentsDragLeaveEvent(QDragLeaveEvent *)
virtual QSize sizeHint() const
virtual void contentsContextMenuEvent(QContextMenuEvent *)
virtual void contentsDragMoveEvent(QDragMoveEvent *)
bool highQualityRendering
Definition qtcanvas.h:467
void dragLeaveEvent(QDragLeaveEvent *e)
void dragMoveEvent(QDragMoveEvent *e)
void dragEnterEvent(QDragEnterEvent *e)
void mousePressEvent(QMouseEvent *e)
void contextMenuEvent(QContextMenuEvent *e)
void paintEvent(QPaintEvent *e)
void mouseMoveEvent(QMouseEvent *e)
void mouseReleaseEvent(QMouseEvent *e)
QtCanvasView * m_view
void mouseDoubleClickEvent(QMouseEvent *e)
QtCanvasWidget(QtCanvasView *view)
void wheelEvent(QWheelEvent *e)
void dropEvent(QDropEvent *e)
virtual void setAllChanged()
void drawViewArea(QtCanvasView *view, QPainter *p, const QRect &r, bool dbuf)
virtual void setBackgroundColor(const QColor &c)
virtual void setTiles(QPixmap tiles, int h, int v, int tilewidth, int tileheight)
void resized()
QtCanvas(QObject *parent=0)
Definition qtcanvas.cpp:618
int tile(int x, int y) const
Definition qtcanvas.h:290
QtCanvasItemList collisions(const QPoint &) const
virtual void setAdvancePeriod(int ms)
Definition qtcanvas.cpp:998
virtual void setUnchanged(const QRect &area)
virtual void setTile(int x, int y, int tilenum)
int tilesHorizontally() const
Definition qtcanvas.h:296
virtual void setUpdatePeriod(int ms)
void drawArea(const QRect &, QPainter *p, bool double_buffer=false)
virtual void setChanged(const QRect &area)
virtual void resize(int width, int height)
Definition qtcanvas.cpp:716
bool validChunk(int x, int y) const
Definition qtcanvas.h:358
virtual void addView(QtCanvasView *)
Definition qtcanvas.cpp:966
int chunkSize() const
Definition qtcanvas.h:370
virtual void drawBackground(QPainter &, const QRect &area)
virtual void setBackgroundPixmap(const QPixmap &p)
virtual ~QtCanvas()
Definition qtcanvas.cpp:663
void removeItemFromChunkContaining(QtCanvasItem *, int x, int y)
virtual void advance()
virtual void setChangedChunk(int i, int j)
void removeItemFromChunk(QtCanvasItem *, int i, int j)
QPixmap backgroundPixmap() const
virtual void removeItem(QtCanvasItem *)
Definition qtcanvas.cpp:955
virtual void update()
virtual void removeAnimation(QtCanvasItem *)
Definition qtcanvas.cpp:944
virtual void removeView(QtCanvasView *)
Definition qtcanvas.cpp:985
int height() const
Definition qtcanvas.h:328
void addItemToChunkContaining(QtCanvasItem *, int x, int y)
int width() const
Definition qtcanvas.h:322
QColor backgroundColor() const
virtual void addItem(QtCanvasItem *)
Definition qtcanvas.cpp:922
virtual void addAnimation(QtCanvasItem *)
Definition qtcanvas.cpp:933
void drawCanvasArea(const QRect &, QPainter *p=0, bool double_buffer=true)
QtCanvasItemList allItems()
Definition qtcanvas.cpp:706
int tilesVertically() const
Definition qtcanvas.h:302
void addItemToChunk(QtCanvasItem *, int i, int j)
virtual void setChangedChunkContaining(int x, int y)
virtual void retune(int chunksize, int maxclusters=100)
Definition qtcanvas.cpp:800
virtual void drawForeground(QPainter &, const QRect &area)
virtual void processSpans(int n, QPoint *point, int *width)=0
virtual ~QtPolygonScanner()
void scan(const QPolygon &pa, bool winding, int index=0, int npoints=-1)
void scan(const QPolygon &pa, bool winding, int index, int npoints, bool stitchable)
void scan(const QPolygon &pa, bool winding, int index, int npoints, Edge edges)
bool update(mongocxx::collection &coll, const nlohmann::json &query, const nlohmann::json &update)
Definition mongodb.cpp:68
bool contains(const MemoryID &general, const MemoryID &specific)
Indicates whether general is "less specific" than, or equal to, specific, i.e.
Definition MemoryID.cpp:563
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
double a(double t, double a0, double j)
Definition CtrlUtil.h:45
double v(double t, double v0, double a0, double j)
Definition CtrlUtil.h:39
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::vector< T > abs(const std::vector< T > &v)
constexpr auto n() noexcept
bool intersects(const Circlef &circle, const Segment2D &segment)
double angle(const Point &a, const Point &b, const Point &c)
Definition point.hpp:109
ScanLineList scanlines
struct _EdgeTableEntry * back
struct _EdgeTableEntry * nextWETE
struct _EdgeTableEntry * next
ScanLineList SLLs[SLLSPERBLOCK]
struct _ScanLineListBlock * next
EdgeTableEntry * edgelist
struct _ScanLineList * next