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