class library: PriorityQueue - implement removeValue, hide array
[supercollider.git] / QtCollider / primitives / prim_QPen.cpp
blobb7fff7844f0b3522909a155f3ce5ba0f775c1d8d
1 /************************************************************************
3 * Copyright 2010 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 namespace QtCollider
36 template <int FN( PyrSlot*, PyrSlot*, VMGlobals* )>
37 class QPenPrimitive
39 public:
40 QPenPrimitive ( const char *name, int argc ) {
41 LangPrimitiveData d;
42 d.mediator = &mediate;
43 d.name = strdup(name);
44 d.argc = argc;
45 langPrimitives().append( d );
47 private:
48 static int mediate( VMGlobals *g, int i ) {
49 if( !globalPainter() ) {
50 qcErrorMsg( QString("Usage of QPen is not allowed at this point!") );
51 return errFailed;
53 PyrSlot *stack = g->sp - i + 1;
54 int ret = (*FN)( stack, i > 1 ? stack+1 : 0, g );
55 return ret;
61 #define QC_QPEN_PRIMITIVE( name, argc, receiver, args, global ) \
62 int name ( receiver, args, global ); \
63 static QtCollider::QPenPrimitive<&name> p_##name( "_" #name, argc ); \
64 int name ( receiver, args, global )
66 static bool announced = false;
67 static QPainter *painter = 0;
68 static QPainterPath path;
70 namespace QtCollider {
71 void announcePainting() { announced = true; }
72 bool paintingAnnounced() { return announced; }
74 bool beginPainting( QPainter *p )
76 if( painter ) {
77 qcErrorMsg( QString("Painting already in progress!") );
78 return false;
81 painter = p;
83 painter->setRenderHint( QPainter::Antialiasing, true );
84 QColor black( 0,0,0 );
85 QPen pen( black );
86 pen.setCapStyle( Qt::FlatCap );
87 painter->setPen( pen );
88 painter->setBrush( black );
90 path = QPainterPath();
92 return true;
95 void endPainting()
97 painter = 0;
98 announced = false;
101 QPainter *globalPainter() { return painter; }
104 typedef QVector2D vec2;
105 typedef QVector3D vec3;
106 static const double PI = 3.14159265358979323846264338327950288419717;
108 inline static qreal globalAngle( const vec2 &v )
110 //assuming v is normalized
111 qreal cosa = v.x();
112 qreal sina = -v.y();
113 return sina >= 0.0 ? acosf(cosa) : 2.0*PI - acosf(cosa);
116 inline static qreal vectorAngle( const vec2 &v1, const vec2 &v2 )
118 //assuming v1,v2 are normalized
119 return acosf( vec2::dotProduct( v1, v2 ) );
122 inline static qreal signedAngle( const vec2 &v1, const vec2 &v2 )
124 //assuming v1,v2 are normalized
125 qreal a = vectorAngle( v1, v2 );
126 vec3 c = vec3::crossProduct( vec3(v1), vec3(v2) );
127 return c.z() > 0.0 ? a : -a;
130 inline static qreal radToDeg( qreal rad )
132 return rad * 180.0 / PI;
135 using namespace QtCollider;
137 QC_QPEN_PRIMITIVE( QPen_Save, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
139 painter->save();
140 return errNone;
143 QC_QPEN_PRIMITIVE( QPen_Restore, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
145 painter->restore();
146 return errNone;
149 QC_QPEN_PRIMITIVE( QPen_Clear, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
151 path = QPainterPath();
152 return errNone;
155 QC_QPEN_PRIMITIVE( QPen_FillColor, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
157 QColor color = Slot::toColor( a );
158 painter->setBrush( color );
159 return errNone;
162 QC_QPEN_PRIMITIVE( QPen_StrokeColor, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
164 QColor color = Slot::toColor( a );
165 QPen pen = painter->pen();
166 pen.setColor( color );
167 painter->setPen( pen );
168 return errNone;
171 QC_QPEN_PRIMITIVE( QPen_Width, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
173 float width;
174 if( slotFloatVal( a, &width ) ) return errWrongType;
175 QPen pen = painter->pen();
176 pen.setWidthF( width );
177 painter->setPen( pen );
178 return errNone;
181 QC_QPEN_PRIMITIVE( QPen_SetJoinStyle, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
183 int style = Slot::toInt(a);
184 QPen pen = painter->pen();
186 switch( style ) {
187 case 0:
188 pen.setJoinStyle( Qt::MiterJoin ); break;
189 case 1:
190 pen.setJoinStyle( Qt::RoundJoin ); break;
191 case 2:
192 pen.setJoinStyle( Qt::BevelJoin ); break;
193 default:
194 return errFailed;
197 painter->setPen( pen );
199 return errNone;
202 QC_QPEN_PRIMITIVE( QPen_SetCapStyle, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
204 int style = Slot::toInt(a);
205 QPen pen = painter->pen();
207 switch( style ) {
208 case 0:
209 pen.setCapStyle( Qt::FlatCap ); break;
210 case 1:
211 pen.setCapStyle( Qt::RoundCap ); break;
212 case 2:
213 pen.setCapStyle( Qt::SquareCap ); break;
214 default:
215 return errFailed;
218 painter->setPen( pen );
220 return errNone;
223 QC_QPEN_PRIMITIVE( QPen_SetDashPattern, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
225 if( !IsObj( a ) ) return errWrongType;
226 PyrObject *obj = slotRawObject( a );
227 if( obj->classptr != class_FloatArray ) return errWrongType;
228 PyrFloatArray *farray = reinterpret_cast<PyrFloatArray*>(obj);
230 int s = farray->size;
231 float *f = farray->f;
232 QVector<qreal> pattern;
234 int i = 1;
235 while( i<s ) {
236 pattern << *f << *(f+1);
237 f += 2;
238 i += 2;
241 if( pattern.size() ) {
242 QPen pen = painter->pen();
243 pen.setDashPattern( pattern );
244 painter->setPen( pen );
247 return errNone;
250 QC_QPEN_PRIMITIVE( QPen_SetOpacity, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
252 float opacity = Slot::toFloat(a);
253 painter->setOpacity( opacity );
254 return errNone;
257 QC_QPEN_PRIMITIVE( QPen_Clip, 0, PyrSlot *r, PyrSlot *a, VMGlobals *g )
259 painter->setClipPath( path );
260 path = QPainterPath();
261 return errNone;
264 QC_QPEN_PRIMITIVE( QPen_AntiAliasing, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
266 bool b = IsTrue( a );
267 if( !b && !IsFalse( a ) ) return errWrongType;
268 painter->setRenderHint( QPainter::Antialiasing, b );
269 return errNone;
272 QC_QPEN_PRIMITIVE( QPen_SetFont, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
274 painter->setFont( Slot::toFont( a ) );
275 return errNone;
278 QC_QPEN_PRIMITIVE( QPen_Translate, 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->translate( x, y );
284 return errNone;
287 QC_QPEN_PRIMITIVE( QPen_Scale, 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->scale( x, y );
293 return errNone;
296 QC_QPEN_PRIMITIVE( QPen_Shear, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
298 float x, y;
299 if( slotFloatVal( a, &x ) ) return errWrongType;
300 if( slotFloatVal( a+1, &y ) ) return errWrongType;
301 painter->shear( x, y );
302 return errNone;
305 QC_QPEN_PRIMITIVE( QPen_Rotate, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
307 float angle, x, y;
308 if( slotFloatVal( a, &angle ) ) return errWrongType;
309 if( slotFloatVal( a+1, &x ) ) return errWrongType;
310 if( slotFloatVal( a+2, &y ) ) return errWrongType;
312 painter->translate( x, y );
313 painter->rotate( angle );
314 painter->translate( -x, -y );
316 return errNone;
319 QC_QPEN_PRIMITIVE( QPen_Transform, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
321 VariantList list = Slot::toVariantList( a );
322 if( list.data.count() < 6 ) return errWrongType;
323 float f[6];
324 int i = 6;
325 while( i ) {
326 --i;
327 QVariant var = list.data[i];
328 if( !var.canConvert( QVariant::Double ) ) return errWrongType;
329 f[i] = list.data[i].value<float>();
331 QTransform transform( f[0], f[1], f[2], f[3], f[4], f[5] );
332 painter->setWorldTransform( transform );
333 return errNone;
336 QC_QPEN_PRIMITIVE( QPen_MoveTo, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
338 QPointF point = Slot::toPoint( a );
339 path.moveTo( point );
340 return errNone;
343 QC_QPEN_PRIMITIVE( QPen_LineTo, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
345 QPointF point = Slot::toPoint( a );
346 path.lineTo( point );
347 return errNone;
350 QC_QPEN_PRIMITIVE( QPen_CubicTo, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
352 QPointF endPoint = Slot::toPoint( a );
353 QPointF cPoint1 = Slot::toPoint( a+1 );
354 QPointF cPoint2 = Slot::toPoint( a+2 );
355 path.cubicTo( cPoint1, cPoint2, endPoint );
356 return errNone;
359 QC_QPEN_PRIMITIVE( QPen_QuadTo, 2, PyrSlot *r, PyrSlot *a, VMGlobals *g )
361 QPointF endPoint = Slot::toPoint( a );
362 QPointF cPoint = Slot::toPoint( a+1 );
363 path.quadTo( cPoint, endPoint );
364 return errNone;
367 QC_QPEN_PRIMITIVE( QPen_ArcTo, 3, PyrSlot *r, PyrSlot *arg, VMGlobals *g )
369 QPointF pt1 = Slot::toPoint( arg );
370 QPointF pt2 = Slot::toPoint( arg+1 );
371 float radius;
372 if( slotFloatVal( arg+2, &radius ) ) return errWrongType;
373 radius = qMax( 0.f, radius );
375 vec2 a( path.currentPosition() );
376 vec2 b( pt1 );
377 vec2 c( pt2 );
379 vec2 va = (a - b).normalized();
380 vec2 vc = (c - b).normalized();
381 vec2 m = (va + vc).normalized();
383 qreal lineAngle = vectorAngle( va, vc );
384 qreal dm = radius / sinf( lineAngle * 0.5f );
385 qreal dv = radius / tanf( lineAngle * 0.5f );
387 vec2 center = b + dm * m;
388 vec2 start = b + dv * va;
389 vec2 end = b + dv * vc;
391 vec2 vs = (start - center).normalized();
392 vec2 ve = (end - center).normalized();
394 qreal arcAngle = signedAngle( ve, vs );
395 qreal arcStart = globalAngle( vs );
397 path.lineTo( start.x(), start.y() );
399 path.arcTo( center.x() - radius, center.y() - radius, 2*radius, 2*radius,
400 radToDeg( arcStart ), radToDeg( arcAngle ) );
402 // The current SC API does not want to pull the line to the last point.
403 // Personally, I think it would be better:
405 // path.lineTo( pt2 );
407 return errNone;
410 QC_QPEN_PRIMITIVE( QPen_AddRect, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
412 QRectF rect = Slot::toRect( a );
413 path.addRect( rect );
414 return errNone;
417 QC_QPEN_PRIMITIVE( QPen_AddRoundedRect, 3, PyrSlot *r, PyrSlot *a, VMGlobals *g )
419 float radiusX, radiusY;
420 QRectF rect;
422 rect = Slot::toRect( a );
423 if( slotFloatVal( a+1, &radiusX ) ) return errWrongType;
424 if( slotFloatVal( a+2, &radiusY ) ) return errWrongType;
426 path.addRoundedRect( rect, radiusX, radiusY );
427 return errNone;
430 QC_QPEN_PRIMITIVE( QPen_AddEllipse, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
432 QRectF rect = Slot::toRect( a );
433 path.addEllipse( rect );
434 return errNone;
437 QC_QPEN_PRIMITIVE( QPen_AddArc, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
439 QPointF center = Slot::toPoint( a );
440 float radius, startAngle, arcAngle;
441 if( slotFloatVal( a+1, &radius ) ) return errWrongType;
442 if( slotFloatVal( a+2, &startAngle ) ) return errWrongType;
443 if( slotFloatVal( a+3, &arcAngle ) ) return errWrongType;
445 // have to swap angle direction for sinf()
446 QPointF start( radius * cosf( startAngle ), -radius * sinf( startAngle ) );
447 start += center;
449 QRectF rect;
450 rect.setSize( QSizeF( 2*radius, 2*radius ) );
451 rect.moveCenter( center );
453 path.moveTo( start );
454 path.arcTo( rect, radToDeg( startAngle ), radToDeg( arcAngle ) );
456 return errNone;
459 QC_QPEN_PRIMITIVE( QPen_AddWedge, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
461 QPointF center = Slot::toPoint( a );
462 float radius, startAngle, sweepLength;
463 if( slotFloatVal( a+1, &radius ) ) return errWrongType;
464 if( slotFloatVal( a+2, &startAngle ) ) return errWrongType;
465 if( slotFloatVal( a+3, &sweepLength ) ) return errWrongType;
466 path.moveTo( center );
467 QRectF rect;
468 rect.setSize( QSizeF( 2*radius, 2*radius ) );
469 rect.moveCenter( center );
470 path.arcTo( rect, startAngle, sweepLength );
471 path.closeSubpath();
472 return errNone;
475 QC_QPEN_PRIMITIVE( QPen_AddAnnularWedge, 5, PyrSlot *, PyrSlot *a, VMGlobals *g )
477 QPointF c = Slot::toPoint( a );
478 float innerRadius, outerRadius, startAngle, arcAngle;
479 if( slotFloatVal( a+1, &innerRadius ) ) return errWrongType;
480 if( slotFloatVal( a+2, &outerRadius ) ) return errWrongType;
481 if( slotFloatVal( a+3, &startAngle ) ) return errWrongType;
482 if( slotFloatVal( a+4, &arcAngle ) ) return errWrongType;
484 float saDeg = radToDeg( startAngle );
485 float aaDeg = radToDeg( arcAngle );
487 QPointF start( outerRadius * cosf( startAngle ), -outerRadius * sinf( startAngle ) );
488 start += c;
489 path.moveTo( start );
491 QPointF pt( innerRadius, innerRadius );
492 path.arcTo( QRectF( c - pt, c + pt ), saDeg, aaDeg );
494 pt = QPointF( outerRadius, outerRadius );
495 path.arcTo( QRectF( c - pt, c + pt ), saDeg + aaDeg, -aaDeg );
497 return errNone;
500 QC_QPEN_PRIMITIVE( QPen_Draw, 1, PyrSlot *r, PyrSlot *a, VMGlobals *g )
502 if( path.isEmpty() ) return errNone;
504 int style = Slot::toInt( a );
505 QPen pen = painter->pen();
506 QBrush brush = painter->brush();
508 switch ( style ) {
509 case 0:
510 case 1:
511 painter->setPen( Qt::NoPen ); break;
512 case 2:
513 painter->setBrush( Qt::NoBrush ); break;
514 case 3:
515 case 4:
516 default: ;
519 painter->drawPath( path );
521 path = QPainterPath();
523 painter->setPen( pen );
524 painter->setBrush( brush );
526 return errNone;
529 QC_QPEN_PRIMITIVE( QPen_FillAxialGradient, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
531 QPointF pt1 = Slot::toPoint(a+0);
532 QPointF pt2 = Slot::toPoint(a+1);
533 QColor c1 = Slot::toColor(a+2);
534 QColor c2 = Slot::toColor(a+3);
536 QLinearGradient grad( pt1, pt2 );
537 grad.setColorAt( 0, c1 );
538 grad.setColorAt( 1, c2 );
540 QPen pen = painter->pen();
541 QBrush brush = painter->brush();
543 painter->setPen( Qt::NoPen );
544 painter->setBrush( grad );
546 painter->drawPath( path );
548 painter->setPen( pen );
549 painter->setBrush( brush );
551 path = QPainterPath();
553 return errNone;
556 QC_QPEN_PRIMITIVE( QPen_FillRadialGradient, 6, PyrSlot *r, PyrSlot *a, VMGlobals *g )
558 QPointF pt1 = Slot::toPoint(a+0);
559 QPointF pt2 = Slot::toPoint(a+1);
560 float r1 = Slot::toFloat(a+2);
561 float r2 = Slot::toFloat(a+3);
562 QColor c1 = Slot::toColor(a+4);
563 QColor c2 = Slot::toColor(a+5);
565 QRadialGradient grad( pt2, r2, pt1 );
566 grad.setColorAt( (r2 > 0 ? r1 / r2 : 0), c1 );
567 grad.setColorAt( 1, c2 );
569 QPen pen = painter->pen();
570 QBrush brush = painter->brush();
572 painter->setPen( Qt::NoPen );
573 painter->setBrush( grad );
575 painter->drawPath( path );
577 painter->setPen( pen );
578 painter->setBrush( brush );
580 path = QPainterPath();
582 return errNone;
585 QC_QPEN_PRIMITIVE( QPen_StringAtPoint, 4, PyrSlot *r, PyrSlot *a, VMGlobals *g )
587 QString str = Slot::toString( a );
588 if( str.isEmpty() ) return errNone;
589 QPointF pt = Slot::toPoint( a+1 );
591 painter->save();
592 if( NotNil( a+2 ) ) painter->setFont( Slot::toFont( a+2 ) );
593 QPen pen( painter->pen() );
594 pen.setColor( NotNil( a+3 ) ? Slot::toColor( a+3 ) : painter->brush().color() );
595 painter->setPen( pen );
597 QFont f( painter->font() );
598 QFontMetrics fm( f );
599 QRect rect = fm.boundingRect( str );
600 painter->drawText( pt - rect.topLeft(), str );
602 painter->restore();
604 return errNone;
607 QC_QPEN_PRIMITIVE( QPen_StringInRect, 5, PyrSlot *r, PyrSlot *a, VMGlobals *g )
609 QString str = Slot::toString( a );
610 if( str.isEmpty() ) return errNone;
611 QRectF rect = Slot::toRect( a+1 );
613 painter->save();
614 if( NotNil( a+2 ) ) painter->setFont( Slot::toFont( a+2 ) );
615 QPen pen( painter->pen() );
616 pen.setColor( NotNil( a+3 ) ? Slot::toColor( a+3 ) : painter->brush().color() );
617 painter->setPen( pen );
619 Qt::Alignment align;
620 if( NotNil(a+4) ) align = static_cast<Qt::Alignment>( Slot::toInt( a+4 ) );
621 else align = Qt::AlignTop | Qt::AlignLeft;
623 painter->drawText( rect, align, str );
625 painter->restore();
627 return errNone;