]> Shamusworld >> Repos - architektonas/blob - src/base/paintinterface.cpp
d98db849261454baad24bd36c4a69d7f2e851925
[architektonas] / src / base / paintinterface.cpp
1 // paintinterface.cpp
2 //
3 // Part of the Architektonas Project
4 // Extensively rewritten and refactored by James L. Hammons
5 // Portions copyright (C) 2001-2003 RibbonSoft
6 // Copyright (C) 2010 Underground Software
7 // See the README and GPLv2 files for licensing and warranty information
8 //
9 // JLH = James L. Hammons <jlhamm@acm.org>
10 //
11 // Who  When        What
12 // ---  ----------  -----------------------------------------------------------
13 // JLH  05/21/2010  Created this file. :-)
14 //
15
16 // BIG NOTE: THIS CLASS ASSUMES THAT THE PAINTER * IS VALID!!! BAD THINGS WILL
17 //           HAPPEN IF IT IS NOT!!!
18
19 #include "paintinterface.h"
20
21 #include "rs_debug.h"
22 #include "rs_math.h"
23
24 PaintInterface::PaintInterface(QPainter * p): painter(p), drawingMode(RS2::ModeFull),
25         offset(Vector(0.0, 0.0))
26 {
27 }
28
29 /**
30 * Sets the drawing mode.
31 */
32 void PaintInterface::setDrawingMode(RS2::DrawingMode m)
33 {
34         drawingMode = m;
35 }
36
37 /**
38 * @return Current drawing mode.
39 */
40 RS2::DrawingMode PaintInterface::getDrawingMode()
41 {
42         return drawingMode;
43 }
44
45 void PaintInterface::createArc(QPolygon & pa, const Vector& cp, double radius,
46         double a1, double a2, bool reversed)
47 {
48         if (radius < 1.0e-6)
49         {
50                 RS_DEBUG->print(RS_Debug::D_WARNING, "PaintInterface::createArc: invalid radius: %f", radius);
51                 return;
52         }
53
54         int cix;              // Next point on circle
55         int ciy;              //
56         double aStep;         // Angle Step (rad)
57         double a;             // Current Angle (rad)
58
59         if (fabs(2.0 / radius) <= 1.0)
60                 aStep = asin(2.0 / radius);
61         else
62                 aStep = 1.0;
63
64         //if (aStep<0.05) {
65         //    aStep = 0.05;
66         //}
67
68         // less than a pixel long lines:
69         //if (radius*aStep<1.0) {
70         //      aStep =
71         //}
72
73         //QPointArray pa;
74         int i = 0;
75         pa.resize(i + 1);
76         pa.setPoint(i++, toScreenX(cp.x + cos(a1) * radius), toScreenY(cp.y - sin(a1) * radius));
77         //moveTo(toScreenX(cp.x+cos(a1)*radius),
78         //       toScreenY(cp.y-sin(a1)*radius));
79
80         if (!reversed)
81         {
82                 // Arc Counterclockwise:
83                 if (a1 > a2 - 1.0e-10)
84                         a2 += 2 * M_PI;
85
86                 for(a=a1+aStep; a<=a2; a+=aStep)
87                 {
88                         cix = toScreenX(cp.x + cos(a) * radius);
89                         ciy = toScreenY(cp.y - sin(a) * radius);
90                         pa.resize(i + 1);
91                         pa.setPoint(i++, cix, ciy);
92                 }
93         }
94         else
95         {
96                 // Arc Clockwise:
97                 if (a1 < a2 + 1.0e-10)
98                         a2 -= 2 * M_PI;
99
100                 for(a=a1-aStep; a>=a2; a-=aStep)
101                 {
102                         cix = toScreenX(cp.x + cos(a) * radius);
103                         ciy = toScreenY(cp.y - sin(a) * radius);
104                         //lineTo(cix, ciy);
105                         pa.resize(i + 1);
106                         pa.setPoint(i++, cix, ciy);
107                 }
108         }
109
110         pa.resize(i + 1);
111         pa.setPoint(i++, toScreenX(cp.x + cos(a2) * radius), toScreenY(cp.y - sin(a2) * radius));
112         //drawPolyline(pa);
113 }
114
115 void PaintInterface::drawRect(const Vector & p1, const Vector & p2)
116 {
117         drawLine(Vector(p1.x, p1.y), Vector(p2.x, p1.y));
118         drawLine(Vector(p2.x, p1.y), Vector(p2.x, p2.y));
119         drawLine(Vector(p2.x, p2.y), Vector(p1.x, p2.y));
120         drawLine(Vector(p1.x, p2.y), Vector(p1.x, p1.y));
121 }
122
123 void PaintInterface::drawHandle(const Vector & p, const RS_Color & c, int size)
124 {
125         if (size < 0)
126                 size = 2;
127
128         fillRect((int)(p.x - size), (int)(p.y - size), 2 * size, 2 * size, c);
129 }
130
131 void PaintInterface::setPreviewMode()
132 {
133         drawingMode = RS2::ModeXOR;
134         setXORMode();
135         setPreviewPen();
136 }
137
138 bool PaintInterface::isPreviewMode()
139 {
140         return (drawingMode == RS2::ModeXOR);
141 }
142
143 void PaintInterface::setOffset(const Vector & o)
144 {
145         offset = o;
146 }
147
148 int PaintInterface::toScreenX(double x)
149 {
150         return RS_Math::round(offset.x + x);
151 }
152
153 int PaintInterface::toScreenY(double y)
154 {
155         return RS_Math::round(offset.y + y);
156 }
157
158 /**
159  * Draws a grid point at (x1, y1).
160  */
161 void PaintInterface::drawGridPoint(const Vector & p)
162 {
163         painter->drawPoint(toScreenX(p.x), toScreenY(p.y));
164 }
165
166 /**
167  * Draws a point at (x1, y1).
168  */
169 void PaintInterface::drawPoint(const Vector & p)
170 {
171         painter->drawLine(toScreenX(p.x - 1), toScreenY(p.y), toScreenX(p.x + 1), toScreenY(p.y));
172         painter->drawLine(toScreenX(p.x), toScreenY(p.y - 1), toScreenX(p.x), toScreenY(p.y + 1));
173 }
174
175 /**
176  * Draws a line from (x1, y1) to (x2, y2).
177  */
178 void PaintInterface::drawLine(const Vector & p1, const Vector & p2)
179 {
180 #ifdef __APPLE__
181         int w2 = (int)getPen().getScreenWidth() / 2;
182         QPainter::drawLine(toScreenX(p1.x - w2), toScreenY(p1.y - w2),
183                 toScreenX(p2.x - w2), toScreenY(p2.y - w2));
184 #else
185         painter->drawLine(toScreenX(p1.x), toScreenY(p1.y), toScreenX(p2.x), toScreenY(p2.y));
186 #endif
187 }
188
189 /**
190  * Draws a rectangle with corners p1, p2.
191  */
192 /*void PaintInterface::drawRect(const Vector& p1, const Vector& p2) {
193         / *QPainter::drawRect(toScreenX(p1.x), toScreenY(p1.y),
194                                            abs(toScreenX(p2.x) - toScreenX(p1.x)),
195                                            abs(toScreenY(p2.y) - toScreenY(p1.y)));* /
196         QPainter::drawLine(toScreenX(p1.x), toScreenY(p1.y),
197                        toScreenX(p2.x), toScreenY(p1.y));
198     QPainter::drawLine(toScreenX(p2.x), toScreenY(p1.y),
199                        toScreenX(p2.x), toScreenY(p2.y));
200     QPainter::drawLine(toScreenX(p2.x), toScreenY(p2.y),
201                        toScreenX(p1.x), toScreenY(p2.y));
202     QPainter::drawLine(toScreenX(p1.x), toScreenY(p2.y),
203                        toScreenX(p1.x), toScreenY(p1.y));
204 }*/
205
206 /**
207  * Draws an arc which starts / ends exactly at the given coordinates.
208  *
209  * @param cx center in x
210  * @param cy center in y
211  * @param radius Radius
212  * @param a1 Angle 1 in rad
213  * @param a2 Angle 2 in rad
214  * @param x1 startpoint x
215  * @param y1 startpoint y
216  * @param x2 endpoint x
217  * @param y2 endpoint y
218  * @param reversed true: clockwise, false: counterclockwise
219  */
220 void PaintInterface::drawArc(const Vector & cp, double radius,
221         double a1, double a2, const Vector & p1, const Vector & p2,
222         bool reversed)
223 {
224         /*
225         QPainter::drawArc(cx - radius, cy - radius, 2 * radius, 2 * radius, a1 * 16, (a2 - a1) * 16);
226         */
227
228         if (radius <= 0.5)
229                 drawGridPoint(cp);
230         else
231         {
232 #ifdef __APPLE__
233                 drawArcMac(cp, radius, a1, a2, reversed);
234 #else
235                 int   cix;            // Next point on circle
236                 int   ciy;            //
237                 double aStep;         // Angle Step (rad)
238                 double a;             // Current Angle (rad)
239                 double linStep;       // linear step (pixels)
240
241                 if (drawingMode == RS2::ModePreview)
242                         linStep = 20.0;
243                 else
244                         linStep = 6.0;
245
246                 if (fabs(linStep / radius) <= 1.0)
247                         aStep = asin(linStep / radius);
248                 else
249                         aStep = 1.0;
250
251                 if (aStep < 0.05)
252                         aStep = 0.05;
253
254                 if (!reversed)
255                 {
256                         // Arc Counterclockwise:
257                         if (a1 > a2 - 1.0e-10)
258                                 a2 += 2 * M_PI;
259
260                         QPolygon poly;
261                         int i = 0;
262 //                      pa.resize(i + 1);
263                         poly.setPoint(i++, toScreenX(p1.x), toScreenY(p1.y));
264
265                         for(a=a1+aStep; a<=a2; a+=aStep)
266                         {
267                                 cix = toScreenX(cp.x + cos(a) * radius);
268                                 ciy = toScreenY(cp.y - sin(a) * radius);
269                                 //lineTo(cix, ciy);
270 //                              pa.resize(i + 1);
271                                 poly.setPoint(i++, cix, ciy);
272                         }
273
274                         //lineTo(toScreenX(p2.x), toScreenY(p2.y));
275 //                      pa.resize(i + 1);
276                         poly.setPoint(i++, toScreenX(p2.x), toScreenY(p2.y));
277                         drawPolygon(poly);
278                 }
279                 else
280                 {
281                         // Arc Clockwise:
282                         if (a1 < a2 + 1.0e-10)
283                                 a2 -= 2 * M_PI;
284
285                         QPolygon poly;
286                         int i = 0;
287                         poly.setPoint(i++, toScreenX(p1.x), toScreenY(p1.y));
288
289                         for(a=a1-aStep; a>=a2; a-=aStep)
290                         {
291                                 cix = toScreenX(cp.x + cos(a) * radius);
292                                 ciy = toScreenY(cp.y - sin(a) * radius);
293                                 poly.setPoint(i++, cix, ciy);
294                         }
295
296                         poly.setPoint(i++, toScreenX(p2.x), toScreenY(p2.y));
297                         drawPolygon(poly);
298                 }
299 #endif
300         }
301 }
302
303 /**
304  * Draws an arc.
305  *
306  * @param cx center in x
307  * @param cy center in y
308  * @param radius Radius
309  * @param a1 Angle 1 in rad
310  * @param a2 Angle 2 in rad
311  * @param reversed true: clockwise, false: counterclockwise
312  */
313 void PaintInterface::drawArc(const Vector & cp, double radius, double a1, double a2, bool reversed)
314 {
315         if (radius <= 0.5)
316                 drawGridPoint(cp);
317         else
318         {
319 #ifdef __APPLE__
320                 drawArcMac(cp, radius, a1, a2, reversed);
321 #else
322                 QPolygon p;
323                 createArc(p, cp, radius, a1, a2, reversed);
324 //        drawPolygon(p);
325                 painter->drawPolyline(p);
326 #endif
327         }
328 }
329
330 /**
331  * Draws an arc on apple.
332  *
333  * @param cx center in x
334  * @param cy center in y
335  * @param radius Radius
336  * @param a1 Angle 1 in rad
337  * @param a2 Angle 2 in rad
338  * @param reversed true: clockwise, false: counterclockwise
339  */
340 void PaintInterface::drawArcMac(const Vector& cp, double radius, double a1, double a2, bool reversed)
341 {
342         RS_DEBUG->print("PaintInterface::drawArcMac");
343         if (radius <= 0.5)
344                 drawGridPoint(cp);
345         else
346         {
347                 double cix;            // Next point on circle
348                 double ciy;            //
349                 double aStep;         // Angle Step (rad)
350                 double a;             // Current Angle (rad)
351                 double ox;
352                 double oy;
353
354                 if (2.0 / radius <= 1.0)
355                         aStep = asin(2.0 / radius);
356                 else
357                         aStep = 1.0;
358
359                 if (aStep < 0.05)
360                         aStep = 0.05;
361
362                 ox = cp.x + cos(a1) * radius;
363                 oy = cp.y - sin(a1) * radius;
364
365                 if (!reversed)
366                 {
367                         // Arc Counterclockwise:
368                         if (a1 > a2 - 1.0e-10)
369                                 a2 += 2 * M_PI;
370
371                         for(a=a1+aStep; a<=a2; a+=aStep)
372                         {
373                                 cix = cp.x + cos(a) * radius;
374                                 ciy = cp.y - sin(a) * radius;
375                                 drawLine(Vector(ox, oy), Vector(cix, ciy));
376                                 ox = cix;
377                                 oy = ciy;
378                         }
379                 }
380                 else
381                 {
382                         // Arc Clockwise:
383                         if (a1 < a2 + 1.0e-10)
384                                 a2 -= 2 * M_PI;
385
386                         for(a=a1-aStep; a>=a2; a-=aStep)
387                         {
388                                 cix = cp.x + cos(a) * radius;
389                                 ciy = cp.y - sin(a) * radius;
390                                 drawLine(Vector(ox, oy), Vector(cix, ciy));
391                                 ox = cix;
392                                 oy = ciy;
393                         }
394                 }
395
396                 drawLine(Vector(ox, oy), Vector(cp.x + cos(a2) * radius, cp.y - sin(a2) * radius));
397         }
398 }
399
400 /**
401  * Draws a circle.
402  * @param cx center in x
403  * @param cy center in y
404  * @param radius Radius
405  */
406 void PaintInterface::drawCircle(const Vector & cp, double radius)
407 {
408 //This old code was causing the Qt painter code to segfault...
409 //If we have problems with arcs, then drawArc() is probably the culprit.
410 #if 0
411         if (drawingMode == RS2::ModeXOR && radius < 500)
412         {
413                 // This is _very_ slow for large arcs:
414                 painter->drawEllipse(toScreenX(cp.x - radius), toScreenY(cp.y - radius), RS_Math::round(2.0 * radius), RS_Math::round(2.0 * radius));
415         }
416         else
417         {
418 #ifdef __APPLE__
419                 drawArcMac(cp, radius, 0.0, 2 * M_PI, false);
420 #else
421                 drawArc(cp, radius, 0.0, 2 * M_PI, cp + Vector(radius, 0.0), cp + Vector(radius, 0.0), false);
422 #endif
423         }
424 #else
425 //      painter->drawEllipse(toScreenX(cp.x - radius), toScreenY(cp.y - radius), RS_Math::round(2.0 * radius), RS_Math::round(2.0 * radius));
426         painter->drawEllipse(QPointF(cp.x, cp.y), radius, radius);
427 #endif
428 }
429
430 /**
431  * Draws a rotated ellipse arc.
432  */
433 void PaintInterface::drawEllipse(const Vector & cp, double radius1, double radius2,
434         double angle, double a1, double a2, bool reversed)
435 {
436         double aStep;         // Angle Step (rad)
437         double a;             // Current Angle (rad)
438
439         // Angle step in rad
440         aStep = 0.01;
441
442         Vector vp;
443         Vector vc(cp.x, cp.y);
444         vp.set(cp.x + cos(a1) * radius1, cp.y - sin(a1) * radius2);
445         vp.rotate(vc, -angle);
446 //    moveTo(toScreenX(vp.x), toScreenY(vp.y));
447
448         if (!reversed)
449         {
450                 // Arc Counterclockwise:
451                 if (a1 > a2 - RS_TOLERANCE)
452                         a2 += 2 * M_PI;
453
454                 for(a=a1+aStep; a<=a2; a+=aStep)
455                 {
456                         Vector last = vp;
457                         vp.set(cp.x + cos(a) * radius1, cp.y - sin(a) * radius2);
458                         vp.rotate(vc, -angle);
459 //            lineTo(toScreenX(vp.x), toScreenY(vp.y));
460                         drawLine(Vector(toScreenX(last.x), toScreenY(last.y)),
461                                 Vector(toScreenX(vp.x), toScreenY(vp.y)));
462                 }
463         }
464         else
465         {
466                 // Arc Clockwise:
467                 if (a1 < a2 + RS_TOLERANCE)
468                         a2 -= 2 * M_PI;
469
470                 for(a=a1-aStep; a>=a2; a-=aStep)
471                 {
472                         Vector last = vp;
473                         vp.set(cp.x + cos(a) * radius1, cp.y - sin(a) * radius2);
474                         vp.rotate(vc, -angle);
475 //            lineTo(toScreenX(vp.x), toScreenY(vp.y));
476                         drawLine(Vector(toScreenX(last.x), toScreenY(last.y)),
477                                 Vector(toScreenX(vp.x), toScreenY(vp.y)));
478                 }
479         }
480
481         Vector last = vp;
482         vp.set(cp.x + cos(a2) * radius1, cp.y - sin(a2) * radius2);
483         vp.rotate(vc, -angle);
484 //    lineTo(toScreenX(vp.x), toScreenY(vp.y));
485         drawLine(Vector(toScreenX(last.x), toScreenY(last.y)),
486                 Vector(toScreenX(vp.x), toScreenY(vp.y)));
487         //}
488 }
489
490 /**
491  * Draws image.
492  */
493 void PaintInterface::drawImg(QImage & img, const Vector & pos,
494         double angle, const Vector & factor, int sx, int sy, int sw, int sh)
495 {
496         painter->save();
497
498         QMatrix wm;
499         wm.translate(pos.x, pos.y);
500         wm.scale(factor.x, factor.y);
501         wm.rotate(RS_Math::rad2deg(-angle));
502         painter->setWorldMatrix(wm);
503
504         if (fabs(angle) < 1.0e-4)
505                 painter->drawImage(0 + sx, -img.height() + sy, img, sx, sy, sw, sh);
506         else
507                 painter->drawImage(0, -img.height(), img);
508
509         painter->restore();
510 }
511
512 void PaintInterface::fillRect(int x1, int y1, int w, int h, const RS_Color & col)
513 {
514         painter->fillRect(x1, y1, w, h, col);
515 }
516
517 void PaintInterface::fillTriangle(const Vector & p1, const Vector & p2, const Vector & p3)
518 {
519         QPolygon poly(3);
520         poly.putPoints(0, 3, toScreenX(p1.x),toScreenY(p1.y), toScreenX(p2.x),toScreenY(p2.y), toScreenX(p3.x),toScreenY(p3.y));
521         setBrush(painter->pen().color());
522         drawPolygon(poly);
523 }
524
525 void PaintInterface::erase()
526 {
527         painter->eraseRect(0, 0, getWidth(), getHeight());
528 }
529
530 int PaintInterface::getWidth()
531 {
532         return painter->device()->width();
533 }
534
535 int PaintInterface::getHeight()
536 {
537         return painter->device()->height();
538 }
539
540 void PaintInterface::setPreviewPen()
541 {
542         setPen(RS_Color(0, 255, 255));
543 }
544
545 RS_Pen PaintInterface::getPen()
546 {
547         return lpen;
548 }
549
550 void PaintInterface::setPen(const RS_Pen & pen)
551 {
552         lpen = pen;
553
554         if (drawingMode == RS2::ModeBW)
555                 lpen.setColor(RS_Color(0, 0, 0));
556
557         QPen p(lpen.getColor(), RS_Math::round(lpen.getScreenWidth()), RS2::rsToQtLineType(lpen.getLineType()));
558         p.setJoinStyle(Qt::RoundJoin);
559         p.setCapStyle(Qt::RoundCap);
560         painter->setPen(p);
561 }
562
563 void PaintInterface::setPen(const RS_Color & color)
564 {
565         if (drawingMode == RS2::ModeBW)
566         {
567                 lpen.setColor(RS_Color(0, 0, 0));
568                 painter->setPen(RS_Color(0, 0, 0));
569         }
570         else
571         {
572                 lpen.setColor(color);
573                 painter->setPen(color);
574         }
575 }
576
577 void PaintInterface::setPen(int r, int g, int b)
578 {
579         if (drawingMode == RS2::ModeBW)
580                 setPen(QColor(0, 0, 0));
581         else
582                 setPen(QColor(r, g, b));
583 }
584
585 void PaintInterface::disablePen(void)
586 {
587         lpen = RS_Pen(RS2::FlagInvalid);
588         painter->setPen(Qt::NoPen);
589 }
590
591 void PaintInterface::setBrush(const RS_Color & color)
592 {
593         if (drawingMode == RS2::ModeBW)
594                 painter->setBrush(QColor(0, 0, 0));
595         else
596                 painter->setBrush(color);
597 }
598
599 void PaintInterface::disableBrush(void)
600 {
601 //      lpen = RS_Pen(RS2::FlagInvalid);
602         painter->setBrush(Qt::NoBrush);
603 }
604
605 void PaintInterface::drawPolygon(const QPolygon & a)
606 {
607         painter->drawPolygon(a);
608 }
609
610 void PaintInterface::setXORMode(void)
611 {
612         painter->setCompositionMode(QPainter::CompositionMode_Xor);
613 }
614
615 void PaintInterface::setNormalMode(void)
616 {
617         painter->setCompositionMode(QPainter::CompositionMode_SourceOver);
618 }
619
620 void PaintInterface::setClipRect(int x, int y, int w, int h)
621 {
622         painter->setClipRect(x, y, w, h);
623         painter->setClipping(true);
624 }
625
626 void PaintInterface::resetClipping()
627 {
628         painter->setClipping(false);
629 }