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"
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
)
45 qcErrorMsg( QString("Painting already in progress!") );
51 painter
->setRenderHint( QPainter::Antialiasing
, true );
52 QColor
black( 0,0,0 );
54 pen
.setCapStyle( Qt::FlatCap
);
55 painter
->setPen( pen
);
56 painter
->setBrush( black
);
58 path
= QPainterPath();
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
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() {
105 qcErrorMsg( QString("Usage of QPen is not allowed at this point!") );
111 #define QC_QPEN_PRIMITIVE( NAME, ARGC, RECEIVER, ARGS, GLOBAL ) \
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 ); \
124 int LangPrimitive<NAME>::implementation( RECEIVER, ARGS, GLOBAL )
126 namespace QtCollider
{
128 QC_QPEN_PRIMITIVE( QPen_Save
, 0, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
134 QC_QPEN_PRIMITIVE( QPen_Restore
, 0, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
140 QC_QPEN_PRIMITIVE( QPen_Clear
, 0, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
142 path
= QPainterPath();
146 QC_QPEN_PRIMITIVE( QPen_FillColor
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
148 QColor color
= Slot::toColor( a
);
149 painter
->setBrush( color
);
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
);
162 QC_QPEN_PRIMITIVE( QPen_Width
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
165 if( slotFloatVal( a
, &width
) ) return errWrongType
;
166 QPen pen
= painter
->pen();
167 pen
.setWidthF( width
);
168 painter
->setPen( pen
);
172 QC_QPEN_PRIMITIVE( QPen_SetJoinStyle
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
174 int style
= Slot::toInt(a
);
175 QPen pen
= painter
->pen();
179 pen
.setJoinStyle( Qt::MiterJoin
); break;
181 pen
.setJoinStyle( Qt::RoundJoin
); break;
183 pen
.setJoinStyle( Qt::BevelJoin
); break;
188 painter
->setPen( pen
);
193 QC_QPEN_PRIMITIVE( QPen_SetCapStyle
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
195 int style
= Slot::toInt(a
);
196 QPen pen
= painter
->pen();
200 pen
.setCapStyle( Qt::FlatCap
); break;
202 pen
.setCapStyle( Qt::RoundCap
); break;
204 pen
.setCapStyle( Qt::SquareCap
); break;
209 painter
->setPen( pen
);
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
;
227 pattern
<< *f
<< *(f
+1);
232 if( pattern
.size() ) {
233 QPen pen
= painter
->pen();
234 pen
.setDashPattern( pattern
);
235 painter
->setPen( pen
);
241 QC_QPEN_PRIMITIVE( QPen_SetOpacity
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
243 float opacity
= Slot::toFloat(a
);
244 painter
->setOpacity( opacity
);
248 QC_QPEN_PRIMITIVE( QPen_Clip
, 0, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
250 painter
->setClipPath( path
);
251 path
= QPainterPath();
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
);
263 QC_QPEN_PRIMITIVE( QPen_SetFont
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
265 painter
->setFont( Slot::toFont( a
) );
269 QC_QPEN_PRIMITIVE( QPen_Translate
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
272 if( slotFloatVal( a
, &x
) ) return errWrongType
;
273 if( slotFloatVal( a
+1, &y
) ) return errWrongType
;
274 painter
->translate( x
, y
);
278 QC_QPEN_PRIMITIVE( QPen_Scale
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
281 if( slotFloatVal( a
, &x
) ) return errWrongType
;
282 if( slotFloatVal( a
+1, &y
) ) return errWrongType
;
283 painter
->scale( x
, y
);
287 QC_QPEN_PRIMITIVE( QPen_Shear
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
290 if( slotFloatVal( a
, &x
) ) return errWrongType
;
291 if( slotFloatVal( a
+1, &y
) ) return errWrongType
;
292 painter
->shear( x
, y
);
296 QC_QPEN_PRIMITIVE( QPen_Rotate
, 3, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
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
);
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
;
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
);
327 QC_QPEN_PRIMITIVE( QPen_MoveTo
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
329 QPointF point
= Slot::toPoint( a
);
330 path
.moveTo( point
);
334 QC_QPEN_PRIMITIVE( QPen_LineTo
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
336 QPointF point
= Slot::toPoint( a
);
337 path
.lineTo( point
);
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
);
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
);
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 );
363 if( slotFloatVal( arg
+2, &radius
) ) return errWrongType
;
364 radius
= qMax( 0.f
, radius
);
366 vec2
a( path
.currentPosition() );
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 );
401 QC_QPEN_PRIMITIVE( QPen_AddRect
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
403 QRectF rect
= Slot::toRect( a
);
404 path
.addRect( rect
);
408 QC_QPEN_PRIMITIVE( QPen_AddRoundedRect
, 3, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
410 float radiusX
, radiusY
;
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
);
421 QC_QPEN_PRIMITIVE( QPen_AddEllipse
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
423 QRectF rect
= Slot::toRect( a
);
424 path
.addEllipse( rect
);
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
) );
441 rect
.setSize( QSizeF( 2*radius
, 2*radius
) );
442 rect
.moveCenter( center
);
444 path
.moveTo( start
);
445 path
.arcTo( rect
, radToDeg( startAngle
), radToDeg( arcAngle
) );
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
);
459 rect
.setSize( QSizeF( 2*radius
, 2*radius
) );
460 rect
.moveCenter( center
);
461 path
.arcTo( rect
, startAngle
, sweepLength
);
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
) );
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
);
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();
502 painter
->setPen( Qt::NoPen
); break;
504 painter
->setBrush( Qt::NoBrush
); break;
510 painter
->drawPath( path
);
512 path
= QPainterPath();
514 painter
->setPen( pen
);
515 painter
->setBrush( brush
);
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();
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();
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 );
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
);
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 );
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
);
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
);
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