"Post Window" -> "Post window" prevents it being seen as two separate
[supercollider.git] / QtCollider / primitives / prim_QPen.cpp
blobc28ac4f2ed85feeeeb1f5eaf92db7c7847dcf9c4
1 /************************************************************************
3 * Copyright 2010-2012 Jakob Leben (jakob.leben@gmail.com)
5 * Copyright 2010 Ivan Leben (ivan.leben@gmail.com) (QPen_ArcTo)
7 * This file is part of SuperCollider Qt GUI.
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 ************************************************************************/
24 #include "primitives.h"
25 #include "../painting.h"
26 #include "../Slot.h"
28 #include <QPainter>
29 #include <QVector2D>
30 #include <QVector3D>
31 #include <cmath>
33 static bool announced = false;
34 static QPainter *painter = 0;
35 static QPainterPath path;
37 namespace QtCollider {
39 void announcePainting() { announced = true; }
40 bool paintingAnnounced() { return announced; }
42 bool beginPainting( QPainter *p )
44 if( painter ) {
45 qcErrorMsg( QString("Painting already in progress!") );
46 return false;
49 painter = p;
51 painter->setRenderHint( QPainter::Antialiasing, true );
52 QColor black( 0,0,0 );
53 QPen pen( black );
54 pen.setCapStyle( Qt::FlatCap );
55 painter->setPen( pen );
56 painter->setBrush( black );
58 path = QPainterPath();
60 return true;
63 void endPainting()
65 painter = 0;
66 announced = false;
69 QPainter *globalPainter() { return painter; }
72 typedef QVector2D vec2;
73 typedef QVector3D vec3;
74 static const double PI = 3.14159265358979323846264338327950288419717;
76 inline static qreal globalAngle( const vec2 &v )
78 //assuming v is normalized
79 qreal cosa = v.x();
80 qreal sina = -v.y();
81 return sina >= 0.0 ? acosf(cosa) : 2.0*PI - acosf(cosa);
84 inline static qreal vectorAngle( const vec2 &v1, const vec2 &v2 )
86 //assuming v1,v2 are normalized
87 return acosf( vec2::dotProduct( v1, v2 ) );
90 inline static qreal signedAngle( const vec2 &v1, const vec2 &v2 )
92 //assuming v1,v2 are normalized
93 qreal a = vectorAngle( v1, v2 );
94 vec3 c = vec3::crossProduct( vec3(v1), vec3(v2) );
95 return c.z() > 0.0 ? a : -a;
98 inline static qreal radToDeg( qreal rad )
100 return rad * 180.0 / PI;
103 inline static bool isPenValid() {
104 if( !painter ) {
105 qcErrorMsg( QString("Usage of QPen is not allowed at this point!") );
106 return false;
108 return true;
111 #define QC_QPEN_PRIMITIVE( NAME, ARGC, RECEIVER, ARGS, GLOBAL ) \
112 struct NAME {}; \
113 template<> struct LangPrimitive <NAME> { \
114 static int implementation ( RECEIVER, ARGS, GLOBAL ); \
115 static int mediate( VMGlobals *g, int i ) { \
116 if(!isPenValid()) return errFailed; \
117 PyrSlot *stack = g->sp - i + 1; \
118 return implementation( stack, i > 1 ? stack+1 : 0, g ); \
120 static void define( int base, int index ) { \
121 definePrimitive( base, index, "_" #NAME, &mediate, ARGC + 1, 0 ); \
123 }; \
124 int LangPrimitive<NAME>::implementation( RECEIVER, ARGS, GLOBAL )
126 namespace QtCollider {
128 QC_QPEN_PRIMITIVE( QPen_Save, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
130 painter->save();
131 return errNone;
134 QC_QPEN_PRIMITIVE( QPen_Restore, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
136 painter->restore();
137 return errNone;
140 QC_QPEN_PRIMITIVE( QPen_Clear, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
142 path = QPainterPath();
143 return errNone;
146 QC_QPEN_PRIMITIVE( QPen_FillColor, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
148 QColor color = Slot::toColor( a );
149 painter->setBrush( color );
150 return errNone;
153 QC_QPEN_PRIMITIVE( QPen_StrokeColor, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
155 QColor color = Slot::toColor( a );
156 QPen pen = painter->pen();
157 pen.setColor( color );
158 painter->setPen( pen );
159 return errNone;
162 QC_QPEN_PRIMITIVE( QPen_Width, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
164 float width;
165 if( slotFloatVal( a, &width ) ) return errWrongType;
166 QPen pen = painter->pen();
167 pen.setWidthF( width );
168 painter->setPen( pen );
169 return errNone;
172 QC_QPEN_PRIMITIVE( QPen_SetJoinStyle, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
174 int style = Slot::toInt(a);
175 QPen pen = painter->pen();
177 switch( style ) {
178 case 0:
179 pen.setJoinStyle( Qt::MiterJoin ); break;
180 case 1:
181 pen.setJoinStyle( Qt::RoundJoin ); break;
182 case 2:
183 pen.setJoinStyle( Qt::BevelJoin ); break;
184 default:
185 return errFailed;
188 painter->setPen( pen );
190 return errNone;
193 QC_QPEN_PRIMITIVE( QPen_SetCapStyle, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
195 int style = Slot::toInt(a);
196 QPen pen = painter->pen();
198 switch( style ) {
199 case 0:
200 pen.setCapStyle( Qt::FlatCap ); break;
201 case 1:
202 pen.setCapStyle( Qt::RoundCap ); break;
203 case 2:
204 pen.setCapStyle( Qt::SquareCap ); break;
205 default:
206 return errFailed;
209 painter->setPen( pen );
211 return errNone;
214 QC_QPEN_PRIMITIVE( QPen_SetDashPattern, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
216 if( !IsObj( a ) ) return errWrongType;
217 PyrObject *obj = slotRawObject( a );
218 if( obj->classptr != class_floatarray ) return errWrongType;
219 PyrFloatArray *farray = reinterpret_cast<PyrFloatArray*>(obj);
221 int s = farray->size;
222 float *f = farray->f;
223 QVector<qreal> pattern;
225 int i = 1;
226 while( i<s ) {
227 pattern << *f << *(f+1);
228 f += 2;
229 i += 2;
232 if( pattern.size() ) {
233 QPen pen = painter->pen();
234 pen.setDashPattern( pattern );
235 painter->setPen( pen );
238 return errNone;
241 QC_QPEN_PRIMITIVE( QPen_SetOpacity, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
243 float opacity = Slot::toFloat(a);
244 painter->setOpacity( opacity );
245 return errNone;
248 QC_QPEN_PRIMITIVE( QPen_Clip, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
250 painter->setClipPath( path );
251 path = QPainterPath();
252 return errNone;
255 QC_QPEN_PRIMITIVE( QPen_AntiAliasing, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
257 bool b = IsTrue( a );
258 if( !b && !IsFalse( a ) ) return errWrongType;
259 painter->setRenderHint( QPainter::Antialiasing, b );
260 return errNone;
263 QC_QPEN_PRIMITIVE( QPen_SetFont, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
265 painter->setFont( Slot::toFont( a ) );
266 return errNone;
269 QC_QPEN_PRIMITIVE( QPen_Translate, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
271 float x, y;
272 if( slotFloatVal( a, &x ) ) return errWrongType;
273 if( slotFloatVal( a+1, &y ) ) return errWrongType;
274 painter->translate( x, y );
275 return errNone;
278 QC_QPEN_PRIMITIVE( QPen_Scale, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
280 float x, y;
281 if( slotFloatVal( a, &x ) ) return errWrongType;
282 if( slotFloatVal( a+1, &y ) ) return errWrongType;
283 painter->scale( x, y );
284 return errNone;
287 QC_QPEN_PRIMITIVE( QPen_Shear, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
289 float x, y;
290 if( slotFloatVal( a, &x ) ) return errWrongType;
291 if( slotFloatVal( a+1, &y ) ) return errWrongType;
292 painter->shear( x, y );
293 return errNone;
296 QC_QPEN_PRIMITIVE( QPen_Rotate, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
298 float angle, x, y;
299 if( slotFloatVal( a, &angle ) ) return errWrongType;
300 if( slotFloatVal( a+1, &x ) ) return errWrongType;
301 if( slotFloatVal( a+2, &y ) ) return errWrongType;
303 painter->translate( x, y );
304 painter->rotate( angle );
305 painter->translate( -x, -y );
307 return errNone;
310 QC_QPEN_PRIMITIVE( QPen_Transform, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
312 VariantList list = Slot::toVariantList( a );
313 if( list.data.count() < 6 ) return errWrongType;
314 float f[6];
315 int i = 6;
316 while( i ) {
317 --i;
318 QVariant var = list.data[i];
319 if( !var.canConvert( QVariant::Double ) ) return errWrongType;
320 f[i] = list.data[i].value<float>();
322 QTransform transform( f[0], f[1], f[2], f[3], f[4], f[5] );
323 painter->setWorldTransform( transform );
324 return errNone;
327 QC_QPEN_PRIMITIVE( QPen_MoveTo, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
329 QPointF point = Slot::toPoint( a );
330 path.moveTo( point );
331 return errNone;
334 QC_QPEN_PRIMITIVE( QPen_LineTo, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
336 QPointF point = Slot::toPoint( a );
337 path.lineTo( point );
338 return errNone;
341 QC_QPEN_PRIMITIVE( QPen_CubicTo, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
343 QPointF endPoint = Slot::toPoint( a );
344 QPointF cPoint1 = Slot::toPoint( a+1 );
345 QPointF cPoint2 = Slot::toPoint( a+2 );
346 path.cubicTo( cPoint1, cPoint2, endPoint );
347 return errNone;
350 QC_QPEN_PRIMITIVE( QPen_QuadTo, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
352 QPointF endPoint = Slot::toPoint( a );
353 QPointF cPoint = Slot::toPoint( a+1 );
354 path.quadTo( cPoint, endPoint );
355 return errNone;
358 QC_QPEN_PRIMITIVE( QPen_ArcTo, 3, PyrSlot *r, PyrSlot *arg, VMGlobals *g )
360 QPointF pt1 = Slot::toPoint( arg );
361 QPointF pt2 = Slot::toPoint( arg+1 );
362 float radius;
363 if( slotFloatVal( arg+2, &radius ) ) return errWrongType;
364 radius = qMax( 0.f, radius );
366 vec2 a( path.currentPosition() );
367 vec2 b( pt1 );
368 vec2 c( pt2 );
370 vec2 va = (a - b).normalized();
371 vec2 vc = (c - b).normalized();
372 vec2 m = (va + vc).normalized();
374 qreal lineAngle = vectorAngle( va, vc );
375 qreal dm = radius / sinf( lineAngle * 0.5f );
376 qreal dv = radius / tanf( lineAngle * 0.5f );
378 vec2 center = b + dm * m;
379 vec2 start = b + dv * va;
380 vec2 end = b + dv * vc;
382 vec2 vs = (start - center).normalized();
383 vec2 ve = (end - center).normalized();
385 qreal arcAngle = signedAngle( ve, vs );
386 qreal arcStart = globalAngle( vs );
388 path.lineTo( start.x(), start.y() );
390 path.arcTo( center.x() - radius, center.y() - radius, 2*radius, 2*radius,
391 radToDeg( arcStart ), radToDeg( arcAngle ) );
393 // The current SC API does not want to pull the line to the last point.
394 // Personally, I think it would be better:
396 // path.lineTo( pt2 );
398 return errNone;
401 QC_QPEN_PRIMITIVE( QPen_AddRect, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
403 QRectF rect = Slot::toRect( a );
404 path.addRect( rect );
405 return errNone;
408 QC_QPEN_PRIMITIVE( QPen_AddRoundedRect, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
410 float radiusX, radiusY;
411 QRectF rect;
413 rect = Slot::toRect( a );
414 if( slotFloatVal( a+1, &radiusX ) ) return errWrongType;
415 if( slotFloatVal( a+2, &radiusY ) ) return errWrongType;
417 path.addRoundedRect( rect, radiusX, radiusY );
418 return errNone;
421 QC_QPEN_PRIMITIVE( QPen_AddEllipse, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
423 QRectF rect = Slot::toRect( a );
424 path.addEllipse( rect );
425 return errNone;
428 QC_QPEN_PRIMITIVE( QPen_AddArc, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
430 QPointF center = Slot::toPoint( a );
431 float radius, startAngle, arcAngle;
432 if( slotFloatVal( a+1, &radius ) ) return errWrongType;
433 if( slotFloatVal( a+2, &startAngle ) ) return errWrongType;
434 if( slotFloatVal( a+3, &arcAngle ) ) return errWrongType;
436 // have to swap angle direction for sinf()
437 QPointF start( radius * cosf( startAngle ), -radius * sinf( startAngle ) );
438 start += center;
440 QRectF rect;
441 rect.setSize( QSizeF( 2*radius, 2*radius ) );
442 rect.moveCenter( center );
444 path.moveTo( start );
445 path.arcTo( rect, radToDeg( startAngle ), radToDeg( arcAngle ) );
447 return errNone;
450 QC_QPEN_PRIMITIVE( QPen_AddWedge, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
452 QPointF center = Slot::toPoint( a );
453 float radius, startAngle, sweepLength;
454 if( slotFloatVal( a+1, &radius ) ) return errWrongType;
455 if( slotFloatVal( a+2, &startAngle ) ) return errWrongType;
456 if( slotFloatVal( a+3, &sweepLength ) ) return errWrongType;
457 path.moveTo( center );
458 QRectF rect;
459 rect.setSize( QSizeF( 2*radius, 2*radius ) );
460 rect.moveCenter( center );
461 path.arcTo( rect, startAngle, sweepLength );
462 path.closeSubpath();
463 return errNone;
466 QC_QPEN_PRIMITIVE( QPen_AddAnnularWedge, 5, PyrSlot *, PyrSlot *a, VMGlobals *g )
468 QPointF c = Slot::toPoint( a );
469 float innerRadius, outerRadius, startAngle, arcAngle;
470 if( slotFloatVal( a+1, &innerRadius ) ) return errWrongType;
471 if( slotFloatVal( a+2, &outerRadius ) ) return errWrongType;
472 if( slotFloatVal( a+3, &startAngle ) ) return errWrongType;
473 if( slotFloatVal( a+4, &arcAngle ) ) return errWrongType;
475 float saDeg = radToDeg( startAngle );
476 float aaDeg = radToDeg( arcAngle );
478 QPointF start( outerRadius * cosf( startAngle ), -outerRadius * sinf( startAngle ) );
479 start += c;
480 path.moveTo( start );
482 QPointF pt( innerRadius, innerRadius );
483 path.arcTo( QRectF( c - pt, c + pt ), saDeg, aaDeg );
485 pt = QPointF( outerRadius, outerRadius );
486 path.arcTo( QRectF( c - pt, c + pt ), saDeg + aaDeg, -aaDeg );
488 return errNone;
491 QC_QPEN_PRIMITIVE( QPen_Draw, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
493 if( path.isEmpty() ) return errNone;
495 int style = Slot::toInt( a );
496 QPen pen = painter->pen();
497 QBrush brush = painter->brush();
499 switch ( style ) {
500 case 0:
501 case 1:
502 painter->setPen( Qt::NoPen ); break;
503 case 2:
504 painter->setBrush( Qt::NoBrush ); break;
505 case 3:
506 case 4:
507 default: ;
510 painter->drawPath( path );
512 path = QPainterPath();
514 painter->setPen( pen );
515 painter->setBrush( brush );
517 return errNone;
520 QC_QPEN_PRIMITIVE( QPen_FillAxialGradient, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
522 QPointF pt1 = Slot::toPoint(a+0);
523 QPointF pt2 = Slot::toPoint(a+1);
524 QColor c1 = Slot::toColor(a+2);
525 QColor c2 = Slot::toColor(a+3);
527 QLinearGradient grad( pt1, pt2 );
528 grad.setColorAt( 0, c1 );
529 grad.setColorAt( 1, c2 );
531 QPen pen = painter->pen();
532 QBrush brush = painter->brush();
534 painter->setPen( Qt::NoPen );
535 painter->setBrush( grad );
537 painter->drawPath( path );
539 painter->setPen( pen );
540 painter->setBrush( brush );
542 path = QPainterPath();
544 return errNone;
547 QC_QPEN_PRIMITIVE( QPen_FillRadialGradient, 6, PyrSlot *r, PyrSlot *a, VMGlobals *g )
549 QPointF pt1 = Slot::toPoint(a+0);
550 QPointF pt2 = Slot::toPoint(a+1);
551 float r1 = Slot::toFloat(a+2);
552 float r2 = Slot::toFloat(a+3);
553 QColor c1 = Slot::toColor(a+4);
554 QColor c2 = Slot::toColor(a+5);
556 QRadialGradient grad( pt2, r2, pt1 );
557 grad.setColorAt( (r2 > 0 ? r1 / r2 : 0), c1 );
558 grad.setColorAt( 1, c2 );
560 QPen pen = painter->pen();
561 QBrush brush = painter->brush();
563 painter->setPen( Qt::NoPen );
564 painter->setBrush( grad );
566 painter->drawPath( path );
568 painter->setPen( pen );
569 painter->setBrush( brush );
571 path = QPainterPath();
573 return errNone;
576 QC_QPEN_PRIMITIVE( QPen_StringAtPoint, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
578 QString str = Slot::toString( a );
579 if( str.isEmpty() ) return errNone;
580 QPointF pt = Slot::toPoint( a+1 );
582 painter->save();
583 if( NotNil( a+2 ) ) painter->setFont( Slot::toFont( a+2 ) );
584 QPen pen( painter->pen() );
585 pen.setColor( NotNil( a+3 ) ? Slot::toColor( a+3 ) : painter->brush().color() );
586 painter->setPen( pen );
588 QFont f( painter->font() );
589 QFontMetrics fm( f );
590 QRect rect = fm.boundingRect( str );
591 painter->drawText( pt - rect.topLeft(), str );
593 painter->restore();
595 return errNone;
598 QC_QPEN_PRIMITIVE( QPen_StringInRect, 5, PyrSlot *r, PyrSlot *a, VMGlobals *g )
600 QString str = Slot::toString( a );
601 if( str.isEmpty() ) return errNone;
602 QRectF rect = Slot::toRect( a+1 );
604 painter->save();
605 if( NotNil( a+2 ) ) painter->setFont( Slot::toFont( a+2 ) );
606 QPen pen( painter->pen() );
607 pen.setColor( NotNil( a+3 ) ? Slot::toColor( a+3 ) : painter->brush().color() );
608 painter->setPen( pen );
610 Qt::Alignment align;
611 if( NotNil(a+4) ) align = static_cast<Qt::Alignment>( Slot::toInt( a+4 ) );
612 else align = Qt::AlignTop | Qt::AlignLeft;
614 painter->drawText( rect, align, str );
616 painter->restore();
618 return errNone;
621 void defineQPenPrimitives()
623 LangPrimitiveDefiner definer;
624 definer.define<QPen_Save>();
625 definer.define<QPen_Restore>();
626 definer.define<QPen_Clear>();
627 definer.define<QPen_FillColor>();
628 definer.define<QPen_StrokeColor>();
629 definer.define<QPen_Width>();
630 definer.define<QPen_SetJoinStyle>();
631 definer.define<QPen_SetCapStyle>();
632 definer.define<QPen_SetDashPattern>();
633 definer.define<QPen_SetOpacity>();
634 definer.define<QPen_Clip>();
635 definer.define<QPen_AntiAliasing>();
636 definer.define<QPen_SetFont>();
637 definer.define<QPen_Translate>();
638 definer.define<QPen_Scale>();
639 definer.define<QPen_Shear>();
640 definer.define<QPen_Rotate>();
641 definer.define<QPen_Transform>();
642 definer.define<QPen_MoveTo>();
643 definer.define<QPen_LineTo>();
644 definer.define<QPen_CubicTo>();
645 definer.define<QPen_QuadTo>();
646 definer.define<QPen_ArcTo>();
647 definer.define<QPen_AddRect>();
648 definer.define<QPen_AddRoundedRect>();
649 definer.define<QPen_AddEllipse>();
650 definer.define<QPen_AddArc>();
651 definer.define<QPen_AddWedge>();
652 definer.define<QPen_AddAnnularWedge>();
653 definer.define<QPen_Draw>();
654 definer.define<QPen_FillAxialGradient>();
655 definer.define<QPen_FillRadialGradient>();
656 definer.define<QPen_StringAtPoint>();
657 definer.define<QPen_StringInRect>();
660 } // namespace QtCollider