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