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"
36 template <int FN( PyrSlot
*, PyrSlot
*, VMGlobals
* )>
40 QPenPrimitive ( const char *name
, int argc
) {
42 d
.mediator
= &mediate
;
43 d
.name
= strdup(name
);
45 langPrimitives().append( d
);
48 static int mediate( VMGlobals
*g
, int i
) {
49 if( !globalPainter() ) {
50 qcErrorMsg( QString("Usage of QPen is not allowed at this point!") );
53 PyrSlot
*stack
= g
->sp
- i
+ 1;
54 int ret
= (*FN
)( stack
, i
> 1 ? stack
+1 : 0, g
);
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
)
77 qcErrorMsg( QString("Painting already in progress!") );
83 painter
->setRenderHint( QPainter::Antialiasing
, true );
84 QColor
black( 0,0,0 );
86 pen
.setCapStyle( Qt::FlatCap
);
87 painter
->setPen( pen
);
88 painter
->setBrush( black
);
90 path
= QPainterPath();
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
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
)
143 QC_QPEN_PRIMITIVE( QPen_Restore
, 0, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
149 QC_QPEN_PRIMITIVE( QPen_Clear
, 0, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
151 path
= QPainterPath();
155 QC_QPEN_PRIMITIVE( QPen_FillColor
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
157 QColor color
= Slot::toColor( a
);
158 painter
->setBrush( color
);
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
);
171 QC_QPEN_PRIMITIVE( QPen_Width
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
174 if( slotFloatVal( a
, &width
) ) return errWrongType
;
175 QPen pen
= painter
->pen();
176 pen
.setWidthF( width
);
177 painter
->setPen( pen
);
181 QC_QPEN_PRIMITIVE( QPen_SetJoinStyle
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
183 int style
= Slot::toInt(a
);
184 QPen pen
= painter
->pen();
188 pen
.setJoinStyle( Qt::MiterJoin
); break;
190 pen
.setJoinStyle( Qt::RoundJoin
); break;
192 pen
.setJoinStyle( Qt::BevelJoin
); break;
197 painter
->setPen( pen
);
202 QC_QPEN_PRIMITIVE( QPen_SetCapStyle
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
204 int style
= Slot::toInt(a
);
205 QPen pen
= painter
->pen();
209 pen
.setCapStyle( Qt::FlatCap
); break;
211 pen
.setCapStyle( Qt::RoundCap
); break;
213 pen
.setCapStyle( Qt::SquareCap
); break;
218 painter
->setPen( pen
);
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
;
236 pattern
<< *f
<< *(f
+1);
241 if( pattern
.size() ) {
242 QPen pen
= painter
->pen();
243 pen
.setDashPattern( pattern
);
244 painter
->setPen( pen
);
250 QC_QPEN_PRIMITIVE( QPen_SetOpacity
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
252 float opacity
= Slot::toFloat(a
);
253 painter
->setOpacity( opacity
);
257 QC_QPEN_PRIMITIVE( QPen_Clip
, 0, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
259 painter
->setClipPath( path
);
260 path
= QPainterPath();
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
);
272 QC_QPEN_PRIMITIVE( QPen_SetFont
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
274 painter
->setFont( Slot::toFont( a
) );
278 QC_QPEN_PRIMITIVE( QPen_Translate
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
281 if( slotFloatVal( a
, &x
) ) return errWrongType
;
282 if( slotFloatVal( a
+1, &y
) ) return errWrongType
;
283 painter
->translate( x
, y
);
287 QC_QPEN_PRIMITIVE( QPen_Scale
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
290 if( slotFloatVal( a
, &x
) ) return errWrongType
;
291 if( slotFloatVal( a
+1, &y
) ) return errWrongType
;
292 painter
->scale( x
, y
);
296 QC_QPEN_PRIMITIVE( QPen_Shear
, 2, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
299 if( slotFloatVal( a
, &x
) ) return errWrongType
;
300 if( slotFloatVal( a
+1, &y
) ) return errWrongType
;
301 painter
->shear( x
, y
);
305 QC_QPEN_PRIMITIVE( QPen_Rotate
, 3, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
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
);
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
;
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
);
336 QC_QPEN_PRIMITIVE( QPen_MoveTo
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
338 QPointF point
= Slot::toPoint( a
);
339 path
.moveTo( point
);
343 QC_QPEN_PRIMITIVE( QPen_LineTo
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
345 QPointF point
= Slot::toPoint( a
);
346 path
.lineTo( point
);
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
);
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
);
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 );
372 if( slotFloatVal( arg
+2, &radius
) ) return errWrongType
;
373 radius
= qMax( 0.f
, radius
);
375 vec2
a( path
.currentPosition() );
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 );
410 QC_QPEN_PRIMITIVE( QPen_AddRect
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
412 QRectF rect
= Slot::toRect( a
);
413 path
.addRect( rect
);
417 QC_QPEN_PRIMITIVE( QPen_AddRoundedRect
, 3, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
419 float radiusX
, radiusY
;
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
);
430 QC_QPEN_PRIMITIVE( QPen_AddEllipse
, 1, PyrSlot
*r
, PyrSlot
*a
, VMGlobals
*g
)
432 QRectF rect
= Slot::toRect( a
);
433 path
.addEllipse( rect
);
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
) );
450 rect
.setSize( QSizeF( 2*radius
, 2*radius
) );
451 rect
.moveCenter( center
);
453 path
.moveTo( start
);
454 path
.arcTo( rect
, radToDeg( startAngle
), radToDeg( arcAngle
) );
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
);
468 rect
.setSize( QSizeF( 2*radius
, 2*radius
) );
469 rect
.moveCenter( center
);
470 path
.arcTo( rect
, startAngle
, sweepLength
);
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
) );
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
);
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();
511 painter
->setPen( Qt::NoPen
); break;
513 painter
->setBrush( Qt::NoBrush
); break;
519 painter
->drawPath( path
);
521 path
= QPainterPath();
523 painter
->setPen( pen
);
524 painter
->setBrush( brush
);
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();
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();
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 );
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
);
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 );
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
);
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
);