2 * Simple Framebuffer Gfx/GUI lib
4 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
5 * Understanding is not required. Only obedience.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 /* *****************************************************************************
20 Anti-Grain Geometry (AGG) - Version 2.5
21 A high quality rendering engine for C++
22 Copyright (C) 2002-2006 Maxim Shemanarev
23 Contact: mcseem@antigrain.com
27 AGG is free software; you can redistribute it and/or
28 modify it under the terms of the GNU General Public License
29 as published by the Free Software Foundation; either version 2
30 of the License, or (at your option) any later version.
32 AGG is distributed in the hope that it will be useful,
33 but WITHOUT ANY WARRANTY; without even the implied warranty of
34 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35 GNU General Public License for more details.
37 You should have received a copy of the GNU General Public License
38 along with AGG; if not, write to the Free Software
39 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
41 ***************************************************************************** */
42 module iv
.egra
.gfx
.aggmini
.stroker
;
43 package(iv
.egra
.gfx
.aggmini
):
44 nothrow @trusted @nogc:
46 private import iv
.egra
.gfx
.aggmini
: DisableCopyingMixin
;
47 private import iv
.egra
.gfx
.aggmini
.supmath
;
48 private import iv
.egra
.gfx
.aggmini
.enums
;
51 // ////////////////////////////////////////////////////////////////////////// //
54 * alias value_type = VertexType; // shoud support `VertexType(x, y)`
55 * vc.remove_all(); -- start new polygon
56 * vc.add(VT); -- add point to the current polygon
58 struct StrokeCalc(VertexConsumer
) {
61 float mWidthAbs
= 0.5f;
62 float mWidthEps
= 0.5f/1024.0f;
64 float mMiterLimit
= 4.0f;
65 float mInnerMiterLimit
= 1.01f;
66 float mApproxScale
= 1.0f;
67 LineCap mLineCap
= LineCap
.Butt
;
68 LineJoin mLineJoin
= LineJoin
.Miter
;
69 InnerJoin mInnerJoin
= InnerJoin
.Miter
;
71 public nothrow @trusted @nogc:
72 alias CoordType
= VertexConsumer
.ValueType
;
74 mixin(DisableCopyingMixin
);
76 @property void lineCap (in LineCap lc
) { pragma(inline
, true); mLineCap
= lc
; }
77 @property void lineJoin (in LineJoin lj
) { pragma(inline
, true); mLineJoin
= lj
; }
78 @property void innerJoin (in InnerJoin ij
) { pragma(inline
, true); mInnerJoin
= ij
; }
80 @property LineCap
lineCap () const pure { pragma(inline
, true); return mLineCap
; }
81 @property LineJoin
lineJoin () const pure { pragma(inline
, true); return mLineJoin
; }
82 @property InnerJoin
innerJoin () const pure { pragma(inline
, true); return mInnerJoin
; }
84 @property float width () const pure { pragma(inline
, true); return mWidth
*2.0f; }
85 @property void width (in float w
) {
94 mWidthEps
= mWidth
/1024.0f;
97 @property void miterLimit (in float ml
) { pragma(inline
, true); mMiterLimit
= ml
; }
98 @property void miterLimitTheta (in float t
) { pragma(inline
, true); import core
.stdc
.math
: sinf
; mMiterLimit
= 1.0f/sinf(t
*0.5f); }
99 @property void innerMiterLimit (in float ml
) { pragma(inline
, true); mInnerMiterLimit
= ml
; }
100 @property void approximationScale (in float as
) { pragma(inline
, true); mApproxScale
= as
; }
102 @property float miterLimit () const pure { pragma(inline
, true); return mMiterLimit
; }
103 @property float innerMiterLimit () const pure { pragma(inline
, true); return mInnerMiterLimit
; }
104 @property float approximationScale () const pure { pragma(inline
, true); return mApproxScale
; }
106 void calcCap() (ref VertexConsumer vc
, in auto ref VertexDist v0
, in auto ref VertexDist v1
, in float len
) {
107 import core
.stdc
.math
: acosf
, atan2f
, cosf
, sinf
;
111 float dx1
= (v1
.y
-v0
.y
)/len
;
112 float dy1
= (v1
.x
-v0
.x
)/len
;
119 if (mLineCap
!= LineCap
.Round
) {
120 if (mLineCap
== LineCap
.Square
) {
121 dx2
= dy1
*mWidthSign
;
122 dy2
= dx1
*mWidthSign
;
124 addVertex(vc
, v0
.x
-dx1
-dx2
, v0
.y
+dy1
-dy2
);
125 addVertex(vc
, v0
.x
+dx1
-dx2
, v0
.y
-dy1
-dy2
);
127 float da = acosf(mWidthAbs
/(mWidthAbs
+0.125f/mApproxScale
))*2.0f;
128 immutable int n
= cast(int)(FLT_PI
/da);
130 addVertex(vc
, v0
.x
-dx1
, v0
.y
+dy1
);
131 if (mWidthSign
> 0.0f) {
132 float a1
= atan2f(dy1
, -dx1
);
134 foreach (immutable int i
; 0..n
) {
135 addVertex(vc
, v0
.x
+cosf(a1
)*mWidth
, v0
.y
+sinf(a1
)*mWidth
);
139 float a1
= atan2f(-dy1
, dx1
);
141 foreach (immutable int i
; 0..n
) {
142 addVertex(vc
, v0
.x
+cosf(a1
)*mWidth
, v0
.y
+sinf(a1
)*mWidth
);
146 addVertex(vc
, v0
.x
+dx1
, v0
.y
-dy1
);
150 void calcJoin() (ref VertexConsumer vc
, in auto ref VertexDist v0
, in auto ref VertexDist v1
, in auto ref VertexDist v2
, in float len1
, in float len2
) {
151 import core
.stdc
.math
: sqrtf
;
153 immutable float dx1
= mWidth
*(v1
.y
-v0
.y
)/len1
;
154 immutable float dy1
= mWidth
*(v1
.x
-v0
.x
)/len1
;
155 immutable float dx2
= mWidth
*(v2
.y
-v1
.y
)/len2
;
156 immutable float dy2
= mWidth
*(v2
.x
-v1
.x
)/len2
;
160 float cp
= cross3(v0
.x
, v0
.y
, v1
.x
, v1
.y
, v2
.x
, v2
.y
);
161 if (cp
!= 0.0f && (cp
> 0.0f) == (mWidth
> 0.0f)) {
163 float limit
= (len1
< len2 ? len1
: len2
)/mWidthAbs
;
164 if (limit
< mInnerMiterLimit
) limit
= mInnerMiterLimit
;
166 switch (mInnerJoin
) {
167 default: // inner_bevel
168 addVertex(vc
, v1
.x
+dx1
, v1
.y
-dy1
);
169 addVertex(vc
, v1
.x
+dx2
, v1
.y
-dy2
);
171 case InnerJoin
.Miter
:
172 calcMiter(vc
, v0
, v1
, v2
, dx1
, dy1
, dx2
, dy2
, LineJoin
.MiterRevert
, limit
, 0.0f);
175 case InnerJoin
.Round
:
176 cp
= (dx1
-dx2
)*(dx1
-dx2
)+(dy1
-dy2
)*(dy1
-dy2
);
177 if (cp
< len1
*len1
&& cp
< len2
*len2
) {
178 calcMiter(vc
, v0
, v1
, v2
, dx1
, dy1
, dx2
, dy2
, LineJoin
.MiterRevert
, limit
, 0.0f);
180 if (mInnerJoin
== InnerJoin
.Jag
) {
181 addVertex(vc
, v1
.x
+dx1
, v1
.y
-dy1
);
182 addVertex(vc
, v1
.x
, v1
.y
);
183 addVertex(vc
, v1
.x
+dx2
, v1
.y
-dy2
);
185 addVertex(vc
, v1
.x
+dx1
, v1
.y
-dy1
);
186 addVertex(vc
, v1
.x
, v1
.y
);
187 calcArc(vc
, v1
.x
, v1
.y
, dx2
, -dy2
, dx1
, -dy1
);
188 addVertex(vc
, v1
.x
, v1
.y
);
189 addVertex(vc
, v1
.x
+dx2
, v1
.y
-dy2
);
197 // Calculate the distance between v1 and
198 // the central point of the bevel line segment
199 float dx
= (dx1
+dx2
)*0.5f;
200 float dy
= (dy1
+dy2
)*0.5f;
201 immutable float dbevel
= sqrtf(dx
*dx
+dy
*dy
);
203 if (mLineJoin
== LineJoin
.Round || mLineJoin
== LineJoin
.Bevel
) {
204 // This is an optimization that reduces the number of points
205 // in cases of almost collinear segments. If there's no
206 // visible difference between bevel and miter joins we'd rather
207 // use miter join because it adds only one point instead of two.
209 // Here we calculate the middle point between the bevel points
210 // and then, the distance between v1 and this middle point.
211 // At outer joins this distance always less than stroke width,
212 // because it's actually the height of an isosceles triangle of
213 // v1 and its two bevel points. If the difference between this
214 // width and this value is small (no visible bevel) we can
215 // add just one point.
217 // The constant in the expression makes the result approximately
218 // the same as in round joins and caps. You can safely comment
219 // out this entire "if".
220 if (mApproxScale
*(mWidthAbs
-dbevel
) < mWidthEps
) {
221 if (intersection(v0
.x
+dx1
, v0
.y
-dy1
, v1
.x
+dx1
, v1
.y
-dy1
, v1
.x
+dx2
, v1
.y
-dy2
, v2
.x
+dx2
, v2
.y
-dy2
, &dx
, &dy
)) {
222 addVertex(vc
, dx
, dy
);
224 addVertex(vc
, v1
.x
+dx1
, v1
.y
-dy1
);
232 case LineJoin
.MiterRevert
:
233 case LineJoin
.MiterRound
:
234 calcMiter(vc
, v0
, v1
, v2
, dx1
, dy1
, dx2
, dy2
, mLineJoin
, mMiterLimit
, dbevel
);
237 calcArc(vc
, v1
.x
, v1
.y
, dx1
, -dy1
, dx2
, -dy2
);
239 default: // Bevel join
240 addVertex(vc
, v1
.x
+dx1
, v1
.y
-dy1
);
241 addVertex(vc
, v1
.x
+dx2
, v1
.y
-dy2
);
248 void addVertex (ref VertexConsumer vc
, in float x
, in float y
) {
249 pragma(inline
, true);
250 vc
.add(CoordType(x
, y
));
253 void calcArc (ref VertexConsumer vc
, in float x
, in float y
, in float dx1
, in float dy1
, in float dx2
, in float dy2
) {
254 import core
.stdc
.math
: acosf
, atan2f
, cosf
, sinf
;
256 float a1
= atan2f(dy1
*mWidthSign
, dx1
*mWidthSign
);
257 float a2
= atan2f(dy2
*mWidthSign
, dx2
*mWidthSign
);
259 immutable float da = acosf(mWidthAbs
/(mWidthAbs
+0.125f/mApproxScale
))*2.0f;
261 addVertex(vc
, x
+dx1
, y
+dy1
);
262 if (mWidthSign
> 0.0f) {
263 if (a1
> a2
) a2
+= 2.0f*FLT_PI
;
264 immutable int n
= cast(int)((a2
-a1
)/da);
265 immutable float daa = (a2
-a1
)/(n
+1);
267 foreach (immutable int i
; 0..n
) {
268 addVertex(vc
, x
+cosf(a1
)*mWidth
, y
+sinf(a1
)*mWidth
);
272 if (a1
< a2
) a2
-= 2.0f*FLT_PI
;
273 immutable int n
= cast(int)((a1
-a2
)/da);
274 immutable float daa = (a1
-a2
)/(n
+1);
276 foreach (immutable int i
; 0..n
) {
277 addVertex(vc
, x
+cosf(a1
)*mWidth
, y
+sinf(a1
)*mWidth
);
281 addVertex(vc
, x
+dx2
, y
+dy2
);
284 void calcMiter (ref VertexConsumer vc
, in ref VertexDist v0
, in ref VertexDist v1
, in ref VertexDist v2
,
285 in float dx1
, in float dy1
, in float dx2
, in float dy2
, in LineJoin lj
,
286 in float mlimit
, in float dbevel
)
291 immutable float lim
= mWidthAbs
*mlimit
;
292 bool miterLimitExceeded
= true; // assume the worst
293 bool intersectionFailed
= true; // assume the worst
295 if (intersection(v0
.x
+dx1
, v0
.y
-dy1
, v1
.x
+dx1
, v1
.y
-dy1
, v1
.x
+dx2
, v1
.y
-dy2
, v2
.x
+dx2
, v2
.y
-dy2
, &xi
, &yi
)) {
296 // Calculation of the intersection succeeded
297 di = distance(v1
.x
, v1
.y
, xi
, yi
);
299 // Inside the miter limit
300 addVertex(vc
, xi
, yi
);
301 miterLimitExceeded
= false;
303 intersectionFailed
= false;
305 // Calculation of the intersection failed, most probably
306 // the three points lie one straight line.
307 // First check if v0 and v2 lie on the opposite sides of vector:
308 // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
309 // to the line determined by vertices v0 and v1.
310 // This condition determines whether the next line segments continues
311 // the previous one or goes back.
312 immutable float x2
= v1
.x
+dx1
;
313 immutable float y2
= v1
.y
-dy1
;
314 if ((cross3(v0
.x
, v0
.y
, v1
.x
, v1
.y
, x2
, y2
) < 0.0f) == (cross3(v1
.x
, v1
.y
, v2
.x
, v2
.y
, x2
, y2
) < 0.0f)) {
315 // This case means that the next segment continues
316 // the previous one (straight line)
317 addVertex(vc
, v1
.x
+dx1
, v1
.y
-dy1
);
318 miterLimitExceeded
= false;
322 if (miterLimitExceeded
) {
323 // Miter limit exceeded
324 //------------------------
326 case LineJoin
.MiterRevert
:
327 // For the compatibility with SVG, PDF, etc,
328 // we use a simple bevel join instead of
330 addVertex(vc
, v1
.x
+dx1
, v1
.y
-dy1
);
331 addVertex(vc
, v1
.x
+dx2
, v1
.y
-dy2
);
333 case LineJoin
.MiterRound
:
334 calcArc(vc
, v1
.x
, v1
.y
, dx1
, -dy1
, dx2
, -dy2
);
337 // If no miter-revert, calculate new dx1, dy1, dx2, dy2
338 if (intersectionFailed
) {
339 immutable float mlimitM
= mlimit
*mWidthSign
;
340 addVertex(vc
, v1
.x
+dx1
+dy1
*mlimitM
, v1
.y
-dy1
+dx1
*mlimitM
);
341 addVertex(vc
, v1
.x
+dx2
-dy2
*mlimitM
, v1
.y
-dy2
-dx2
*mlimitM
);
343 immutable float x1
= v1
.x
+dx1
;
344 immutable float y1
= v1
.y
-dy1
;
345 immutable float x2
= v1
.x
+dx2
;
346 immutable float y2
= v1
.y
-dy2
;
347 di = (lim
-dbevel
)/(di-dbevel
);
348 addVertex(vc
, x1
+(xi
-x1
)*di, y1
+(yi
-y1
)*di);
349 addVertex(vc
, x2
+(xi
-x2
)*di, y2
+(yi
-y2
)*di);
358 // ////////////////////////////////////////////////////////////////////////// //
360 public nothrow @trusted @nogc:
361 alias VertexStorage
= VertexSequence
!VertexDist
;
362 alias CoordStorage
= SimpleVector
!AGGPoint
;
380 StrokeCalc
!CoordStorage mStroker
;
381 VertexStorage mSrcVertices
;
382 CoordStorage mOutVertices
;
385 State mStatus
= State
.Initial
;
391 mixin(DisableCopyingMixin
);
393 @property void lineCap (LineCap lc
) { pragma(inline
, true); mStroker
.lineCap(lc
); }
394 @property void lineJoin (LineJoin lj
) { pragma(inline
, true); mStroker
.lineJoin(lj
); }
395 @property void innerJoin (InnerJoin ij
) { pragma(inline
, true); mStroker
.innerJoin(ij
); }
397 @property LineCap
lineCap () const pure { pragma(inline
, true); return mStroker
.lineCap(); }
398 @property LineJoin
lineJoin () const pure { pragma(inline
, true); return mStroker
.lineJoin(); }
399 @property InnerJoin
innerJoin () const pure { pragma(inline
, true); return mStroker
.innerJoin(); }
401 @property void width (float w
) { pragma(inline
, true); mStroker
.width(w
); }
402 @property void miterLimit (float ml
) { pragma(inline
, true); mStroker
.miterLimit(ml
); }
403 @property void miterLimitTheta (float t
) { pragma(inline
, true); mStroker
.miterLimitTheta(t
); }
404 @property void innerMiterLimit (float ml
) { pragma(inline
, true); mStroker
.innerMiterLimit(ml
); }
405 @property void approximationScale (float as
) { pragma(inline
, true); mStroker
.approximationScale(as
); }
407 @property float width () const pure { pragma(inline
, true); return mStroker
.width(); }
408 @property float miterLimit () const pure { pragma(inline
, true); return mStroker
.miterLimit(); }
409 @property float innerMiterLimit () const pure { pragma(inline
, true); return mStroker
.innerMiterLimit(); }
410 @property float approximationScale () const pure { pragma(inline
, true); return mStroker
.approximationScale(); }
412 @property void shorten (float s
) { pragma(inline
, true); mShorten
= s
; }
413 @property float shorten () const pure { pragma(inline
, true); return mShorten
; }
415 // Generator interface
417 mSrcVertices
.removeAll();
419 mOutVertices
.removeAll();
422 mStatus
= mPrevStatus
= State
.Initial
;
425 void addVertex (float x
, float y
, uint cmd
) {
426 mStatus
= State
.Initial
;
427 if (isMoveTo(cmd
)) mSrcVertices
.modifyLast(VertexDist(x
, y
));
428 else if (isVertex(cmd
)) mSrcVertices
.add(VertexDist(x
, y
));
429 else mClosed
= getCloseFlag(cmd
);
432 // Vertex Source Interface
434 if (mStatus
== State
.Initial
) {
435 mSrcVertices
.close(mClosed
!= 0);
436 shortenPath(mSrcVertices
, mShorten
, mClosed
);
437 if (mSrcVertices
.length
< 3) mClosed
= 0;
439 mStatus
= State
.Ready
;
444 uint vertex (float* x
, float* y
) {
445 uint cmd
= PathCommand
.LineTo
;
446 while (!isStop(cmd
)) {
447 final switch (mStatus
) {
453 if (mSrcVertices
.length
< 2+cast(uint)(mClosed
!= 0)) {
454 cmd
= PathCommand
.Stop
;
457 mStatus
= (mClosed ? State
.Outline1
: State
.Cap1
);
458 cmd
= PathCommand
.MoveTo
;
464 mStroker
.calcCap(mOutVertices
, mSrcVertices
[0], mSrcVertices
[1], mSrcVertices
[0].dist
);
466 mPrevStatus
= State
.Outline1
;
467 mStatus
= State
.OutVertices
;
472 mStroker
.calcCap(mOutVertices
, mSrcVertices
[mSrcVertices
.length
-1], mSrcVertices
[mSrcVertices
.length
-2], mSrcVertices
[mSrcVertices
.length
-2].dist
);
473 mPrevStatus
= State
.Outline2
;
474 mStatus
= State
.OutVertices
;
480 if (mSrcVertex
>= mSrcVertices
.length
) {
481 mPrevStatus
= State
.CloseFirst
;
482 mStatus
= State
.End_poly1
;
486 if (mSrcVertex
>= mSrcVertices
.length
-1) {
487 mStatus
= State
.Cap2
;
491 mStroker
.calcJoin(mOutVertices
, mSrcVertices
.prev(mSrcVertex
), mSrcVertices
.curr(mSrcVertex
), mSrcVertices
.next(mSrcVertex
), mSrcVertices
.prev(mSrcVertex
).dist
, mSrcVertices
.curr(mSrcVertex
).dist
);
493 mPrevStatus
= mStatus
;
494 mStatus
= State
.OutVertices
;
498 case State
.CloseFirst
:
499 mStatus
= State
.Outline2
;
500 cmd
= PathCommand
.MoveTo
;
504 if (mSrcVertex
<= uint(mClosed
== 0)) {
505 mStatus
= State
.End_poly2
;
506 mPrevStatus
= State
.Stop
;
511 mStroker
.calcJoin(mOutVertices
, mSrcVertices
.next(mSrcVertex
), mSrcVertices
.curr(mSrcVertex
), mSrcVertices
.prev(mSrcVertex
), mSrcVertices
.curr(mSrcVertex
).dist
, mSrcVertices
.prev(mSrcVertex
).dist
);
513 mPrevStatus
= mStatus
;
514 mStatus
= State
.OutVertices
;
518 case State
.OutVertices
:
519 if (mOutVertex
>= mOutVertices
.length
) {
520 mStatus
= mPrevStatus
;
522 const(AGGPoint
)* c
= &mOutVertices
[mOutVertex
++];
529 case State
.End_poly1
:
530 mStatus
= mPrevStatus
;
531 return PathCommand
.EndPoly|PathFlag
.Close|PathFlag
.CCW
;
533 case State
.End_poly2
:
534 mStatus
= mPrevStatus
;
535 return PathCommand
.EndPoly|PathFlag
.Close|PathFlag
.CW
;
538 cmd
= PathCommand
.Stop
;
547 // ////////////////////////////////////////////////////////////////////////// //
549 public nothrow @trusted @nogc:
550 alias VertexStorage
= VertexSequence
!VertexDist
;
551 alias CoordStorage
= SimpleVector
!AGGPoint
;
563 StrokeCalc
!CoordStorage mStroker
;
565 VertexStorage mSrcVertices
;
566 CoordStorage mOutVertices
;
571 uint mOrientation
= 0;
572 bool mAutoDetect
= false;
575 mixin(DisableCopyingMixin
);
577 @property void lineCap (LineCap lc
) { pragma(inline
, true); mStroker
.lineCap(lc
); }
578 @property void lineJoin (LineJoin lj
) { pragma(inline
, true); mStroker
.lineJoin(lj
); }
579 @property void innerJoin (InnerJoin ij
) { pragma(inline
, true); mStroker
.innerJoin(ij
); }
581 @property LineCap
lineCap () const pure { pragma(inline
, true); return mStroker
.lineCap(); }
582 @property LineJoin
lineJoin () const pure { pragma(inline
, true); return mStroker
.lineJoin(); }
583 @property InnerJoin
innerJoin () const pure { pragma(inline
, true); return mStroker
.innerJoin(); }
585 @property void width (float w
) { pragma(inline
, true); mStroker
.width(mWidth
= w
); }
586 @property void miterLimit (float ml
) { pragma(inline
, true); mStroker
.miterLimit(ml
); }
587 @property void miterLimitTheta (float t
) { pragma(inline
, true); mStroker
.miterLimitTheta(t
); }
588 @property void innerMiterLimit (float ml
) { pragma(inline
, true); mStroker
.innerMiterLimit(ml
); }
589 @property void approximationScale (float as
) { pragma(inline
, true); mStroker
.approximationScale(as
); }
591 @property float width () const pure { pragma(inline
, true); return mWidth
; }
592 @property float miterLimit () const pure { pragma(inline
, true); return mStroker
.miterLimit(); }
593 @property float innerMiterLimit () const pure { pragma(inline
, true); return mStroker
.innerMiterLimit(); }
594 @property float approximationScale () const pure { pragma(inline
, true); return mStroker
.approximationScale(); }
596 @property void autoDetectOrientation (bool v
) { pragma(inline
, true); mAutoDetect
= v
; }
597 @property bool autoDetectOrientation () const pure { pragma(inline
, true); return mAutoDetect
; }
599 // Generator interface
601 mSrcVertices
.removeAll();
603 mOutVertices
.removeAll();
607 mStatus
= State
.Initial
;
610 void addVertex (float x
, float y
, uint cmd
) {
611 mStatus
= State
.Initial
;
613 mSrcVertices
.modifyLast(VertexDist(x
, y
));
614 } else if (isVertex(cmd
)) {
615 mSrcVertices
.add(VertexDist(x
, y
));
616 } else if (isEndPoly(cmd
)) {
617 mClosed
= getCloseFlag(cmd
);
618 if (mOrientation
== PathFlag
.None
) mOrientation
= getOrientation(cmd
);
622 // Vertex Source Interface
624 if (mStatus
== State
.Initial
) {
625 mSrcVertices
.close(true);
627 if (!isOriented(mOrientation
)) {
628 mOrientation
= (calcPolygonArea(mSrcVertices
) > 0 ? PathFlag
.CCW
: PathFlag
.CW
);
631 if (isOriented(mOrientation
)) {
632 mStroker
.width(isCCW(mOrientation
) ? mWidth
: -mWidth
);
635 mStatus
= State
.Ready
;
639 uint vertex (float* x
, float* y
) {
640 uint cmd
= PathCommand
.LineTo
;
641 while (!isStop(cmd
)) {
642 final switch (mStatus
) {
648 if (mSrcVertices
.length
< 2+cast(uint)(mClosed
!= 0)) {
649 cmd
= PathCommand
.Stop
;
652 mStatus
= State
.Outline
;
653 cmd
= PathCommand
.MoveTo
;
659 if (mSrcVertex
>= mSrcVertices
.length
) {
660 mStatus
= State
.EndPoly
;
663 mStroker
.calcJoin(mOutVertices
, mSrcVertices
.prev(mSrcVertex
), mSrcVertices
.curr(mSrcVertex
), mSrcVertices
.next(mSrcVertex
), mSrcVertices
.prev(mSrcVertex
).dist
, mSrcVertices
.curr(mSrcVertex
).dist
);
665 mStatus
= State
.OutVertices
;
669 case State
.OutVertices
:
670 if (mOutVertex
>= mOutVertices
.length
) {
671 mStatus
= State
.Outline
;
673 const(AGGPoint
)* c
= &mOutVertices
[mOutVertex
++];
681 if (!mClosed
) return PathCommand
.Stop
;
682 mStatus
= State
.Stop
;
683 return PathCommand
.EndPoly|PathFlag
.Close|PathFlag
.CCW
;
686 return PathCommand
.Stop
;
694 // ////////////////////////////////////////////////////////////////////////// //
696 private nothrow @trusted @nogc:
697 alias VertexStorage
= VertexSequence
!VertexDist
;
708 float[MaxDashes
] mDashes
= 0;
709 float mTotalDashLen
= 0;
711 float mDashStart
= 0;
713 float mCurrDashStart
= 0;
716 const(VertexDist
)* m_v1
= null;
717 const(VertexDist
)* m_v2
= null;
719 VertexStorage mSrcVertices
;
721 State mStatus
= State
.Initial
;
725 mixin(DisableCopyingMixin
);
727 @property void shorten (in float s
) { pragma(inline
, true); mShorten
= s
; }
728 @property float shorten () const pure { pragma(inline
, true); return mShorten
; }
730 @property bool hasDashes () const pure { pragma(inline
, true); return (mNumDashes
> 0); }
732 void removeAllDashes () {
739 void addDash (in float dashLen
, in float gapLen
) {
740 if (mNumDashes
< MaxDashes
) {
741 mTotalDashLen
+= dashLen
+gapLen
;
742 mDashes
[mNumDashes
++] = dashLen
;
743 mDashes
[mNumDashes
++] = gapLen
;
747 void dashStart (in float ds) {
748 import core
.stdc
.math
: fabsf
;
750 calcDashStart(fabsf(ds));
753 // Vertex Generator Interface
755 mStatus
= State
.Initial
;
756 mSrcVertices
.removeAll();
759 calcDashStart(mDashStart
);
763 void addVertex (float x
, float y
, uint cmd
) {
764 mStatus
= State
.Initial
;
765 if (isMoveTo(cmd
)) mSrcVertices
.modifyLast(VertexDist(x
, y
));
766 else if (isVertex(cmd
)) mSrcVertices
.add(VertexDist(x
, y
));
767 else mClosed
= getCloseFlag(cmd
);
770 // Vertex Source Interface
772 if (mStatus
== State
.Initial
) {
773 mSrcVertices
.close(mClosed
!= 0);
774 shortenPath(mSrcVertices
, mShorten
, mClosed
);
776 mStatus
= State
.Ready
;
780 uint vertex (float* x
, float* y
) {
781 uint cmd
= PathCommand
.MoveTo
;
783 while (!isStop(cmd
)) {
784 final switch (mStatus
) {
790 if (mNumDashes
< 2 || mSrcVertices
.length
< 2) {
791 cmd
= PathCommand
.Stop
;
794 mStatus
= State
.Polyline
;
796 m_v1
= &mSrcVertices
[0];
797 m_v2
= &mSrcVertices
[1];
798 mCurrRest
= m_v1
.dist
;
801 if (mDashStart
>= 0) calcDashStart(mDashStart
);
802 return PathCommand
.MoveTo
;
805 immutable float dashRest
= mDashes
[mCurrDash
]-mCurrDashStart
;
806 cmd
= (mCurrDash
&1 ? PathCommand
.MoveTo
: PathCommand
.LineTo
);
807 if (mCurrRest
> dashRest
) {
808 mCurrRest
-= dashRest
;
810 if (mCurrDash
>= mNumDashes
) mCurrDash
= 0;
812 *x
= m_v2
.x
-(m_v2
.x
-m_v1
.x
)*mCurrRest
/m_v1
.dist
;
813 *y
= m_v2
.y
-(m_v2
.y
-m_v1
.y
)*mCurrRest
/m_v1
.dist
;
815 mCurrDashStart
+= mCurrRest
;
820 mCurrRest
= m_v1
.dist
;
822 if (mSrcVertex
> mSrcVertices
.length
) {
823 mStatus
= State
.Stop
;
825 m_v2
= &mSrcVertices
[mSrcVertex
>= mSrcVertices
.length ?
0 :mSrcVertex
];
828 if (mSrcVertex
>= mSrcVertices
.length
) {
829 mStatus
= State
.Stop
;
831 m_v2
= &mSrcVertices
[mSrcVertex
];
838 cmd
= PathCommand
.Stop
;
842 return PathCommand
.Stop
;
846 void calcDashStart (float ds) {
850 if (ds > mDashes
[mCurrDash
]) {
851 ds -= mDashes
[mCurrDash
];
854 if (mCurrDash
>= mNumDashes
) mCurrDash
= 0;