erga: agg cosmetix
[iv.d.git] / egra / gfx / aggmini.d
blob6db827424b05c8bbfdf0406566d5772e852839ca
1 /*
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 - Version 2.1 Lite
21 Copyright (C) 2002-2003 Maxim Shemanarev (McSeem)
22 D port, additional work: Ketmar Dark
24 Permission to copy, use, modify, sell and distribute this software
25 is granted provided this copyright notice appears in all copies.
26 This software is provided "as is" without express or implied
27 warranty, and with no claim as to its suitability for any purpose.
29 The author gratefully acknowleges the support of David Turner,
30 Robert Wilhelm, and Werner Lemberg - the authors of the FreeType
31 libray - in producing this work. See http://www.freetype.org for details.
33 Initially the rendering algorithm was designed by David Turner and the
34 other authors of the FreeType library - see the above notice. I nearly
35 created a similar renderer, but still I was far from David's work.
36 I completely redesigned the original code and adapted it for Anti-Grain
37 ideas. Two functions - renderLine and renderScanLine are the core of
38 the algorithm - they calculate the exact coverage of each pixel cell
39 of the polygon. I left these functions almost as is, because there's
40 no way to improve the perfection - hats off to David and his group!
42 All other code is very different from the original.
43 ***************************************************************************** */
44 module iv.egra.gfx.aggmini;
45 private:
46 nothrow @safe @nogc:
48 private enum DisableCopyingMixin = "@disable this (this); void opAssign() (in auto ref typeof(this)) { static assert(0, `assigning disabled`); }";
50 import iv.egra.gfx.base;
51 import iv.egra.gfx.lowlevel;
53 /* Bezier curve rasterizer.
55 * De Casteljau Bezier rasterizer is faster, but currently rasterizing curves with cusps sligtly wrong.
56 * It doesn't really matter in practice.
58 * AFD tesselator is somewhat slower, but does cusps better.
60 * McSeem rasterizer should have the best quality, bit it is the slowest method. Basically, you will
61 * never notice any visial difference (and this code is not really debugged), so you probably should
62 * not use it. It is there for further experiments.
64 public enum AGGTesselation {
65 DeCasteljau, /// default: standard well-known tesselation algorithm
66 AFD, /// adaptive forward differencing
67 DeCasteljauMcSeem, /// standard well-known tesselation algorithm, with improvements from Maxim Shemanarev; slowest one, but should give best results
71 // ////////////////////////////////////////////////////////////////////////// //
72 // a pixel cell
73 align(1) struct Cell {
74 align(1):
75 public:
76 static uint packCoord (in int x, in int y) pure nothrow @safe @nogc { pragma(inline, true); return (y<<16)|(x&0xffff); }
78 public:
79 uint packedCoord;
80 int cover;
81 int area;
83 public nothrow @trusted @nogc:
84 this (in int cx, in int cy, in int c, in int a) pure { pragma(inline, true); packedCoord = packCoord(cx, cy); cover = c; area = a; }
86 @property int x () const pure { pragma(inline, true); return cast(short)(packedCoord&0xffff); }
87 @property int y () const pure { pragma(inline, true); return cast(short)(packedCoord>>16); }
89 void setCover (in int c, in int a) { pragma(inline, true); cover = c; area = a; }
90 void addCover (in int c, in int a) { pragma(inline, true); cover += c; area += a; }
92 void setCoord (in int cx, in int cy) { pragma(inline, true); packedCoord = packCoord(cx, cy); }
94 void set (in int cx, in int cy, in int c, in int a) { pragma(inline, true); packedCoord = packCoord(cx, cy); cover = c; area = a; }
98 // ////////////////////////////////////////////////////////////////////////// //
99 /* An internal class that implements the main rasterization algorithm.
100 Used in the rasterizer. Should not be used direcly.
102 struct Outline {
103 private nothrow @trusted @nogc:
104 enum : uint {
105 CellBlockShift = 12U,
106 CellBlockSize = cast(uint)(1<<CellBlockShift),
107 CellBlockMask = cast(uint)(CellBlockSize-1),
108 CellBlockPool = 256U,
109 CellBlockLimit = 1024U,
112 enum QSortThreshold = 9;
114 enum : uint {
115 NotClosed = 1U,
116 SortRequired = 2U,
119 private:
120 uint mNumBlocks;
121 uint mMaxBlocks;
122 uint mCurBlock;
123 uint mNumCells;
124 Cell** mCells;
125 Cell* mCurCellPtr;
126 Cell** mSortedCells;
127 uint mSortedSize;
128 Cell mCurCell = Cell(0x7fff, 0x7fff, 0, 0);
129 int mCurX;
130 int mCurY;
131 int mCloseX;
132 int mCloseY;
133 int mMinX = 0x7fffffff;
134 int mMinY = 0x7fffffff;
135 int mMaxX = -0x7fffffff;
136 int mMaxY = -0x7fffffff;
137 uint mFlags = SortRequired;
139 private:
140 void allocateBlock () {
141 import core.stdc.stdlib : realloc;
142 if (mCurBlock >= mNumBlocks) {
143 import core.stdc.string : memset;
144 if (mNumBlocks >= mMaxBlocks) {
145 Cell** newCells = cast(Cell**)realloc(mCells, (mMaxBlocks+CellBlockPool)*(Cell*).sizeof);
146 if (newCells is null) assert(0, "out of memory");
147 mCells = newCells;
148 mMaxBlocks += CellBlockPool;
150 auto cc = cast(Cell*)realloc(null, Cell.sizeof*CellBlockSize);
151 if (cc is null) assert(0, "out of memory");
152 memset(cc, 0, Cell.sizeof*CellBlockSize);
153 //foreach (ref c; cc[0..CellBlockSize]) c = Cell.init;
154 mCells[mNumBlocks++] = cc;
156 mCurCellPtr = mCells[mCurBlock++];
159 public:
160 mixin(DisableCopyingMixin);
162 ~this () {
163 import core.stdc.stdlib : free;
164 free(mSortedCells);
165 if (mNumBlocks) {
166 Cell** ptr = mCells+mNumBlocks-1;
167 while (mNumBlocks--) {
168 free(*ptr);
169 --ptr;
171 free(mCells);
175 void reset () {
176 mNumCells = 0;
177 mCurBlock = 0;
178 mCurCell.set(0x7fff, 0x7fff, 0, 0);
179 mFlags |= SortRequired;
180 mFlags &= ~NotClosed;
181 mMinX = 0x7fffffff;
182 mMinY = 0x7fffffff;
183 mMaxX = -0x7fffffff;
184 mMaxY = -0x7fffffff;
187 void moveTo (in int x, in int y) {
188 if ((mFlags&SortRequired) == 0) reset();
189 if (mFlags&NotClosed) lineTo(mCloseX, mCloseY);
190 setCurCell(x>>PolyBaseShift, y>>PolyBaseShift);
191 mCloseX = mCurX = x;
192 mCloseY = mCurY = y;
195 void lineTo (in int x, in int y) {
196 if ((mFlags&SortRequired) && ((mCurX^x)|(mCurY^y))) {
197 int c = mCurX>>PolyBaseShift;
198 if (c < mMinX) mMinX = c;
199 ++c;
200 if (c > mMaxX) mMaxX = c;
202 c = x>>PolyBaseShift;
203 if (c < mMinX) mMinX = c;
204 ++c;
205 if (c > mMaxX) mMaxX = c;
207 renderLine(mCurX, mCurY, x, y);
208 mCurX = x;
209 mCurY = y;
210 mFlags |= NotClosed;
214 @property float currX () const pure { pragma(inline, true); return cast(float)mCurX/cast(float)PolyBaseSize; }
215 @property float currY () const pure { pragma(inline, true); return cast(float)mCurY/cast(float)PolyBaseSize; }
217 @property int minX () const pure { pragma(inline, true); return mMinX; }
218 @property int minY () const pure { pragma(inline, true); return mMinY; }
219 @property int maxX () const pure { pragma(inline, true); return mMaxX; }
220 @property int maxY () const pure { pragma(inline, true); return mMaxY; }
222 @property uint numCells () const pure { pragma(inline, true); return mNumCells; }
224 const(Cell)** cells () {
225 if (mFlags&NotClosed) {
226 lineTo(mCloseX, mCloseY);
227 mFlags &= ~NotClosed;
229 // perform sort only the first time
230 if (mFlags&SortRequired) {
231 addCurCell();
232 if (mNumCells == 0) return null;
233 sortCells();
234 mFlags &= ~SortRequired;
236 return cast(const(Cell)**)mSortedCells;
239 private:
240 void addCurCell () {
241 if (mCurCell.area|mCurCell.cover) {
242 if ((mNumCells&CellBlockMask) == 0) {
243 if (mNumBlocks >= CellBlockLimit) return;
244 allocateBlock();
246 *mCurCellPtr++ = mCurCell;
247 ++mNumCells;
251 void setCurCell (in int x, in int y) {
252 if (mCurCell.packedCoord != Cell.packCoord(x, y)) {
253 addCurCell();
254 mCurCell.set(x, y, 0, 0);
258 void renderScanLine (in int ey, in int x1, int y1, in int x2, in int y2) {
259 immutable int ex2 = x2>>PolyBaseShift;
261 // trivial case; happens often
262 if (y1 == y2) {
263 setCurCell(ex2, ey);
264 return;
267 int ex1 = x1>>PolyBaseShift;
268 immutable int fx1 = x1&PolyBaseMask;
269 immutable int fx2 = x2&PolyBaseMask;
271 // everything is located in a single cell: that is easy!
272 if (ex1 == ex2) {
273 immutable int delta = y2-y1;
274 mCurCell.addCover(delta, (fx1+fx2)*delta);
275 return;
278 // ok, we'll have to render a run of adjacent cells on the same scanline...
279 int p = (PolyBaseSize-fx1)*(y2-y1);
280 int first = PolyBaseSize;
281 int incr = 1;
283 int dx = x2-x1;
285 if (dx < 0) {
286 p = fx1*(y2-y1);
287 first = 0;
288 incr = -1;
289 dx = -dx;
292 int delta = p/dx;
293 int mod = p%dx;
294 if (mod < 0) { --delta; mod += dx; }
296 mCurCell.addCover(delta, (fx1+first)*delta);
298 ex1 += incr;
299 setCurCell(ex1, ey);
300 y1 += delta;
302 if (ex1 != ex2) {
303 p = PolyBaseSize*(y2-y1+delta);
304 int lift = p/dx;
305 int rem = p%dx;
306 if (rem < 0) { --lift; rem += dx; }
307 mod -= dx;
308 while (ex1 != ex2) {
309 delta = lift;
310 mod += rem;
311 if (mod >= 0) { mod -= dx; ++delta; }
312 mCurCell.addCover(delta, PolyBaseSize*delta);
313 y1 += delta;
314 ex1 += incr;
315 setCurCell(ex1, ey);
319 delta = y2-y1;
320 mCurCell.addCover(delta, (fx2+PolyBaseSize-first)*delta);
323 void renderLine (in int x1, in int y1, in int x2, in int y2) {
324 int ey1 = y1>>PolyBaseShift;
325 immutable int ey2 = y2>>PolyBaseShift;
327 if (ey1 < mMinY) mMinY = ey1;
328 if (ey1+1 > mMaxY) mMaxY = ey1+1;
329 if (ey2 < mMinY) mMinY = ey2;
330 if (ey2+1 > mMaxY) mMaxY = ey2+1;
332 immutable int fy1 = y1&PolyBaseMask;
333 immutable int fy2 = y2&PolyBaseMask;
335 // everything is on a single scanline
336 if (ey1 == ey2) {
337 renderScanLine(ey1, x1, fy1, x2, fy2);
338 return;
341 int dx = x2-x1;
342 int dy = y2-y1;
344 //int xFrom, xTo;
345 //int p, rem, mod, lift, delta, first, incr;
347 // vertical line: we have to calculate start and end cells,
348 // and then the common values of the area and coverage for
349 // all cells of the line. we know exactly there's only one
350 // cell, so, we don't have to call renderScanLine().
351 int incr = 1;
352 if (dx == 0) {
353 int ex = x1>>PolyBaseShift;
354 int twoFx = (x1-(ex<<PolyBaseShift))<<1;
355 int area;
357 int first = PolyBaseSize;
358 if (dy < 0) { first = 0; incr = -1; }
360 immutable int xFrom = x1;
362 //renderScanLine(ey1, xFrom, fy1, xFrom, first);
363 int delta = first-fy1;
364 mCurCell.addCover(delta, twoFx*delta);
366 ey1 += incr;
367 setCurCell(ex, ey1);
369 delta = first+first-PolyBaseSize;
370 area = twoFx*delta;
371 while (ey1 != ey2) {
372 //renderScanLine(ey1, xFrom, PolyBaseSize - first, xFrom, first);
373 mCurCell.setCover(delta, area);
374 ey1 += incr;
375 setCurCell(ex, ey1);
377 //renderScanLine(ey1, xFrom, PolyBaseSize - first, xFrom, fy2);
378 delta = fy2-PolyBaseSize+first;
379 mCurCell.addCover(delta, twoFx*delta);
380 return;
383 // ok, we have to render several scanlines
384 int p = (PolyBaseSize-fy1)*dx;
385 int first = PolyBaseSize;
387 if (dy < 0) {
388 p = fy1*dx;
389 first = 0;
390 incr = -1;
391 dy = -dy;
394 int delta = p/dy;
395 int mod = p%dy;
397 if (mod < 0) { --delta; mod += dy; }
399 int xFrom = x1+delta;
400 renderScanLine(ey1, x1, fy1, xFrom, first);
402 ey1 += incr;
403 setCurCell(xFrom>>PolyBaseShift, ey1);
405 if (ey1 != ey2) {
406 p = PolyBaseSize*dx;
407 int lift = p/dy;
408 int rem = p%dy;
410 if (rem < 0) { --lift; rem += dy; }
411 mod -= dy;
413 while (ey1 != ey2) {
414 delta = lift;
415 mod += rem;
416 if (mod >= 0) { mod -= dy; ++delta; }
418 immutable int xTo = xFrom+delta;
419 renderScanLine(ey1, xFrom, PolyBaseSize-first, xTo, first);
420 xFrom = xTo;
422 ey1 += incr;
423 setCurCell(xFrom>>PolyBaseShift, ey1);
427 renderScanLine(ey1, xFrom, PolyBaseSize-first, x2, fy2);
430 private:
431 static void qsortCells (Cell** start, uint num) {
432 static void swapCells (Cell** a, Cell** b) nothrow @trusted @nogc {
433 pragma(inline, true);
434 auto temp = *a;
435 *a = *b;
436 *b = temp;
439 static bool lessThan (Cell** a, Cell** b) nothrow @trusted @nogc pure { pragma(inline, true); return ((**a).packedCoord < (**b).packedCoord); }
441 Cell**[80] stack = void;
442 Cell*** top;
443 Cell** limit;
444 Cell** base;
446 limit = start+num;
447 base = start;
448 top = stack.ptr;
450 for (;;) {
451 immutable int len = cast(int)(limit-base);
453 if (len > QSortThreshold) {
454 // we use base + len/2 as the pivot
455 //auto pivot = base+len/2;
456 auto pivot = base+(len>>1);
457 swapCells(base, pivot);
459 auto i = base+1;
460 auto j = limit-1;
462 // now ensure that *i <= *base <= *j
463 if (lessThan(j, i)) swapCells(i, j);
464 if (lessThan(base, i)) swapCells(base, i);
465 if (lessThan(j, base)) swapCells(base, j);
467 for (;;) {
468 do { ++i; } while (lessThan(i, base));
469 do { --j; } while (lessThan(base, j));
470 if (i > j) break;
471 swapCells(i, j);
474 swapCells(base, j);
476 // now, push the largest sub-array
477 if (j-base > limit-i) {
478 top[0] = base;
479 top[1] = j;
480 base = i;
481 } else {
482 top[0] = i;
483 top[1] = limit;
484 limit = j;
486 top += 2;
487 } else {
488 // the sub-array is small, perform insertion sort
489 auto j = base;
490 auto i = j+1;
491 for (; i < limit; j = i, ++i) {
492 for (; lessThan(j+1, j); --j) {
493 swapCells(j+1, j);
494 if (j == base) break;
497 if (top > stack.ptr) {
498 top -= 2;
499 base = top[0];
500 limit = top[1];
501 } else {
502 break;
508 void sortCells () {
509 if (mNumCells == 0) return;
511 if (mNumCells > mSortedSize) {
512 import core.stdc.stdlib: realloc;
513 mSortedSize = mNumCells;
514 mSortedCells = cast(Cell**)realloc(mSortedCells, (mNumCells+1)*(Cell*).sizeof);
517 Cell** sortedPtr = mSortedCells;
518 Cell** blockPtr = mCells;
519 Cell* cellPtr;
521 uint nb = mNumCells>>CellBlockShift;
523 while (nb--) {
524 cellPtr = *blockPtr++;
525 uint i = CellBlockSize;
526 while (i--) *sortedPtr++ = cellPtr++;
529 cellPtr = *blockPtr++;
530 uint i = mNumCells&CellBlockMask;
531 while (i--) *sortedPtr++ = cellPtr++;
532 mSortedCells[mNumCells] = null;
533 qsortCells(mSortedCells, mNumCells);
538 // ////////////////////////////////////////////////////////////////////////// //
539 /*This class is used to transfer data from class outline (or a similar one)
540 to the rendering buffer. It's organized very simple. The class stores
541 information of horizontal spans to render it into a pixel-map buffer.
542 Each span has initial X, length, and an array of bytes that determine the
543 alpha-values for each pixel. So, the restriction of using this class is 256
544 levels of Anti-Aliasing, which is quite enough for any practical purpose.
545 Before using this class you should know the minimal and maximal pixel
546 coordinates of your scanline. The protocol of using is:
547 1. reset(minX, maxX)
548 2. addCell() / addSpan() - accumulate scanline. You pass Y-coordinate
549 into these functions in order to make scanline know the last Y. Before
550 calling addCell() / addSpan() you should check with method isReady(y)
551 if the last Y has changed. It also checks if the scanline is not empty.
552 When forming one scanline the next X coordinate must be always greater
553 than the last stored one, i.e. it works only with ordered coordinates.
554 3. If the current scanline isReady() you should render it and then call
555 resetSpans() before adding new cells/spans.
557 4. Rendering:
559 Scanline provides an iterator class that allows you to extract
560 the spans and the cover values for each pixel. Be aware that clipping
561 has not been done yet, so you should perform it yourself.
563 -------------------------------------------------------------------------
564 int baseX = sl.baseX; // base X. Should be added to the span's X
565 // "sl" is a const reference to the
566 // scanline passed in.
568 int y = sl.y; // Y-coordinate of the scanline
570 ************************************
571 ...Perform vertical clipping here...
572 ************************************
574 ubyte* row = mRBuf[y]; // the the address of the beginning of the current row
576 auto span = scanline.iterator;
578 uint spanCount = scanline.spanCount(); // number of spans; it's guaranteed that
579 // spanCount is always greater than 0
581 do {
582 int x = span.next+baseX; // the beginning X of the span
584 const(ubyte)* covers = span.covers; // the array of the cover values
586 int pixelCount = span.pixelCount; // number of pixels of the span;
587 // always greater than 0, still we
588 // shoud use "int" instead of "uint"
589 // because it's more convenient for clipping
591 **************************************
592 ...Perform horizontal clipping here...
593 ...you have x, covers, and pixCount..
594 **************************************
596 ubyte* dst = row+x; // calculate the start address of the row;
597 // in this case we assume a simple
598 // grayscale image 1-byte per pixel
599 do {
600 *dst++ = *covers++; // hypotetical rendering
601 } while (--pixelCount);
602 } while (--spanCount); // spanCount cannot be 0, so this loop is quite safe
603 ------------------------------------------------------------------------
605 The question is: why should we accumulate the whole scanline when we
606 could render just separate spans when they're ready?
607 That's because using the scaline is in general faster. When is consists
608 of more than one span the conditions for the processor cash system
609 are better, because switching between two different areas of memory
610 (that can be large ones) occures less frequently.
612 struct ScanLine {
613 private:
614 int mMinX;
615 uint mMaxLen;
616 int mDX;
617 int mDY;
618 int mLastX = 0x7fff;
619 int mLastY = 0x7fff;
620 ubyte* mCovers;
621 ubyte** mStartPtrs;
622 ushort* mCounts;
623 uint mNumSpans;
624 ubyte** mCurStartPtr;
625 ushort* mCurCount;
627 public:
628 enum AAShift = 8;
630 static struct Iterator {
631 private:
632 const(ubyte)* mCovers;
633 const(ushort)* mCurCount;
634 const(ubyte*)* mCurStartPtr;
636 public nothrow @trusted @nogc:
637 mixin(DisableCopyingMixin);
639 this (in ref ScanLine sl) pure {
640 pragma(inline, true);
641 mCovers = sl.mCovers;
642 mCurCount = sl.mCounts;
643 mCurStartPtr = sl.mStartPtrs;
646 int next () {
647 pragma(inline, true);
648 ++mCurCount;
649 ++mCurStartPtr;
650 return cast(int)(*mCurStartPtr-mCovers);
653 @property int pixelCount () const pure { pragma(inline, true); return cast(int)(*mCurCount); }
654 @property const(ubyte)* covers () const pure { pragma(inline, true); return *mCurStartPtr; }
657 public nothrow @trusted @nogc:
658 mixin(DisableCopyingMixin);
660 ~this () {
661 import core.stdc.stdlib : free;
662 if (mCounts !is null) free(mCounts);
663 if (mStartPtrs !is null) free(mStartPtrs);
664 if (mCovers !is null) free(mCovers);
667 auto iterator () const pure { pragma(inline, true); return Iterator(this); }
669 void reset (int minX, int maxX, int dx=0, int dy=0) {
670 uint maxLen = maxX-minX+2;
671 if (maxLen > mMaxLen) {
672 import core.stdc.stdlib : realloc;
673 mCovers = cast(ubyte*)realloc(mCovers, maxLen*mCovers[0].sizeof);
674 if (mCovers is null) assert(0, "out of memory");
675 mStartPtrs = cast(ubyte**)realloc(mStartPtrs, mStartPtrs[0].sizeof*maxLen);
676 if (mStartPtrs is null) assert(0, "out of memory");
677 mCounts = cast(ushort*)realloc(mCounts, mCounts[0].sizeof*maxLen);
678 if (mCounts is null) assert(0, "out of memory");
679 mMaxLen = maxLen;
681 mDX = dx;
682 mDY = dy;
683 mLastX = 0x7fff;
684 mLastY = 0x7fff;
685 mMinX = minX;
686 mCurCount = mCounts;
687 mCurStartPtr = mStartPtrs;
688 mNumSpans = 0;
691 void resetSpans () {
692 pragma(inline, true);
693 mLastX = 0x7fff;
694 mLastY = 0x7fff;
695 mCurCount = mCounts;
696 mCurStartPtr = mStartPtrs;
697 mNumSpans = 0;
700 void addSpan (int x, int y, uint num, uint cover) {
701 import core.stdc.string : memset;
702 x -= mMinX;
703 memset(mCovers+x, cover, num);
704 if (x == mLastX+1) {
705 (*mCurCount) += cast(ushort)num;
706 } else {
707 *++mCurCount = cast(ushort)num;
708 *++mCurStartPtr = mCovers+x;
709 ++mNumSpans;
711 mLastX = x+num-1;
712 mLastY = y;
715 void addCell (int x, int y, uint cover) {
716 x -= mMinX;
717 mCovers[x] = cast(ubyte)cover;
718 if (x == mLastX+1) {
719 ++(*mCurCount);
720 } else {
721 *++mCurCount = 1;
722 *++mCurStartPtr = mCovers+x;
723 ++mNumSpans;
725 mLastX = x;
726 mLastY = y;
729 @property bool isReady (int y) const pure { pragma(inline, true); return (mNumSpans && (y^mLastY)); }
730 @property int baseX () const pure { pragma(inline, true); return mMinX+mDX; }
731 @property int y () const pure { pragma(inline, true); return mLastY+mDY; }
732 @property uint spanCount () const pure { pragma(inline, true); return mNumSpans; }
736 // ////////////////////////////////////////////////////////////////////////// //
737 /* This class template is used basically for rendering scanlines.
738 Usage:
739 auto ras = Rasterizer();
741 // Making polygon
742 // ras.moveTo(...);
743 // ras.lineTo(...);
744 // ...
746 // Rendering
747 ras.render(ren, Color(255, 127, 0));
749 public struct Renderer {
750 public nothrow @trusted @nogc:
751 mixin(DisableCopyingMixin);
753 static void render (in ref ScanLine sl, in GxColor clr, in ref GxRect clipRect) {
754 if (clipRect.size.h < 1 || clipRect.size.w < 1 || sl.y < clipRect.pos.y || sl.y >= clipRect.pos.y+clipRect.size.h) return;
755 immutable GxColorU c = GxColorU(clr);
756 uint spanCount = sl.spanCount;
757 immutable int baseX = sl.baseX;
758 uint* row = vglTexBuf+cast(uint)sl.y*cast(uint)VBufWidth;
759 auto span = sl.iterator;
760 do {
761 int x = span.next+baseX;
762 int pixelCount = span.pixelCount;
763 int leftSkip = void;
764 if (clipRect.clipHStripe(ref x, sl.y, ref pixelCount, &leftSkip)) {
765 const(ubyte)* covers = span.covers+leftSkip;
766 memBlendColorCoverage(row+x, covers, clr, pixelCount);
768 } while (--spanCount);
773 // ////////////////////////////////////////////////////////////////////////// //
774 /* These constants determine the subpixel accuracy, to be more precise,
775 the number of bits of the fractional part of the coordinates.
776 The possible coordinate capacity in bits can be calculated by formula:
777 sizeof(int) * 8 - PolyBaseShift * 2, i.e, for 32-bit integers and
778 8-bits fractional part the capacity is 16 bits or [-32768...32767].
780 enum : uint {
781 PolyBaseShift = 8U,
782 PolyBaseSize = cast(uint)(1<<PolyBaseShift),
783 PolyBaseMask = cast(uint)(PolyBaseSize-1),
787 int polyCoord (in float c) pure nothrow @safe @nogc { pragma(inline, true); return (cast(int)(c*(PolyBaseSize*2))+1)/2; }
789 int dbl2fix (in float c) pure nothrow @safe @nogc { pragma(inline, true); return cast(int)(c*PolyBaseSize); }
790 float fix2dbl (in int c) pure nothrow @safe @nogc { pragma(inline, true); return cast(float)c/cast(float)PolyBaseSize; }
793 // ////////////////////////////////////////////////////////////////////////// //
794 /* Polygon rasterizer that is used to render filled polygons with
795 high-quality Anti-Aliasing. Internally, by default, the class uses
796 integer coordinates in format 24.8, i.e. 24 bits for integer part
797 and 8 bits for fractional - see PolyBaseShift. This class can be
798 used in the following way:
800 1. fillRule = FillRule.EvenOdd; // optional
801 2. gamma() - optional.
802 3. reset()
803 4. moveTo(x, y) / lineTo(x, y) - make the polygon. One can create
804 more than one contour, but each contour must consist of at least 3
805 vertices, i.e. moveTo(x1, y1); lineTo(x2, y2); lineTo(x3, y3);
806 is the absolute minimum of vertices that define a triangle.
807 The algorithm does not check either the number of vertices nor
808 coincidence of their coordinates, but in the worst case it just
809 won't draw anything.
810 The orger of the vertices (clockwise or counterclockwise)
811 is important when using the non-zero filling rule (FillNonZero).
812 In this case the vertex order of all the contours must be the same
813 if you want your intersecting polygons to be without "holes".
814 You actually can use different vertices order. If the contours do not
815 intersect each other the order is not important anyway. If they do,
816 contours with the same vertex order will be rendered without "holes"
817 while the intersecting contours with different orders will have "holes".
819 fillRule() and gamma() can be called anytime before "sweeping".
821 public struct Rasterizer {
822 public nothrow @trusted @nogc:
823 enum : uint {
824 AAShift = ScanLine.AAShift,
825 AANum = 1<<AAShift,
826 AAMask = AANum-1,
827 AA2Num = AANum*2,
828 AA2Mask = AA2Num-1,
831 private:
832 Outline mOutline;
833 ScanLine mScanline;
834 FillRule mFillingRule = FillRule.NonZero;
835 ubyte[256] mGamma = DefaultGamma[];
837 public:
838 mixin(DisableCopyingMixin);
840 void reset () { mOutline.reset(); }
842 @property FillRule fillRule () const pure { pragma(inline, true); return mFillingRule; }
843 @property void fillRule (FillRule v) { pragma(inline, true); mFillingRule = v; }
845 void gamma (in float g) {
846 foreach (immutable uint i; 0..256) {
847 //import std.math : pow;
848 import core.stdc.math : powf;
849 mGamma.ptr[i] = cast(ubyte)(powf(cast(float)i/255.0f, g)*255.0f);
853 void gamma (const(ubyte)[] g) {
854 if (g.length != 256) assert(0, "invalid gamma array");
855 mGamma[] = g[0..256];
858 @property float currX () const pure { pragma(inline, true); return mOutline.currX; }
859 @property float currY () const pure { pragma(inline, true); return mOutline.currY; }
861 void moveToFixed (int x, int y) {
862 if ((x>>PolyBaseShift) <= short.min+8 || (x>>PolyBaseShift) >= short.max-8 ||
863 (y>>PolyBaseShift) <= short.min+8 || (y>>PolyBaseShift) >= short.max-8) assert(0, "coordinates out of bounds");
864 mOutline.moveTo(x, y);
867 void lineToFixed (int x, int y) {
868 if ((x>>PolyBaseShift) <= short.min+8 || (x>>PolyBaseShift) >= short.max-8 ||
869 (y>>PolyBaseShift) <= short.min+8 || (y>>PolyBaseShift) >= short.max-8) assert(0, "coordinates out of bounds");
870 mOutline.lineTo(x, y);
873 void moveTo (in float x, in float y) { mOutline.moveTo(polyCoord(x), polyCoord(y)); }
874 void lineTo (in float x, in float y) { mOutline.lineTo(polyCoord(x), polyCoord(y)); }
876 @property int minX () const pure { pragma(inline, true); return mOutline.minX; }
877 @property int minY () const pure { pragma(inline, true); return mOutline.minY; }
878 @property int maxX () const pure { pragma(inline, true); return mOutline.maxX; }
879 @property int maxY () const pure { pragma(inline, true); return mOutline.maxY; }
881 private uint calculateAlpha (int area) const pure {
882 int cover = area>>(PolyBaseShift*2+1-AAShift);
883 if (cover < 0) cover = -cover;
884 if (mFillingRule == FillRule.EvenOdd) {
885 cover &= AA2Mask;
886 if (cover > AANum) cover = AA2Num-cover;
888 if (cover > AAMask) cover = AAMask;
889 return cover;
892 void render (in ref GxRect clipRect, in GxColor c, int dx=0, int dy=0) {
893 const(Cell)** cells = mOutline.cells();
894 if (mOutline.numCells() == 0) return;
896 mScanline.reset(mOutline.minX, mOutline.maxX, dx, dy);
898 int cover = 0;
899 const(Cell)* curCell = *cells++;
901 for (;;) {
902 const(Cell)* startCell = curCell;
904 int coord = curCell.packedCoord;
905 int x = curCell.x;
906 int y = curCell.y;
908 int area = startCell.area;
909 cover += startCell.cover;
911 // accumulate all start cells
912 while ((curCell = *cells++) !is null) {
913 if (curCell.packedCoord != coord) break;
914 area += curCell.area;
915 cover += curCell.cover;
918 if (area) {
919 int alpha = calculateAlpha((cover<<(PolyBaseShift+1))-area);
920 if (alpha) {
921 if (mScanline.isReady(y)) {
922 Renderer.render(mScanline, c, clipRect);
923 mScanline.resetSpans();
925 mScanline.addCell(x, y, mGamma[alpha]);
927 ++x;
930 if (!curCell) break;
932 if (curCell.x > x) {
933 int alpha = calculateAlpha(cover<<(PolyBaseShift+1));
934 if (alpha) {
935 if (mScanline.isReady(y)) {
936 Renderer.render(mScanline, c, clipRect);
937 mScanline.resetSpans();
939 mScanline.addSpan(x, y, curCell.x-x, mGamma[alpha]);
944 if (mScanline.spanCount) Renderer.render(mScanline, c, clipRect);
947 bool hitTest (int tx, int ty) {
948 const(Cell)** cells = mOutline.cells();
949 if (mOutline.numCells == 0) return false;
951 int cover = 0;
952 const(Cell)* curCell = *cells++;
954 for (;;) {
955 const(Cell)* startCell = curCell;
957 int coord = curCell.packedCoord;
958 int x = curCell.x;
959 int y = curCell.y;
961 if (y > ty) return false;
963 int area = startCell.area;
964 cover += startCell.cover;
966 while ((curCell = *cells++) !is null) {
967 if (curCell.packedCoord != coord) break;
968 area += curCell.area;
969 cover += curCell.cover;
972 if (area) {
973 int alpha = calculateAlpha((cover<<(PolyBaseShift+1))-area);
974 if (alpha) {
975 if (tx == x && ty == y) return true;
977 ++x;
980 if (curCell is null) break;
982 if (curCell.x > x) {
983 int alpha = calculateAlpha(cover<<(PolyBaseShift+1));
984 if (alpha) {
985 if (ty == y && tx >= x && tx <= curCell.x) return true;
989 return false;
992 private:
993 static immutable ubyte[256] DefaultGamma = [
994 0, 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8,
995 9, 10, 10, 11, 11, 12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18,
996 19, 19, 20, 21, 21, 22, 22, 23, 24, 24, 25, 25, 26, 27, 27, 28,
997 29, 29, 30, 30, 31, 32, 32, 33, 34, 34, 35, 36, 36, 37, 37, 38,
998 39, 39, 40, 41, 41, 42, 43, 43, 44, 45, 45, 46, 47, 47, 48, 49,
999 49, 50, 51, 51, 52, 53, 53, 54, 55, 55, 56, 57, 57, 58, 59, 60,
1000 60, 61, 62, 62, 63, 64, 65, 65, 66, 67, 68, 68, 69, 70, 71, 71,
1001 72, 73, 74, 74, 75, 76, 77, 78, 78, 79, 80, 81, 82, 83, 83, 84,
1002 85, 86, 87, 88, 89, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1003 100,101,101,102,103,104,105,106,107,108,109,110,111,112,114,115,
1004 116,117,118,119,120,121,122,123,124,126,127,128,129,130,131,132,
1005 134,135,136,137,139,140,141,142,144,145,146,147,149,150,151,153,
1006 154,155,157,158,159,161,162,164,165,166,168,169,171,172,174,175,
1007 177,178,180,181,183,184,186,188,189,191,192,194,195,197,199,200,
1008 202,204,205,207,209,210,212,214,215,217,219,220,222,224,225,227,
1009 229,230,232,234,236,237,239,241,242,244,246,248,249,251,253,255
1014 // ////////////////////////////////////////////////////////////////////////// //
1015 public alias AGGPoint = AGGPointImpl!float;
1017 public struct AGGPointImpl(T) if (__traits(isIntegral, T) || __traits(isFloating, T)) {
1018 public nothrow @trusted @nogc:
1019 alias ValueT = T;
1020 alias Me = AGGPointImpl!T;
1022 public:
1023 static if (__traits(isFloating, T)) {
1024 T x, y;
1025 } else {
1026 T x=T.min, y=T.min;
1031 // ////////////////////////////////////////////////////////////////////////// //
1032 public enum FillRule {
1033 NonZero,
1034 EvenOdd,
1037 public enum LineCap {
1038 Butt,
1039 Square,
1040 Round,
1043 public enum LineJoin {
1044 Miter,
1045 MiterRevert,
1046 Round,
1047 Bevel,
1048 MiterRound,
1051 public enum InnerJoin {
1052 Bevel,
1053 Miter,
1054 Jag,
1055 Round,
1059 // ////////////////////////////////////////////////////////////////////////// //
1060 // some "fastgfx" backend
1061 public struct AGGDrawer {
1062 protected:
1063 static struct Vertex {
1064 float x, y; // fixed point
1065 bool asmove;
1068 protected:
1069 Rasterizer rast;
1070 float tessTol = 0.25f;
1071 float angleTol = 0.0f; // 0.0f -- angle tolerance for McSeem Bezier rasterizer
1072 float cuspLimit = 0.0f; // 0 -- cusp limit for McSeem Bezier rasterizer (0: real cusps)
1073 Vertex* vtx;
1074 uint vtxcount, vtxsize;
1076 public:
1077 // default tesselator for Bezier curves
1078 AGGTesselation tesselator = AGGTesselation.DeCasteljau;
1079 //AGGTesselation tesselator = AGGTesselation.AFD;
1080 //AGGTesselation tesselator = AGGTesselation.DeCasteljauMcSeem;
1082 nothrow @trusted @nogc:
1083 protected:
1084 enum distTol = cast(float)(1.0f/256.0f);
1085 enum KAPPA90 = 0.5522847493f; // length proportional to radius of a cubic bezier handle for 90deg arcs
1087 static bool ptEquals (in float x0, in float y0, in float x1, in float y1) pure {
1088 enum EPS = cast(float)(1.0f/256.0f);
1089 static bool eq (in float a, in float b) pure nothrow @safe @nogc { immutable float df = a-b; return (df < 0.0f ? (-df < EPS) : (df < EPS)); }
1090 return (eq(x0, x1) && eq(y0, y1));
1093 static float distPtSeg (in float x, in float y, in float px, in float py, in float qx, in float qy) pure nothrow @safe @nogc {
1094 immutable float pqx = qx-px;
1095 immutable float pqy = qy-py;
1096 float dx = x-px;
1097 float dy = y-py;
1098 immutable float d = pqx*pqx+pqy*pqy;
1099 float t = pqx*dx+pqy*dy;
1100 if (d > 0) t /= d;
1101 if (t < 0) t = 0; else if (t > 1) t = 1;
1102 dx = px+t*pqx-x;
1103 dy = py+t*pqy-y;
1104 return dx*dx+dy*dy;
1107 static float cross() (in float dx0, in float dy0, in float dx1, in float dy1) pure { pragma(inline, true); return (dx1*dy0-dx0*dy1); }
1108 static T min(T) (in T a, in T b) pure { pragma(inline, true); return (a < b ? a : b); }
1109 static T max(T) (in T a, in T b) pure { pragma(inline, true); return (a > b ? a : b); }
1110 static T sign(T) (in T a) pure { pragma(inline, true); return (a >= cast(T)0 ? cast(T)1 : cast(T)(-1)); }
1112 static float normalize (float* x, float* y) nothrow @safe @nogc {
1113 import core.stdc.math : sqrtf;
1114 float d = sqrtf((*x)*(*x)+(*y)*(*y));
1115 if (d > 1e-6f) {
1116 immutable float id = 1.0f/d;
1117 *x *= id;
1118 *y *= id;
1120 return d;
1123 void addVertexIntr (float fx, float fy, bool asMove=false) {
1124 //static bool eq (in float a, in float b) pure nothrow @safe @nogc { import std.math : abs; return (abs(a-b) < EPS); }
1125 // it is important to reject pixels that are too close!
1126 if (vtxcount > 0 && ptEquals(fx, fy, vtx[vtxcount-1].x, vtx[vtxcount-1].y)) {
1127 if (asMove != vtx[vtxcount-1].asmove) return;
1129 if (vtxcount+1 > vtxsize) {
1130 import core.stdc.stdlib : realloc;
1131 uint newsz = (vtxsize|0xfff)+1;
1132 vtx = cast(Vertex*)realloc(vtx, newsz*Vertex.sizeof);
1133 if (vtx is null) assert(0, "out of memory");
1134 vtxsize = newsz;
1136 if (asMove && vtxcount > 0 && vtx[vtxcount-1].asmove) --vtxcount;
1137 vtx[vtxcount++] = Vertex(fx, fy, asMove);
1140 void addVertex (in float fx, in float fy, bool asMove=false) {
1141 pragma(inline, true);
1142 if (vtxcount == 0 && !asMove) addVertexIntr(0, 0, true);
1143 addVertexIntr(fx, fy, asMove);
1146 protected:
1147 Stroker mStroker;
1149 public:
1150 @property void lineCap (in LineCap lc) { pragma(inline, true); mStroker.lineCap(lc); }
1151 @property void lineJoin (in LineJoin lj) { pragma(inline, true); mStroker.lineJoin(lj); }
1152 @property void innerJoin (in InnerJoin ij) { pragma(inline, true); mStroker.innerJoin(ij); }
1154 @property LineCap lineCap () const pure { pragma(inline, true); return mStroker.lineCap(); }
1155 @property LineJoin lineJoin () const pure { pragma(inline, true); return mStroker.lineJoin(); }
1156 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mStroker.innerJoin(); }
1158 @property void width (in float w) { pragma(inline, true); mStroker.width(w); }
1159 @property void miterLimit (in float ml) { pragma(inline, true); mStroker.miterLimit(ml); }
1160 @property void miterLimitTheta (in float t) { pragma(inline, true); mStroker.miterLimitTheta(t); }
1161 @property void innerMiterLimit (in float ml) { pragma(inline, true); mStroker.innerMiterLimit(ml); }
1162 @property void approximationScale (in float as) { pragma(inline, true); mStroker.approximationScale(as); }
1164 @property float width () const pure { pragma(inline, true); return mStroker.width(); }
1165 @property float miterLimit () const pure { pragma(inline, true); return mStroker.miterLimit(); }
1166 @property float innerMiterLimit () const pure { pragma(inline, true); return mStroker.innerMiterLimit(); }
1167 @property float approximationScale () const pure { pragma(inline, true); return mStroker.approximationScale(); }
1169 @property void shorten (in float s) { pragma(inline, true); mStroker.shorten = s; }
1170 @property float shorten () const pure { pragma(inline, true); return mStroker.shorten; }
1172 @property FillRule fillRule () const pure { pragma(inline, true); return rast.fillRule; }
1173 @property void fillRule (FillRule v) { pragma(inline, true); rast.fillRule = v; }
1175 @property float currX () const pure { pragma(inline, true); return (vtxcount > 0 ? vtx[vtxcount-1].x : 0.0f); }
1176 @property float currY () const pure { pragma(inline, true); return (vtxcount > 0 ? vtx[vtxcount-1].y : 0.0f); }
1178 // ////////////////////////////////////////////////////////////////////////// //
1179 ref AGGDrawer beginFrame () {
1180 vtxcount = 0;
1181 //addVertex(0, 0, true);
1182 mStroker.width = 1.5f;
1183 rast.reset();
1184 return this;
1187 ref AGGDrawer cancelFrame () {
1188 vtxcount = 0;
1189 //addVertex(0, 0, true);
1190 rast.reset();
1191 return this;
1194 ref AGGDrawer endFrame (in GxColor c) {
1195 GxRect clipRect = gxClipRect;
1196 if (clipRect.intersect(0, 0, VBufWidth, VBufHeight)) {
1197 rast.render(clipRect, c);
1199 return beginFrame();
1202 ref AGGDrawer beginPath () {
1203 pragma(inline, true);
1204 vtxcount = 0;
1205 return this;
1208 ref AGGDrawer closePath () {
1209 if (vtxcount > 1 && !vtx[vtxcount-1].asmove) {
1210 uint vp = vtxcount;
1211 while (vp > 0 && !vtx[vp-1].asmove) --vp;
1212 addVertex(vtx[vp].x, vtx[vp].y); // close it
1213 addVertex(vtx[vp].x, vtx[vp].y, true); // and open new one
1215 return this;
1218 // starts new sub-path with specified point as first point
1219 ref AGGDrawer moveTo (in float x, in float y) { pragma(inline, true); addVertex(x, y, true); return this; }
1221 // adds line segment from the last point in the path to the specified point
1222 ref AGGDrawer lineTo (in float x, in float y) { pragma(inline, true); addVertex(x, y); return this; }
1224 // adds cubic bezier segment from last point in the path via two control points to the specified point
1225 ref AGGDrawer bezierTo (in float x2, in float y2, in float x3, in float y3, in float x4, in float y4) {
1226 pragma(inline, true);
1227 //tesselateBezierMcSeem(currX, currY, x2, y2, x3, y3, x4, y4, 0);
1228 tesselateBezier(currX, currY, x2, y2, x3, y3, x4, y4);
1229 return this;
1232 // adds quadratic bezier segment from last point in the path via a control point to the specified point
1233 void quadTo (in float cx, in float cy, in float x, in float y) {
1234 immutable float x0 = currX;
1235 immutable float y0 = currY;
1236 bezierTo(
1237 x0+2.0f/3.0f*(cx-x0), y0+2.0f/3.0f*(cy-y0),
1238 x+2.0f/3.0f*(cx-x), y+2.0f/3.0f*(cy-y),
1239 x, y,
1243 // adds an arc segment at the corner defined by the last path point, and two specified points
1244 ref AGGDrawer arcTo (in float x1, in float y1, in float x2, in float y2, in float radius) {
1245 import core.stdc.math : acosf, tanf, atan2f;
1247 immutable float x0 = currX;
1248 immutable float y0 = currY;
1250 // handle degenerate cases
1251 if (ptEquals(x0, y0, x1, y1) ||
1252 ptEquals(x1, y1, x2, y2) ||
1253 distPtSeg(x1, y1, x0, y0, x2, y2) < distTol*distTol ||
1254 radius < distTol)
1256 lineTo(x1, y1);
1257 return this;
1260 // calculate tangential circle to lines (x0, y0)-(x1, y1) and (x1, y1)-(x2, y2)
1261 float dx0 = x0-x1;
1262 float dy0 = y0-y1;
1263 float dx1 = x2-x1;
1264 float dy1 = y2-y1;
1265 normalize(&dx0, &dy0);
1266 normalize(&dx1, &dy1);
1267 immutable float a = acosf(dx0*dx1+dy0*dy1);
1268 immutable float d = radius/tanf(a*0.5f);
1270 if (d > 10000.0f) {
1271 lineTo(x1, y1);
1272 return this;
1275 float cx = void, cy = void, a0 = void, a1 = void;
1276 Winding dir = void;
1277 if (cross(dx0, dy0, dx1, dy1) > 0.0f) {
1278 cx = x1+dx0*d+dy0*radius;
1279 cy = y1+dy0*d+-dx0*radius;
1280 a0 = atan2f(dx0, -dy0);
1281 a1 = atan2f(-dx1, dy1);
1282 dir = Winding.CW;
1283 //printf("CW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
1284 } else {
1285 cx = x1+dx0*d+-dy0*radius;
1286 cy = y1+dy0*d+dx0*radius;
1287 a0 = atan2f(-dx0, dy0);
1288 a1 = atan2f(dx1, -dy1);
1289 dir = Winding.CCW;
1290 //printf("CCW c=(%f, %f) a0=%f° a1=%f°\n", cx, cy, a0/NVG_PI*180.0f, a1/NVG_PI*180.0f);
1293 return arc(dir, cx, cy, radius, a0, a1); // first is line
1297 // ////////////////////////////////////////////////////////////////////////// //
1298 // fill pathes
1299 void fill () {
1300 //pragma(inline, true);
1301 foreach (const ref Vertex vt; vtx[0..vtxcount]) {
1302 if (vt.asmove) rast.moveTo(vt.x, vt.y); else rast.lineTo(vt.x, vt.y);
1306 // stroke pathes
1307 void stroke () {
1308 //if (mStroker.width <= 1.0) { contour(); return; }
1309 mStroker.removeAll();
1311 bool waitingLine = true;
1312 foreach (const ref Vertex vt; vtx[0..vtxcount]) {
1313 mStroker.addVertex(vt.x, vt.y, (vt.asmove ? PathCommand.MoveTo : PathCommand.LineTo));
1315 mStroker.addVertex(0, 0, PathCommand.Stop);
1317 float x, y;
1318 mStroker.rewind();
1319 for (;;) {
1320 auto cmd = mStroker.vertex(&x, &y);
1321 if (isStop(cmd)) break;
1322 if (isMoveTo(cmd)) { rast.moveTo(x, y); continue; }
1323 if (isLineTo(cmd)) { rast.lineTo(x, y); continue; }
1324 if (isEndPoly(cmd)) {
1326 if (is_close(cmd) && !first) {
1327 //writeln("s=(", sx, ",", sy, "); c=(", x, ",", y, ")");
1328 pts.add(PathPoint(sx, sy, false));
1335 // contour pathes
1336 void contour () {
1337 Contourer ctr;
1338 ctr.removeAll();
1340 ctr.lineCap = mStroker.lineCap;
1341 ctr.lineJoin = mStroker.lineJoin;
1342 ctr.innerJoin = mStroker.innerJoin;
1344 ctr.width = mStroker.width;
1345 ctr.miterLimit = mStroker.miterLimit;
1346 ctr.innerMiterLimit = mStroker.innerMiterLimit;
1347 ctr.approximationScale = mStroker.approximationScale;
1349 bool waitingLine = true;
1350 foreach (const ref Vertex vt; vtx[0..vtxcount]) {
1351 ctr.addVertex(vt.x, vt.y, (vt.asmove ? PathCommand.MoveTo : PathCommand.LineTo));
1353 ctr.addVertex(0, 0, PathCommand.Stop);
1355 float x, y;
1356 ctr.rewind();
1357 for (;;) {
1358 auto cmd = ctr.vertex(&x, &y);
1359 if (isStop(cmd)) break;
1360 if (isMoveTo(cmd)) { rast.moveTo(x, y); continue; }
1361 if (isLineTo(cmd)) { rast.lineTo(x, y); continue; }
1366 // ////////////////////////////////////////////////////////////////////////// //
1367 enum Winding { CW, CCW }
1369 /* Creates new circle arc shaped sub-path. The arc center is at (cx, cy), the arc radius is r,
1370 * and the arc is drawn from angle a0 to a1, and swept in direction dir (NVGWinding.CCW, or NVGWinding.CW).
1371 * Angles are specified in radians.
1373 * [mode] is: "original", "move", "line" -- first command will be like original NanoVega, MoveTo, or LineTo
1375 ref AGGDrawer arc(string mode="original") (Winding dir, in float cx, in float cy, in float r,
1376 in float a0, in float a1)
1378 static assert(mode == "original" || mode == "move" || mode == "line");
1379 import core.stdc.math : fabsf, cosf, sinf;
1381 //int move = (ctx.ncommands > 0 ? Command.LineTo : Command.MoveTo);
1382 static if (mode == "original") {
1383 bool asMove = (vtxcount == 0);
1384 } else static if (mode == "move") {
1385 enum asMove = true;
1386 } else static if (mode == "line") {
1387 enum asMove = false;
1388 } else {
1389 static assert(0, "wtf?!");
1392 // clamp angles
1393 float da = a1-a0;
1394 if (dir == Winding.CW) {
1395 if (fabsf(da) >= FLT_PI*2.0f) {
1396 da = FLT_PI*2.0f;
1397 } else {
1398 while (da < 0.0f) da += FLT_PI*2.0f;
1400 } else {
1401 if (fabsf(da) >= FLT_PI*2.0f) {
1402 da = -FLT_PI*2.0f;
1403 } else {
1404 while (da > 0.0f) da -= FLT_PI*2.0f;
1408 // split arc into max 90 degree segments
1409 immutable int ndivs = max(1, min(cast(int)(fabsf(da)/(FLT_PI*0.5f)+0.5f), 5));
1410 immutable float hda = (da/cast(float)ndivs)*0.5f;
1411 float kappa = fabsf(4.0f/3.0f*(1.0f-cosf(hda))/sinf(hda));
1412 if (dir == Winding.CCW) kappa = -kappa;
1414 int nvals = 0;
1415 float px = 0, py = 0, ptanx = 0, ptany = 0;
1416 foreach (int i; 0..ndivs+1) {
1417 immutable float a = a0+da*(i/cast(float)ndivs);
1418 immutable float dx = cosf(a);
1419 immutable float dy = sinf(a);
1420 immutable float x = cx+dx*r;
1421 immutable float y = cy+dy*r;
1422 immutable float tanx = -dy*r*kappa;
1423 immutable float tany = dx*r*kappa;
1425 if (i == 0) {
1426 addVertex(x, y, asMove);
1427 } else {
1428 bezierTo(px+ptanx, py+ptany, x-tanx, y-tany, x, y);
1430 px = x;
1431 py = y;
1432 ptanx = tanx;
1433 ptany = tany;
1436 return this;
1439 // creates new rectangle shaped sub-path
1440 ref AGGDrawer rect (in float x, in float y, in float w, in float h) {
1441 if (w && h) {
1442 moveTo(x, y);
1443 lineTo(x, y+h);
1444 lineTo(x+w, y+h);
1445 lineTo(x+w, y);
1446 lineTo(x, y); // close it
1448 return this;
1451 // creates new rounded rectangle shaped sub-path
1452 ref AGGDrawer roundedRect (in float x, in float y, in float w, in float h, in float radius) {
1453 return roundedRectVarying(x, y, w, h, radius, radius, radius, radius);
1456 // creates new rounded rectangle shaped sub-path
1457 // specify ellipse width and height to round corners according to it
1458 ref AGGDrawer roundedRectEllipse (in float x, in float y, in float w, in float h, in float rw, in float rh) {
1459 if (rw < 0.1f || rh < 0.1f) return rect(x, y, w, h);
1460 moveTo(x+rw, y);
1461 lineTo(x+w-rw, y);
1462 bezierTo(x+w-rw*(1.0f-KAPPA90), y, x+w, y+rh*(1.0f-KAPPA90), x+w, y+rh);
1463 lineTo(x+w, y+h-rh);
1464 bezierTo(x+w, y+h-rh*(1.0f-KAPPA90), x+w-rw*(1.0f-KAPPA90), y+h, x+w-rw, y+h);
1465 lineTo(x+rw, y+h);
1466 bezierTo(x+rw*(1.0f-KAPPA90), y+h, x, y+h-rh*(1.0f-KAPPA90), x, y+h-rh);
1467 lineTo(x, y+rh);
1468 bezierTo(x, y+rh*(1.0f-KAPPA90), x+rw*(1.0f-KAPPA90), y, x+rw, y);
1469 return closePath();
1472 // creates new rounded rectangle shaped sub-path
1473 // this one allows you to specify different rounding radii for each corner
1474 ref AGGDrawer roundedRectVarying (in float x, in float y, in float w, in float h,
1475 in float radTopLeft, in float radTopRight,
1476 in float radBottomRight, in float radBottomLeft)
1478 import core.stdc.math : fabsf;
1479 if (radTopLeft < 0.1f && radTopRight < 0.1f && radBottomRight < 0.1f && radBottomLeft < 0.1f) return rect(x, y, w, h);
1480 immutable float halfw = fabsf(w)*0.5f;
1481 immutable float halfh = fabsf(h)*0.5f;
1482 immutable float rxBL = min(radBottomLeft, halfw)*sign(w), ryBL = min(radBottomLeft, halfh)*sign(h);
1483 immutable float rxBR = min(radBottomRight, halfw)*sign(w), ryBR = min(radBottomRight, halfh)*sign(h);
1484 immutable float rxTR = min(radTopRight, halfw)*sign(w), ryTR = min(radTopRight, halfh)*sign(h);
1485 immutable float rxTL = min(radTopLeft, halfw)*sign(w), ryTL = min(radTopLeft, halfh)*sign(h);
1486 moveTo(x, y+ryTL);
1487 lineTo(x, y+h-ryBL);
1488 bezierTo(x, y+h-ryBL*(1.0f-KAPPA90), x+rxBL*(1.0f-KAPPA90), y+h, x+rxBL, y+h);
1489 lineTo(x+w-rxBR, y+h);
1490 bezierTo(x+w-rxBR*(1.0f-KAPPA90), y+h, x+w, y+h-ryBR*(1.0f-KAPPA90), x+w, y+h-ryBR);
1491 lineTo(x+w, y+ryTR);
1492 bezierTo(x+w, y+ryTR*(1.0f-KAPPA90), x+w-rxTR*(1.0f-KAPPA90), y, x+w-rxTR, y);
1493 lineTo(x+rxTL, y);
1494 bezierTo(x+rxTL*(1.0f-KAPPA90), y, x, y+ryTL*(1.0f-KAPPA90), x, y+ryTL);
1495 return closePath();
1498 // creates new ellipse shaped sub-path
1499 ref AGGDrawer ellipse (in float cx, in float cy, in float rx, in float ry) {
1500 moveTo(cx-rx, cy);
1501 bezierTo(cx-rx, cy+ry*KAPPA90, cx-rx*KAPPA90, cy+ry, cx, cy+ry);
1502 bezierTo(cx+rx*KAPPA90, cy+ry, cx+rx, cy+ry*KAPPA90, cx+rx, cy);
1503 bezierTo(cx+rx, cy-ry*KAPPA90, cx+rx*KAPPA90, cy-ry, cx, cy-ry);
1504 bezierTo(cx-rx*KAPPA90, cy-ry, cx-rx, cy-ry*KAPPA90, cx-rx, cy);
1505 return closePath();
1508 // creates new circle shaped sub-path
1509 ref AGGDrawer circle (in float cx, in float cy, in float r) {
1510 return ellipse(cx, cy, r, r);
1513 private:
1514 // ////////////////////////////////////////////////////////////////////////// //
1515 void tesselateBezierDCj (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4, in int level/*, in int type*/) {
1516 import core.stdc.math : fabsf;
1517 if (level > 10) return;
1519 // check for collinear points, and use AFD tesselator on such curves (it is WAY faster for this case)
1521 if (level == 0 && ctx.tesselatortype == NVGTesselation.Combined) {
1522 static bool collinear (in float v0x, in float v0y, in float v1x, in float v1y, in float v2x, in float v2y) nothrow @trusted @nogc {
1523 immutable float cz = (v1x-v0x)*(v2y-v0y)-(v2x-v0x)*(v1y-v0y);
1524 return (fabsf(cz*cz) <= 0.01f); // arbitrary number, seems to work ok with NanoSVG output
1526 if (collinear(x1, y1, x2, y2, x3, y3) && collinear(x2, y2, x3, y3, x3, y4)) {
1527 //{ import core.stdc.stdio; printf("AFD fallback!\n"); }
1528 ctx.tesselateBezierAFD(x1, y1, x2, y2, x3, y3, x4, y4, type);
1529 return;
1534 immutable float x12 = (x1+x2)*0.5f;
1535 immutable float y12 = (y1+y2)*0.5f;
1536 immutable float x23 = (x2+x3)*0.5f;
1537 immutable float y23 = (y2+y3)*0.5f;
1538 immutable float x34 = (x3+x4)*0.5f;
1539 immutable float y34 = (y3+y4)*0.5f;
1540 immutable float x123 = (x12+x23)*0.5f;
1541 immutable float y123 = (y12+y23)*0.5f;
1543 immutable float dx = x4-x1;
1544 immutable float dy = y4-y1;
1545 immutable float d2 = fabsf(((x2-x4)*dy-(y2-y4)*dx));
1546 immutable float d3 = fabsf(((x3-x4)*dy-(y3-y4)*dx));
1548 if ((d2+d3)*(d2+d3) < tessTol*(dx*dx+dy*dy)) {
1549 addVertex(x4, y4/*, type*/);
1550 return;
1553 immutable float x234 = (x23+x34)*0.5f;
1554 immutable float y234 = (y23+y34)*0.5f;
1555 immutable float x1234 = (x123+x234)*0.5f;
1556 immutable float y1234 = (y123+y234)*0.5f;
1558 // "taxicab" / "manhattan" check for flat curves
1559 if (fabsf(x1+x3-x2-x2)+fabsf(y1+y3-y2-y2)+fabsf(x2+x4-x3-x3)+fabsf(y2+y4-y3-y3) < tessTol*0.25f) {
1560 addVertex(x1234, y1234/*, type*/);
1561 return;
1564 tesselateBezierDCj(x1, y1, x12, y12, x123, y123, x1234, y1234, level+1/*, 0*/);
1565 tesselateBezierDCj(x1234, y1234, x234, y234, x34, y34, x4, y4, level+1/*, type*/);
1568 // ////////////////////////////////////////////////////////////////////////// //
1569 // based on the ideas and code of Maxim Shemanarev. Rest in Peace, bro!
1570 // see http://www.antigrain.com/research/adaptive_bezier/index.html
1571 void tesselateBezierMcSeem (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4, in int level/*, in int type*/) {
1572 //import std.math : atan2, PI;
1573 import core.stdc.math : fabsf, atan2f;
1575 enum CollinearEPS = 0.00000001f; // 0.00001f;
1576 enum AngleTolEPS = 0.01f;
1578 static float distSquared (in float x1, in float y1, in float x2, in float y2) pure nothrow @safe @nogc {
1579 pragma(inline, true);
1580 immutable float dx = x2-x1;
1581 immutable float dy = y2-y1;
1582 return dx*dx+dy*dy;
1586 if (level == 0) {
1587 addVertex(x1, y1/*, 0*/);
1588 tesselateBezierMcSeem(x1, y1, x2, y2, x3, y3, x4, y4, 1/*, type*/);
1589 addVertex(x4, y4/*, type*/);
1590 return;
1594 if (level >= 32) return; // recurse limit; practically, it should be never reached, but...
1596 // calculate all the mid-points of the line segments
1597 immutable float x12 = (x1+x2)*0.5f;
1598 immutable float y12 = (y1+y2)*0.5f;
1599 immutable float x23 = (x2+x3)*0.5f;
1600 immutable float y23 = (y2+y3)*0.5f;
1601 immutable float x34 = (x3+x4)*0.5f;
1602 immutable float y34 = (y3+y4)*0.5f;
1603 immutable float x123 = (x12+x23)*0.5f;
1604 immutable float y123 = (y12+y23)*0.5f;
1605 immutable float x234 = (x23+x34)*0.5f;
1606 immutable float y234 = (y23+y34)*0.5f;
1607 immutable float x1234 = (x123+x234)*0.5f;
1608 immutable float y1234 = (y123+y234)*0.5f;
1610 // try to approximate the full cubic curve by a single straight line
1611 immutable float dx = x4-x1;
1612 immutable float dy = y4-y1;
1614 float d2 = fabsf(((x2-x4)*dy-(y2-y4)*dx));
1615 float d3 = fabsf(((x3-x4)*dy-(y3-y4)*dx));
1617 final switch ((cast(int)(d2 > CollinearEPS)<<1)+cast(int)(d3 > CollinearEPS)) {
1618 case 0:
1619 // all collinear or p1 == p4
1620 float k = dx*dx+dy*dy;
1621 if (k == 0) {
1622 d2 = distSquared(x1, y1, x2, y2);
1623 d3 = distSquared(x4, y4, x3, y3);
1624 } else {
1625 k = 1.0f/k;
1626 float da1 = x2-x1;
1627 float da2 = y2-y1;
1628 d2 = k*(da1*dx+da2*dy);
1629 da1 = x3-x1;
1630 da2 = y3-y1;
1631 d3 = k*(da1*dx+da2*dy);
1632 if (d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) {
1633 // Simple collinear case, 1---2---3---4
1634 // We can leave just two endpoints
1635 return;
1637 if (d2 <= 0) d2 = distSquared(x2, y2, x1, y1);
1638 else if (d2 >= 1) d2 = distSquared(x2, y2, x4, y4);
1639 else d2 = distSquared(x2, y2, x1+d2*dx, y1+d2*dy);
1641 if (d3 <= 0) d3 = distSquared(x3, y3, x1, y1);
1642 else if (d3 >= 1) d3 = distSquared(x3, y3, x4, y4);
1643 else d3 = distSquared(x3, y3, x1+d3*dx, y1+d3*dy);
1645 if (d2 > d3) {
1646 if (d2 < tessTol) {
1647 addVertex(x2, y2/*, type*/);
1648 return;
1650 } if (d3 < tessTol) {
1651 addVertex(x3, y3/*, type*/);
1652 return;
1654 break;
1655 case 1:
1656 // p1,p2,p4 are collinear, p3 is significant
1657 if (d3*d3 <= tessTol*(dx*dx+dy*dy)) {
1658 if (angleTol < AngleTolEPS) {
1659 addVertex(x23, y23/*, type*/);
1660 return;
1661 } else {
1662 // angle condition
1663 float da1 = fabsf(atan2f(y4-y3, x4-x3)-atan2f(y3-y2, x3-x2));
1664 if (da1 >= FLT_PI) da1 = 2.0f*FLT_PI-da1;
1665 if (da1 < angleTol) {
1666 addVertex(x2, y2/*, type*/);
1667 addVertex(x3, y3/*, type*/);
1668 return;
1670 if (cuspLimit != 0.0f) {
1671 if (da1 > cuspLimit) {
1672 addVertex(x3, y3/*, type*/);
1673 return;
1678 break;
1679 case 2:
1680 // p1,p3,p4 are collinear, p2 is significant
1681 if (d2*d2 <= tessTol*(dx*dx+dy*dy)) {
1682 if (angleTol < AngleTolEPS) {
1683 addVertex(x23, y23/*, type*/);
1684 return;
1685 } else {
1686 // angle condition
1687 float da1 = fabsf(atan2f(y3-y2, x3-x2)-atan2f(y2-y1, x2-x1));
1688 if (da1 >= FLT_PI) da1 = 2.0f*FLT_PI-da1;
1689 if (da1 < angleTol) {
1690 addVertex(x2, y2/*, type*/);
1691 addVertex(x3, y3/*, type*/);
1692 return;
1694 if (cuspLimit != 0.0f) {
1695 if (da1 > cuspLimit) {
1696 addVertex(x2, y2/*, type*/);
1697 return;
1702 break;
1703 case 3:
1704 // regular case
1705 if ((d2+d3)*(d2+d3) <= tessTol*(dx*dx+dy*dy)) {
1706 // if the curvature doesn't exceed the distance tolerance value, we tend to finish subdivisions
1707 if (angleTol < AngleTolEPS) {
1708 addVertex(x23, y23/*, type*/);
1709 return;
1710 } else {
1711 // angle and cusp condition
1712 immutable float k = atan2f(y3-y2, x3-x2);
1713 float da1 = fabsf(k-atan2f(y2-y1, x2-x1));
1714 float da2 = fabsf(atan2f(y4-y3, x4-x3)-k);
1715 if (da1 >= FLT_PI) da1 = 2.0f*FLT_PI-da1;
1716 if (da2 >= FLT_PI) da2 = 2.0f*FLT_PI-da2;
1717 if (da1+da2 < angleTol) {
1718 // finally we can stop the recursion
1719 addVertex(x23, y23/*, type*/);
1720 return;
1722 if (cuspLimit != 0.0f) {
1723 if (da1 > cuspLimit) {
1724 addVertex(x2, y2/*, type*/);
1725 return;
1727 if (da2 > cuspLimit) {
1728 addVertex(x3, y3/*, type*/);
1729 return;
1734 break;
1737 // continue subdivision
1738 tesselateBezierMcSeem(x1, y1, x12, y12, x123, y123, x1234, y1234, level+1/*, 0*/);
1739 tesselateBezierMcSeem(x1234, y1234, x234, y234, x34, y34, x4, y4, level+1/*, type*/);
1742 // Adaptive forward differencing for bezier tesselation.
1743 // See Lien, Sheue-Ling, Michael Shantz, and Vaughan Pratt.
1744 // "Adaptive forward differencing for rendering curves and surfaces."
1745 // ACM SIGGRAPH Computer Graphics. Vol. 21. No. 4. ACM, 1987.
1746 // original code by Taylor Holliday <taylor@audulus.com>
1747 void tesselateBezierAFD (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4/*, in int type*/) nothrow @trusted @nogc {
1748 enum AFD_ONE = (1<<10);
1750 // power basis
1751 immutable float ax = -x1+3*x2-3*x3+x4;
1752 immutable float ay = -y1+3*y2-3*y3+y4;
1753 immutable float bx = 3*x1-6*x2+3*x3;
1754 immutable float by = 3*y1-6*y2+3*y3;
1755 immutable float cx = -3*x1+3*x2;
1756 immutable float cy = -3*y1+3*y2;
1758 // Transform to forward difference basis (stepsize 1)
1759 float px = x1;
1760 float py = y1;
1761 float dx = ax+bx+cx;
1762 float dy = ay+by+cy;
1763 float ddx = 6*ax+2*bx;
1764 float ddy = 6*ay+2*by;
1765 float dddx = 6*ax;
1766 float dddy = 6*ay;
1768 //printf("dx: %f, dy: %f\n", dx, dy);
1769 //printf("ddx: %f, ddy: %f\n", ddx, ddy);
1770 //printf("dddx: %f, dddy: %f\n", dddx, dddy);
1772 int t = 0;
1773 int dt = AFD_ONE;
1775 immutable float tol = tessTol*4.0f;
1777 while (t < AFD_ONE) {
1778 // Flatness measure.
1779 float d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
1781 // printf("d: %f, th: %f\n", d, th);
1783 // Go to higher resolution if we're moving a lot or overshooting the end.
1784 while ((d > tol && dt > 1) || (t+dt > AFD_ONE)) {
1785 // printf("up\n");
1787 // Apply L to the curve. Increase curve resolution.
1788 dx = 0.5f*dx-(1.0f/8.0f)*ddx+(1.0f/16.0f)*dddx;
1789 dy = 0.5f*dy-(1.0f/8.0f)*ddy+(1.0f/16.0f)*dddy;
1790 ddx = (1.0f/4.0f)*ddx-(1.0f/8.0f)*dddx;
1791 ddy = (1.0f/4.0f)*ddy-(1.0f/8.0f)*dddy;
1792 dddx = (1.0f/8.0f)*dddx;
1793 dddy = (1.0f/8.0f)*dddy;
1795 // Half the stepsize.
1796 dt >>= 1;
1798 // Recompute d
1799 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
1802 // Go to lower resolution if we're really flat
1803 // and we aren't going to overshoot the end.
1804 // XXX: tol/32 is just a guess for when we are too flat.
1805 while ((d > 0 && d < tol/32.0f && dt < AFD_ONE) && (t+2*dt <= AFD_ONE)) {
1806 // printf("down\n");
1808 // Apply L^(-1) to the curve. Decrease curve resolution.
1809 dx = 2*dx+ddx;
1810 dy = 2*dy+ddy;
1811 ddx = 4*ddx+4*dddx;
1812 ddy = 4*ddy+4*dddy;
1813 dddx = 8*dddx;
1814 dddy = 8*dddy;
1816 // Double the stepsize.
1817 dt <<= 1;
1819 // Recompute d
1820 d = ddx*ddx+ddy*ddy+dddx*dddx+dddy*dddy;
1823 // Forward differencing.
1824 px += dx;
1825 py += dy;
1826 dx += ddx;
1827 dy += ddy;
1828 ddx += dddx;
1829 ddy += dddy;
1831 // Output a point.
1832 addVertex(px, py/*, (t > 0 ? type : 0)*/);
1834 // Advance along the curve.
1835 t += dt;
1837 // Ensure we don't overshoot.
1838 assert(t <= AFD_ONE);
1843 public void tesselateBezier (in float x1, in float y1, in float x2, in float y2, in float x3, in float y3, in float x4, in float y4) {
1844 final switch (tesselator) with (AGGTesselation) {
1845 case DeCasteljau:
1846 tesselateBezierDCj(x1, y1, x2, y2, x3, y3, x4, y4, 0/*, type*/);
1847 break;
1848 case AFD:
1849 tesselateBezierAFD(x1, y1, x2, y2, x3, y3, x4, y4/*, type*/);
1850 break;
1851 case DeCasteljauMcSeem:
1852 addVertex(x1, y1/*, 0*/);
1853 tesselateBezierMcSeem(x1, y1, x2, y2, x3, y3, x4, y4, 1/*, type*/);
1854 addVertex(x4, y4/*, type*/);
1855 break;
1859 // ////////////////////////////////////////////////////////////////////////// //
1860 public enum BaphometDims = 512; // [0..511]
1861 private enum baph_debug_time = false;
1862 private enum baph_debug_dump = false;
1863 public void renderBaphomet (float ofsx=0.0f, float ofsy=0.0f, float scalex=1.0f, float scaley=1.0f) {
1864 auto path = cast(const(ubyte)[])baphometPath;
1865 immutable plen = path.length;
1866 uint ppos = 0;
1868 enum Command {
1869 Bounds, // always first, has 4 args (x0, y0, x1, y1)
1870 StrokeMode,
1871 FillMode,
1872 StrokeFillMode,
1873 NormalStroke,
1874 ThinStroke,
1875 MoveTo,
1876 LineTo,
1877 CubicTo, // cubic bezier
1878 EndPath,
1881 Command getCommand () nothrow @trusted @nogc {
1882 if (ppos >= plen) assert(0, "invalid path");
1883 return cast(Command)(path.ptr[ppos++]);
1886 float getFloat () nothrow @trusted @nogc {
1887 if (ppos >= plen || plen-ppos < float.sizeof) assert(0, "invalid path");
1888 version(LittleEndian) {
1889 float res = *cast(const(float)*)(&path.ptr[ppos]);
1890 ppos += cast(uint)float.sizeof;
1891 return res;
1892 } else {
1893 static assert(float.sizeof == 4);
1894 uint xp = path.ptr[ppos]|(path.ptr[ppos+1]<<8)|(path.ptr[ppos+2]<<16)|(path.ptr[ppos+3]<<24);
1895 ppos += cast(uint)float.sizeof;
1896 return *cast(const(float)*)(&xp);
1900 float scaleX (in float v) nothrow @trusted @nogc { pragma(inline, true); return ofsx+cast(float)v*scalex; }
1901 float scaleY (in float v) nothrow @trusted @nogc { pragma(inline, true); return ofsy+cast(float)v*scaley; }
1903 static if (baph_debug_time) {
1904 import iv.pxclock;
1905 immutable tstt = clockMicro();
1908 beginPath();
1909 float cx = 0.0f, cy = 0.0f;
1910 bool doStroke = false, doFill = false;
1911 while (ppos < plen) {
1912 auto cmd = getCommand();
1913 static if (baph_debug_dump) {
1914 foreach (string mn; __traits(allMembers, Command)) {
1915 if (__traits(getMember, Command, mn) == cmd) {
1916 import core.stdc.stdio;
1917 static if (baph_debug_time) {
1918 printf("cmd=%s ... ", mn.ptr);
1919 } else {
1920 printf("cmd=%s\n", mn.ptr);
1924 static if (baph_debug_time) {
1925 immutable cstt = clockMicro();
1928 final switch (cmd) {
1929 case Command.Bounds: ppos += 4*cast(uint)float.sizeof; break;
1930 case Command.StrokeMode: doStroke = true; doFill = false; break;
1931 case Command.FillMode: doStroke = false; doFill = true; break;
1932 case Command.StrokeFillMode: doStroke = true; doFill = true; break;
1933 case Command.NormalStroke: case Command.ThinStroke: break;
1934 case Command.MoveTo:
1935 cx = scaleX(getFloat());
1936 cy = scaleY(getFloat());
1937 moveTo(cx, cy);
1938 break;
1939 case Command.LineTo:
1940 immutable float ex = scaleX(getFloat());
1941 immutable float ey = scaleY(getFloat());
1942 lineTo(ex, ey);
1943 cx = ex;
1944 cy = ey;
1945 break;
1946 case Command.CubicTo: // cubic bezier
1947 immutable float x1 = scaleX(getFloat());
1948 immutable float y1 = scaleY(getFloat());
1949 immutable float x2 = scaleX(getFloat());
1950 immutable float y2 = scaleY(getFloat());
1951 immutable float ex = scaleX(getFloat());
1952 immutable float ey = scaleY(getFloat());
1953 bezierTo(x1, y1, x2, y2, ex, ey);
1954 cx = ex;
1955 cy = ey;
1956 break;
1957 case Command.EndPath: // don't close this path
1958 if (doFill) fill();
1959 if (doStroke) stroke();
1960 //doFill = doStroke = false;
1961 beginPath();
1962 break;
1964 static if (baph_debug_dump && baph_debug_time) {
1965 immutable estt = clockMicro()-cstt;
1966 import core.stdc.stdio; printf("microsecs: %u\n", cast(uint)estt);
1969 static if (baph_debug_time) {
1970 immutable estt = clockMicro()-tstt;
1971 import core.stdc.stdio; printf("total microsecs: %u\n", cast(uint)estt);
1973 if (doFill) {
1974 static if (baph_debug_time) {
1975 immutable fstt = clockMicro();
1977 fill();
1978 static if (baph_debug_time) {
1979 immutable t = clockMicro()-fstt;
1980 import core.stdc.stdio; printf("fill microsecs: %u\n", cast(uint)t);
1983 if (doStroke) {
1984 static if (baph_debug_time) {
1985 immutable sstt = clockMicro();
1987 stroke();
1988 static if (baph_debug_time) {
1989 immutable t = clockMicro()-sstt;
1990 import core.stdc.stdio; printf("stroke microsecs: %u\n", cast(uint)t);
1993 beginPath();
1996 private static immutable ubyte[7641] baphometPath = [
1997 0x01,0x04,0x06,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x08,0xa0,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,
1998 0x00,0x80,0xff,0x43,0xa2,0x1d,0xc6,0x43,0x00,0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x08,0x00,0x80,0xff,
1999 0x43,0x7a,0x89,0xe5,0x42,0xa0,0x1d,0xc6,0x43,0x00,0x00,0x00,0x00,0x30,0x89,0x7f,0x43,0x00,0x00,0x00,
2000 0x00,0x08,0x7a,0x89,0xe5,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7a,0x89,0xe5,0x42,0x00,0x00,
2001 0x00,0x00,0x30,0x89,0x7f,0x43,0x08,0x00,0x00,0x00,0x00,0xa2,0x1d,0xc6,0x43,0x7a,0x89,0xe5,0x42,0x00,
2002 0x80,0xff,0x43,0x30,0x89,0x7f,0x43,0x00,0x80,0xff,0x43,0x09,0x06,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
2003 0x43,0x08,0x16,0x68,0xb3,0x43,0x72,0x87,0xdd,0x43,0x71,0x87,0xdd,0x43,0x17,0x68,0xb3,0x43,0x71,0x87,
2004 0xdd,0x43,0x30,0x89,0x7f,0x43,0x08,0x71,0x87,0xdd,0x43,0xd2,0x2f,0x18,0x43,0x16,0x68,0xb3,0x43,0x35,
2005 0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x35,0xe2,0x87,0x42,0x08,0xd1,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,
2006 0x35,0xe2,0x87,0x42,0xd2,0x2f,0x18,0x43,0x35,0xe2,0x87,0x42,0x30,0x89,0x7f,0x43,0x08,0x35,0xe2,0x87,
2007 0x42,0x17,0x68,0xb3,0x43,0xd1,0x2f,0x18,0x43,0x72,0x87,0xdd,0x43,0x30,0x89,0x7f,0x43,0x72,0x87,0xdd,
2008 0x43,0x09,0x06,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x07,0xa4,0x3f,0x7f,0x43,0x0b,0x86,0xdc,0x43,
2009 0x07,0x6c,0xb9,0xb2,0x43,0xe8,0xd1,0xca,0x42,0x07,0x6e,0x4d,0xa0,0x42,0xa9,0x10,0x9c,0x43,0x07,0xb7,
2010 0x40,0xd7,0x43,0xa9,0x10,0x9c,0x43,0x07,0x79,0xcb,0x11,0x43,0x62,0xbf,0xd7,0x42,0x09,0x06,0x98,0x42,
2011 0x74,0x43,0xb1,0x8d,0x68,0x43,0x08,0xd7,0x24,0x79,0x43,0xba,0x83,0x6e,0x43,0xa9,0x16,0x7c,0x43,0x56,
2012 0xa1,0x76,0x43,0x74,0x2a,0x7d,0x43,0x44,0x73,0x80,0x43,0x08,0x55,0xd1,0x7e,0x43,0xe3,0xea,0x76,0x43,
2013 0xbc,0x18,0x81,0x43,0x7f,0xa8,0x6e,0x43,0x8f,0x0a,0x84,0x43,0x02,0xfc,0x68,0x43,0x09,0x06,0x92,0x29,
2014 0x8d,0x43,0x73,0xc3,0x67,0x43,0x08,0xa4,0xd9,0x8e,0x43,0xf2,0xa6,0x7a,0x43,0x8f,0x22,0x88,0x43,0x75,
2015 0x2a,0x7d,0x43,0x42,0x7f,0x82,0x43,0x08,0xc8,0x88,0x43,0x09,0x06,0xc1,0x79,0x74,0x43,0x50,0x64,0x89,
2016 0x43,0x08,0x68,0x2d,0x72,0x43,0xee,0x21,0x81,0x43,0xcd,0x97,0x55,0x43,0xe6,0xf1,0x7b,0x43,0x91,0xec,
2017 0x5d,0x43,0xa8,0xc7,0x6a,0x43,0x09,0x06,0xfa,0xa5,0x52,0x43,0x60,0x97,0x7c,0x43,0x08,0x19,0xff,0x50,
2018 0x43,0xe9,0x6e,0x8a,0x43,0xb0,0xbd,0x70,0x43,0x4c,0x51,0x82,0x43,0x04,0xeb,0x69,0x43,0x66,0x0f,0x8e,
2019 0x43,0x09,0x06,0x17,0xbf,0x71,0x43,0x2c,0x58,0x94,0x43,0x08,0x1c,0x96,0x6e,0x43,0x61,0x68,0x99,0x43,
2020 0x2d,0x3a,0x6e,0x43,0xc8,0x81,0x9e,0x43,0xb7,0x9b,0x72,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0x30,0xdb,
2021 0x82,0x43,0xdb,0xe9,0x93,0x43,0x08,0x11,0x82,0x84,0x43,0x61,0x68,0x99,0x43,0xe8,0x4a,0x84,0x43,0x8e,
2022 0xa6,0x9e,0x43,0x42,0x7f,0x82,0x43,0x61,0xa4,0xa3,0x43,0x09,0x06,0xc4,0x02,0x85,0x43,0xd1,0x0b,0x92,
2023 0x43,0x08,0xd6,0xb2,0x86,0x43,0x34,0x1e,0x92,0x43,0x4f,0x58,0x87,0x43,0xa4,0xf1,0x92,0x43,0x03,0xd9,
2024 0x87,0x43,0x7b,0xc6,0x94,0x43,0x09,0x06,0x87,0x3e,0x64,0x43,0x31,0x3b,0x93,0x43,0x08,0x3b,0xbf,0x64,
2025 0x43,0x6f,0xf9,0x91,0x43,0x96,0x0b,0x67,0x43,0xc5,0x4a,0x91,0x43,0xcf,0xfe,0x6a,0x43,0x31,0x2f,0x91,
2026 0x43,0x09,0x06,0x16,0x74,0xb5,0x43,0x08,0xec,0x8e,0x43,0x08,0x1b,0x4b,0xb2,0x43,0xee,0x5d,0x8b,0x43,
2027 0x48,0x4d,0xad,0x43,0x12,0xa6,0x8a,0x43,0xf3,0xd7,0xa7,0x43,0x74,0xb8,0x8a,0x43,0x08,0x8c,0xb2,0xa0,
2028 0x43,0xcd,0xf8,0x8a,0x43,0x68,0x46,0x9b,0x43,0x79,0x8f,0x87,0x43,0x49,0xc9,0x96,0x43,0xe9,0x3e,0x82,
2029 0x43,0x08,0x60,0x5c,0x97,0x43,0xa1,0xde,0x8b,0x43,0x4e,0xa0,0x93,0x43,0x31,0x3b,0x93,0x43,0x9f,0xea,
2030 0x8d,0x43,0x27,0x8d,0x99,0x43,0x08,0x07,0xe0,0x8c,0x43,0x06,0x34,0x9b,0x43,0x38,0xe9,0x8c,0x43,0x46,
2031 0x0a,0x9e,0x43,0x3d,0xcc,0x8b,0x43,0xb2,0x06,0xa2,0x43,0x08,0xf1,0x40,0x8a,0x43,0xb0,0x12,0xa4,0x43,
2032 0x39,0xd1,0x88,0x43,0x76,0x43,0xa6,0x43,0xfa,0x06,0x88,0x43,0xa4,0x75,0xa9,0x43,0x08,0x19,0x6c,0x88,
2033 0x43,0x9f,0x9e,0xac,0x43,0x66,0xeb,0x87,0x43,0x44,0x76,0xb0,0x43,0x6b,0xce,0x86,0x43,0x3b,0xbc,0xb4,
2034 0x43,0x08,0xa9,0x8c,0x85,0x43,0x06,0xd0,0xb5,0x43,0xfa,0xee,0x83,0x43,0x74,0xa3,0xb6,0x43,0x3d,0x90,
2035 0x81,0x43,0x31,0xf6,0xb6,0x43,0x08,0x9d,0x61,0x7d,0x43,0xee,0x48,0xb7,0x43,0x3b,0x1f,0x75,0x43,0xcf,
2036 0xe3,0xb6,0x43,0xee,0x6f,0x6d,0x43,0x68,0xe2,0xb5,0x43,0x08,0xd4,0xed,0x6b,0x43,0x87,0x2f,0xb2,0x43,
2037 0x0e,0xc9,0x6b,0x43,0xa7,0x7c,0xae,0x43,0x98,0xfa,0x67,0x43,0xab,0x53,0xab,0x43,0x08,0x25,0x2c,0x64,
2038 0x43,0x33,0xa2,0xa8,0x43,0x40,0x96,0x61,0x43,0xc3,0xc2,0xa5,0x43,0x64,0xde,0x60,0x43,0xfa,0xa2,0xa2,
2039 0x43,0x08,0xb0,0x5d,0x60,0x43,0x06,0x4c,0x9f,0x43,0x9a,0xca,0x5f,0x43,0x38,0x3d,0x9b,0x43,0x3b,0x8f,
2040 0x5c,0x43,0x85,0xb0,0x98,0x43,0x08,0x42,0x36,0x51,0x43,0x3d,0xf0,0x91,0x43,0xcd,0x4f,0x49,0x43,0xdb,
2041 0xb9,0x8b,0x43,0xe0,0xdb,0x44,0x43,0x42,0x8b,0x84,0x43,0x08,0x7e,0xc9,0x44,0x43,0x8a,0x57,0x8d,0x43,
2042 0xbc,0x6c,0x0f,0x43,0x23,0x62,0x8e,0x43,0xf5,0x17,0x07,0x43,0xc5,0x3e,0x8f,0x43,0x09,0x06,0xe0,0xea,
2043 0x76,0x43,0xab,0xef,0xc5,0x43,0x08,0x12,0x00,0x79,0x43,0xab,0xcb,0xbf,0x43,0x79,0xb9,0x6d,0x43,0x7e,
2044 0x8d,0xba,0x43,0xee,0x6f,0x6d,0x43,0x98,0xeb,0xb5,0x43,0x08,0xe0,0x02,0x7b,0x43,0x5f,0x1c,0xb8,0x43,
2045 0x85,0x2c,0x82,0x43,0xe9,0x65,0xb8,0x43,0xd6,0xb2,0x86,0x43,0xc6,0x05,0xb5,0x43,0x08,0x03,0xcd,0x85,
2046 0x43,0x5a,0x39,0xb9,0x43,0xe4,0x4f,0x81,0x43,0xdb,0xd4,0xbf,0x43,0xdf,0x6c,0x82,0x43,0xbc,0x93,0xc5,
2047 0x43,0x09,0x06,0xf0,0xd0,0x22,0x43,0x5d,0x19,0x08,0x43,0x08,0xbc,0xab,0x49,0x43,0x4a,0x35,0x29,0x43,
2048 0xcb,0xf7,0x65,0x43,0xce,0x37,0x45,0x43,0x0e,0x99,0x63,0x43,0x67,0xc6,0x5c,0x43,0x09,0x06,0x05,0x94,
2049 0xab,0x43,0xc2,0x13,0x04,0x43,0x08,0x9f,0x26,0x98,0x43,0x11,0x42,0x25,0x43,0x97,0x00,0x8a,0x43,0x32,
2050 0x32,0x41,0x43,0xf5,0x2f,0x8b,0x43,0xc7,0xc0,0x58,0x43,0x09,0x06,0x8f,0x85,0x48,0x43,0xe0,0xa8,0x8c,
2051 0x43,0x08,0x55,0xaa,0x48,0x43,0xe0,0xa8,0x8c,0x43,0x6b,0x3d,0x49,0x43,0xc1,0x43,0x8c,0x43,0x31,0x62,
2052 0x49,0x43,0xc1,0x43,0x8c,0x43,0x08,0x2f,0xe3,0x2f,0x43,0xad,0xe7,0x98,0x43,0xff,0x0d,0x0d,0x43,0xad,
2053 0xf3,0x9a,0x43,0xf0,0xaf,0xcc,0x42,0x74,0x00,0x97,0x43,0x08,0xbb,0xa2,0xf7,0x42,0x93,0x4d,0x93,0x43,
2054 0x5e,0x19,0x08,0x43,0x5a,0x2a,0x87,0x43,0x23,0x6e,0x10,0x43,0x42,0x97,0x86,0x43,0x08,0xca,0xe8,0x33,
2055 0x43,0x1b,0x3c,0x80,0x43,0x80,0xe8,0x4d,0x43,0xda,0xf4,0x70,0x43,0xae,0x0e,0x4f,0x43,0x2b,0x1b,0x65,
2056 0x43,0x08,0x66,0x96,0x54,0x43,0xa3,0xe1,0x3b,0x43,0x4e,0xc4,0x19,0x43,0xa0,0x1a,0x16,0x43,0x10,0xe2,
2057 0x14,0x43,0x26,0x14,0xe0,0x42,0x08,0x5c,0x91,0x1c,0x43,0xcb,0x27,0xee,0x42,0xa9,0x40,0x24,0x43,0x71,
2058 0x3b,0xfc,0x42,0xf3,0xef,0x2b,0x43,0x8b,0x27,0x05,0x43,0x08,0xe2,0x4b,0x2c,0x43,0x48,0x86,0x07,0x43,
2059 0x79,0x62,0x2f,0x43,0x05,0xe5,0x09,0x43,0x55,0x32,0x34,0x43,0xa0,0xd2,0x09,0x43,0x08,0x74,0xa3,0x36,
2060 0x43,0x3a,0xd1,0x08,0x43,0x7e,0x81,0x38,0x43,0x09,0xd4,0x0a,0x43,0x0d,0xba,0x39,0x43,0xa0,0xea,0x0d,
2061 0x43,0x08,0x6f,0xe4,0x3d,0x43,0x43,0xc7,0x0e,0x43,0xd6,0xe5,0x3e,0x43,0xc4,0x4a,0x11,0x43,0x55,0x7a,
2062 0x40,0x43,0x59,0x72,0x13,0x43,0x08,0x55,0x92,0x44,0x43,0xbf,0x73,0x14,0x43,0x23,0x95,0x46,0x43,0xa5,
2063 0x09,0x17,0x43,0xe0,0xf3,0x48,0x43,0xfe,0x55,0x19,0x43,0x08,0xcd,0x4f,0x49,0x43,0xaa,0x10,0x1c,0x43,
2064 0x61,0x77,0x4b,0x43,0xfe,0x6d,0x1d,0x43,0x80,0xe8,0x4d,0x43,0x2b,0x94,0x1e,0x43,0x08,0x58,0xc9,0x51,
2065 0x43,0x41,0x27,0x1f,0x43,0x9b,0x82,0x53,0x43,0x35,0x72,0x20,0x43,0x53,0xf2,0x54,0x43,0x88,0xcf,0x21,
2066 0x43,0x08,0x7b,0x29,0x55,0x43,0xe8,0x0a,0x25,0x43,0xb2,0x2d,0x58,0x43,0xef,0xe8,0x26,0x43,0x9b,0xb2,
2067 0x5b,0x43,0xd0,0x8f,0x28,0x43,0x08,0x5f,0xef,0x5f,0x43,0xeb,0x11,0x2a,0x43,0xfd,0xdc,0x5f,0x43,0x6e,
2068 0x95,0x2c,0x43,0x3b,0xa7,0x60,0x43,0x2b,0xf4,0x2e,0x43,0x08,0x06,0xbb,0x61,0x43,0xfd,0xe5,0x31,0x43,
2069 0xe7,0x61,0x63,0x43,0xef,0x30,0x33,0x43,0x53,0x52,0x65,0x43,0xa3,0xb1,0x33,0x43,0x08,0x12,0xa0,0x68,
2070 0x43,0x7f,0x69,0x34,0x43,0x40,0xc6,0x69,0x43,0x64,0xff,0x36,0x43,0x7e,0x90,0x6a,0x43,0x71,0xcc,0x39,
2071 0x43,0x08,0xbc,0x5a,0x6b,0x43,0x51,0x73,0x3b,0x43,0xc1,0x49,0x6c,0x43,0xa5,0xd0,0x3c,0x43,0xe0,0xba,
2072 0x6e,0x43,0xb8,0x74,0x3c,0x43,0x08,0x6b,0x1c,0x73,0x43,0x13,0xc1,0x3e,0x43,0x40,0xf6,0x71,0x43,0xce,
2073 0x1f,0x41,0x43,0x55,0x89,0x72,0x43,0x8d,0x7e,0x43,0x43,0x08,0x68,0x2d,0x72,0x43,0x89,0xae,0x4b,0x43,
2074 0xc1,0x79,0x74,0x43,0xcb,0x78,0x4c,0x43,0x55,0xa1,0x76,0x43,0x5b,0xb1,0x4d,0x43,0x08,0xa2,0x38,0x7a,
2075 0x43,0xd1,0x56,0x4e,0x43,0x85,0xb6,0x78,0x43,0xb1,0x15,0x54,0x43,0x83,0xc7,0x77,0x43,0x89,0x0e,0x5c,
2076 0x43,0x08,0xcf,0x46,0x77,0x43,0x0f,0x81,0x5f,0x43,0x1a,0xde,0x7a,0x43,0xce,0xc7,0x5d,0x43,0x42,0x73,
2077 0x80,0x43,0x99,0xc3,0x5a,0x43,0x08,0x85,0x2c,0x82,0x43,0xf6,0xe6,0x59,0x43,0x81,0x3d,0x81,0x43,0x16,
2078 0x10,0x50,0x43,0xd6,0x8e,0x80,0x43,0x5b,0x99,0x49,0x43,0x08,0xc4,0xea,0x80,0x43,0x22,0x95,0x46,0x43,
2079 0xfa,0xe2,0x81,0x43,0xda,0xec,0x43,0x43,0x78,0x77,0x83,0x43,0xe4,0xb2,0x41,0x43,0x08,0x8a,0x27,0x85,
2080 0x43,0x86,0x77,0x3e,0x43,0x0c,0x9f,0x85,0x43,0x07,0xf4,0x3b,0x43,0x8f,0x16,0x86,0x43,0xe6,0x82,0x39,
2081 0x43,0x08,0x85,0x44,0x86,0x43,0x37,0xd9,0x35,0x43,0x1e,0x4f,0x87,0x43,0xe1,0x7b,0x34,0x43,0xdf,0x90,
2082 0x88,0x43,0xb6,0x55,0x33,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0xe5,0x31,0x43,0xfa,0x12,0x8a,0x43,0xbf,
2083 0x03,0x2d,0x43,0x19,0x78,0x8a,0x43,0x45,0x5e,0x2c,0x43,0x08,0x03,0xf1,0x8b,0x43,0xac,0x47,0x29,0x43,
2084 0x2f,0x17,0x8d,0x43,0x45,0x46,0x28,0x43,0xc8,0x21,0x8e,0x43,0x30,0xb3,0x27,0x43,0x08,0xa9,0xc8,0x8f,
2085 0x43,0xef,0xe8,0x26,0x43,0xbf,0x5b,0x90,0x43,0x5b,0xc1,0x24,0x43,0x10,0xca,0x90,0x43,0xa0,0x62,0x22,
2086 0x43,0x08,0x26,0x5d,0x91,0x43,0xbb,0xcc,0x1f,0x43,0xf0,0x70,0x92,0x43,0x78,0x13,0x1e,0x43,0x77,0xd7,
2087 0x93,0x43,0x73,0x24,0x1d,0x43,0x08,0x65,0x3f,0x96,0x43,0xce,0x58,0x1b,0x43,0xbe,0x7f,0x96,0x43,0xbf,
2088 0x8b,0x18,0x43,0x60,0x5c,0x97,0x43,0xb6,0xad,0x16,0x43,0x08,0xba,0xa8,0x99,0x43,0x78,0xcb,0x11,0x43,
2089 0x49,0xe1,0x9a,0x43,0x78,0xcb,0x11,0x43,0x01,0x51,0x9c,0x43,0x73,0xdc,0x10,0x43,0x08,0x72,0x24,0x9d,
2090 0x43,0xd2,0xff,0x0f,0x43,0x1c,0xd3,0x9d,0x43,0x07,0xec,0x0e,0x43,0xeb,0xc9,0x9d,0x43,0xe8,0x7a,0x0c,
2091 0x43,0x08,0x60,0x80,0x9d,0x43,0xd7,0xbe,0x08,0x43,0x4d,0xe8,0x9f,0x43,0x86,0x50,0x08,0x43,0x25,0xbd,
2092 0xa1,0x43,0x5b,0x2a,0x07,0x43,0x08,0x99,0x7f,0xa3,0x43,0xc9,0xf1,0x05,0x43,0x48,0x1d,0xa5,0x43,0x86,
2093 0x38,0x04,0x43,0x6c,0x71,0xa6,0x43,0x18,0x59,0x01,0x43,0x08,0x32,0x96,0xa6,0x43,0x6e,0x64,0xff,0x42,
2094 0x48,0x29,0xa7,0x43,0xed,0xcf,0xfd,0x42,0x5f,0xbc,0xa7,0x43,0x71,0x3b,0xfc,0x42,0x08,0xf3,0xe3,0xa9,
2095 0x43,0xf7,0x7d,0xf7,0x42,0xd8,0x6d,0xaa,0x43,0x45,0xe5,0xf2,0x42,0x48,0x41,0xab,0x43,0xcb,0x27,0xee,
2096 0x42,0x08,0x24,0xf9,0xab,0x43,0x52,0x6a,0xe9,0x42,0xee,0x0c,0xad,0x43,0x4c,0x8c,0xe7,0x42,0x1b,0x33,
2097 0xae,0x43,0xcc,0xf7,0xe5,0x42,0x08,0xaa,0x6b,0xaf,0x43,0xe8,0x61,0xe3,0x42,0x90,0xf5,0xaf,0x43,0xc9,
2098 0xf0,0xe0,0x42,0xe0,0x63,0xb0,0x43,0xe5,0x5a,0xde,0x42,0x08,0xaa,0x83,0xb3,0x43,0x29,0x2d,0x09,0x43,
2099 0x6a,0xfe,0x8e,0x43,0xb8,0x74,0x3c,0x43,0xd5,0x06,0x95,0x43,0xe6,0x79,0x67,0x43,0x08,0x2f,0x53,0x97,
2100 0x43,0xe9,0xb0,0x74,0x43,0xa8,0x28,0xa0,0x43,0x43,0xfd,0x76,0x43,0x83,0x28,0xad,0x43,0x17,0x59,0x81,
2101 0x43,0x08,0x3d,0xe7,0xbf,0x43,0x4b,0x8d,0x8c,0x43,0xae,0x96,0xba,0x43,0x66,0x27,0x92,0x43,0x15,0xe0,
2102 0xc7,0x43,0x6f,0x11,0x96,0x43,0x08,0x7e,0x5d,0xb2,0x43,0xdb,0x01,0x98,0x43,0x9e,0x56,0xa0,0x43,0x80,
2103 0xc1,0x97,0x43,0x69,0x2e,0x97,0x43,0x31,0x17,0x8d,0x43,0x09,0x06,0xab,0xa7,0x39,0x43,0x67,0x0f,0x0e,
2104 0x43,0x08,0xdb,0xbc,0x3b,0x43,0xe8,0x92,0x10,0x43,0xb5,0x85,0x3b,0x43,0x97,0x3c,0x14,0x43,0xab,0xa7,
2105 0x39,0x43,0x0c,0x0b,0x18,0x43,0x09,0x06,0xca,0x30,0x40,0x43,0x30,0x3b,0x13,0x43,0x08,0x17,0xc8,0x43,
2106 0x43,0xa5,0x09,0x17,0x43,0x7e,0xc9,0x44,0x43,0x1a,0xd8,0x1a,0x43,0x9d,0x22,0x43,0x43,0x8d,0xa6,0x1e,
2107 0x43,0x09,0x06,0xc8,0x78,0x4c,0x43,0xed,0xc9,0x1d,0x43,0x08,0x0b,0x32,0x4e,0x43,0x22,0xce,0x20,0x43,
2108 0x23,0xc5,0x4e,0x43,0x58,0xd2,0x23,0x43,0x0b,0x32,0x4e,0x43,0x2b,0xc4,0x26,0x43,0x09,0x06,0xec,0x08,
2109 0x58,0x43,0xc7,0xb1,0x26,0x43,0x08,0x02,0x9c,0x58,0x43,0xef,0x00,0x2b,0x43,0xd9,0x64,0x58,0x43,0x02,
2110 0xbd,0x2e,0x43,0x10,0x51,0x57,0x43,0x37,0xc1,0x31,0x43,0x09,0x06,0xcb,0xdf,0x61,0x43,0x4a,0x65,0x31,
2111 0x43,0x08,0xbe,0x2a,0x63,0x43,0xbd,0x33,0x35,0x43,0x32,0xe1,0x62,0x43,0x56,0x4a,0x38,0x43,0xde,0x83,
2112 0x61,0x43,0x3c,0xe0,0x3a,0x43,0x09,0x06,0x1c,0x7e,0x6a,0x43,0x5b,0x39,0x39,0x43,0x08,0x31,0x11,0x6b,
2113 0x43,0x0c,0xd2,0x3d,0x43,0x1c,0x7e,0x6a,0x43,0x13,0xd9,0x42,0x43,0xd9,0xc4,0x68,0x43,0xcb,0x60,0x48,
2114 0x43,0x09,0x06,0xe5,0xc1,0x73,0x43,0x16,0xf8,0x4b,0x43,0x08,0xa6,0xf7,0x72,0x43,0xb1,0xfd,0x4f,0x43,
2115 0x3b,0x07,0x71,0x43,0x4a,0x14,0x53,0x43,0xa2,0xf0,0x6d,0x43,0x7c,0x29,0x55,0x43,0x09,0x06,0x00,0x8d,
2116 0xa6,0x43,0xef,0x21,0x01,0x43,0x08,0x52,0xfb,0xa6,0x43,0xce,0xc8,0x02,0x43,0xe6,0x16,0xa7,0x43,0x51,
2117 0x4c,0x05,0x43,0x3b,0x68,0xa6,0x43,0x4c,0x75,0x08,0x43,0x09,0x06,0xde,0x20,0xa1,0x43,0x86,0x50,0x08,
2118 0x43,0x08,0xd4,0x4e,0xa1,0x43,0xd3,0xe7,0x0b,0x43,0xb5,0xe9,0xa0,0x43,0x59,0x5a,0x0f,0x43,0xba,0xcc,
2119 0x9f,0x43,0x54,0x83,0x12,0x43,0x09,0x06,0x77,0xfb,0x99,0x43,0x6c,0x16,0x13,0x43,0x08,0xde,0xfc,0x9a,
2120 0x43,0x4a,0xbd,0x14,0x43,0x06,0x34,0x9b,0x43,0xfe,0x55,0x19,0x43,0x13,0xe9,0x99,0x43,0x41,0x27,0x1f,
2121 0x43,0x09,0x06,0x46,0xce,0x93,0x43,0x26,0xa5,0x1d,0x43,0x08,0xe7,0xaa,0x94,0x43,0xbb,0xcc,0x1f,0x43,
2122 0x18,0xb4,0x94,0x43,0xa8,0x40,0x24,0x43,0xe2,0xbb,0x93,0x43,0x21,0xfe,0x28,0x43,0x09,0x06,0xb1,0x8e,
2123 0x8d,0x43,0xa8,0x58,0x28,0x43,0x08,0x19,0x90,0x8e,0x43,0x54,0x13,0x2b,0x43,0xa4,0xd9,0x8e,0x43,0x84,
2124 0x40,0x31,0x43,0x46,0xaa,0x8d,0x43,0x29,0x24,0x37,0x43,0x09,0x06,0xd6,0xbe,0x88,0x43,0xef,0x30,0x33,
2125 0x43,0x08,0x0c,0xb7,0x89,0x43,0x0e,0xa2,0x35,0x43,0xc0,0x37,0x8a,0x43,0x7a,0xaa,0x3b,0x43,0xbb,0x48,
2126 0x89,0x43,0xbb,0x7b,0x41,0x43,0x09,0x06,0x3a,0xad,0x82,0x43,0xc4,0x59,0x43,0x43,0x08,0xd2,0xb7,0x83,
2127 0x43,0x2b,0x5b,0x44,0x43,0x35,0xd6,0x85,0x43,0x48,0xf5,0x49,0x43,0x42,0x97,0x86,0x43,0xc4,0xa1,0x4f,
2128 0x43,0x09,0x06,0x9c,0xb3,0x80,0x43,0x48,0x55,0x5a,0x43,0x08,0xff,0xc5,0x80,0x43,0x09,0x73,0x55,0x43,
2129 0x93,0xe1,0x80,0x43,0x0f,0x39,0x53,0x43,0xf1,0xbe,0x7e,0x43,0x18,0xe7,0x4c,0x43,0x09,0x06,0xe0,0x02,
2130 0x7b,0x43,0x92,0xec,0x5d,0x43,0x08,0x09,0x3a,0x7b,0x43,0xf0,0xf7,0x58,0x43,0x09,0x3a,0x7b,0x43,0xe6,
2131 0x31,0x5b,0x43,0xe0,0x02,0x7b,0x43,0xa8,0x4f,0x56,0x43,0x09,0x06,0x39,0x4f,0x7d,0x43,0x3e,0x8f,0x5c,
2132 0x43,0x08,0xe9,0xe0,0x7c,0x43,0x03,0x9c,0x58,0x43,0x1e,0x2b,0x81,0x43,0x7f,0x30,0x5a,0x43,0xff,0x73,
2133 0x7d,0x43,0xf6,0xb6,0x51,0x43,0x09,0x06,0x5c,0xb8,0x52,0x43,0x28,0x21,0x87,0x43,0x08,0xae,0x3e,0x57,
2134 0x43,0x12,0x9a,0x88,0x43,0x23,0xf5,0x56,0x43,0x04,0xf1,0x8b,0x43,0x25,0xfc,0x5b,0x43,0x85,0x74,0x8e,
2135 0x43,0x08,0x2f,0xf2,0x61,0x43,0x8e,0x52,0x90,0x43,0xd9,0xdc,0x6c,0x43,0x85,0x74,0x8e,0x43,0xc6,0x20,
2136 0x69,0x43,0x3d,0xd8,0x8d,0x43,0x08,0x6d,0x8c,0x5a,0x43,0xf5,0x3b,0x8d,0x43,0x3d,0x77,0x58,0x43,0xa1,
2137 0xc6,0x87,0x43,0xf8,0xed,0x5e,0x43,0x5e,0x0d,0x86,0x43,0x09,0x06,0xde,0xcc,0x92,0x43,0xf7,0x17,0x87,
2138 0x43,0x08,0xb6,0x89,0x90,0x43,0xae,0x87,0x88,0x43,0x4a,0xa5,0x90,0x43,0xa1,0xde,0x8b,0x43,0xf9,0x2a,
2139 0x8e,0x43,0x23,0x62,0x8e,0x43,0x08,0xf5,0x2f,0x8b,0x43,0x5c,0x49,0x90,0x43,0x35,0xd6,0x85,0x43,0x8e,
2140 0x46,0x8e,0x43,0x3d,0xb4,0x87,0x43,0x47,0xaa,0x8d,0x43,0x08,0x6a,0xfe,0x8e,0x43,0xff,0x0d,0x8d,0x43,
2141 0xbb,0x6c,0x8f,0x43,0xf7,0x17,0x87,0x43,0x5c,0x31,0x8c,0x43,0xb2,0x5e,0x85,0x43,0x09,0x06,0x60,0x38,
2142 0x91,0x43,0x69,0x5d,0x7a,0x43,0x08,0x34,0x1e,0x92,0x43,0x1e,0x5b,0x89,0x43,0x04,0x63,0x7e,0x43,0x5e,
2143 0x01,0x84,0x43,0x59,0x2a,0x87,0x43,0x0d,0xcf,0x8d,0x43,0x09,0x03,0x04,0x06,0x5a,0x18,0x63,0x43,0x82,
2144 0x79,0x8b,0x43,0x08,0x25,0x2c,0x64,0x43,0x82,0x79,0x8b,0x43,0x2a,0x1b,0x65,0x43,0x9d,0xef,0x8a,0x43,
2145 0x2a,0x1b,0x65,0x43,0xc1,0x37,0x8a,0x43,0x08,0x2a,0x1b,0x65,0x43,0x17,0x89,0x89,0x43,0x25,0x2c,0x64,
2146 0x43,0x31,0xff,0x88,0x43,0x5a,0x18,0x63,0x43,0x31,0xff,0x88,0x43,0x08,0xf3,0x16,0x62,0x43,0x31,0xff,
2147 0x88,0x43,0xee,0x27,0x61,0x43,0x17,0x89,0x89,0x43,0xee,0x27,0x61,0x43,0xc1,0x37,0x8a,0x43,0x08,0xee,
2148 0x27,0x61,0x43,0x9d,0xef,0x8a,0x43,0xf3,0x16,0x62,0x43,0x82,0x79,0x8b,0x43,0x5a,0x18,0x63,0x43,0x82,
2149 0x79,0x8b,0x43,0x09,0x06,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x08,0x34,0xee,0x89,0x43,0x82,0x79,
2150 0x8b,0x43,0x85,0x5c,0x8a,0x43,0x9d,0xef,0x8a,0x43,0x85,0x5c,0x8a,0x43,0xc1,0x37,0x8a,0x43,0x08,0x85,
2151 0x5c,0x8a,0x43,0x17,0x89,0x89,0x43,0x34,0xee,0x89,0x43,0x31,0xff,0x88,0x43,0x4f,0x64,0x89,0x43,0x31,
2152 0xff,0x88,0x43,0x08,0x9c,0xe3,0x88,0x43,0x31,0xff,0x88,0x43,0x19,0x6c,0x88,0x43,0x17,0x89,0x89,0x43,
2153 0x19,0x6c,0x88,0x43,0xc1,0x37,0x8a,0x43,0x08,0x19,0x6c,0x88,0x43,0x9d,0xef,0x8a,0x43,0x9c,0xe3,0x88,
2154 0x43,0x82,0x79,0x8b,0x43,0x4f,0x64,0x89,0x43,0x82,0x79,0x8b,0x43,0x09,0x02,0x04,0x06,0x19,0x60,0x86,
2155 0x43,0xec,0xed,0xa3,0x43,0x08,0x35,0xd6,0x85,0x43,0x76,0x43,0xa6,0x43,0x93,0xe1,0x80,0x43,0x57,0x02,
2156 0xac,0x43,0x61,0xd8,0x80,0x43,0x87,0x17,0xae,0x43,0x08,0xa5,0x85,0x80,0x43,0xc3,0xfe,0xaf,0x43,0xce,
2157 0xbc,0x80,0x43,0x83,0x40,0xb1,0x43,0xa5,0x91,0x82,0x43,0x79,0x6e,0xb1,0x43,0x08,0x23,0x26,0x84,0x43,
2158 0x40,0x93,0xb1,0x43,0x30,0xe7,0x84,0x43,0xbe,0x1b,0xb1,0x43,0x11,0x82,0x84,0x43,0xab,0x6b,0xaf,0x43,
2159 0x08,0xb7,0x41,0x84,0x43,0x3b,0x98,0xae,0x43,0xb7,0x41,0x84,0x43,0xc3,0xf2,0xad,0x43,0xa1,0xae,0x83,
2160 0x43,0x83,0x28,0xad,0x43,0x08,0xb2,0x52,0x83,0x43,0x80,0x39,0xac,0x43,0x81,0x49,0x83,0x43,0xf0,0x00,
2161 0xab,0x43,0xe4,0x67,0x85,0x43,0x76,0x4f,0xa8,0x43,0x08,0x9c,0xd7,0x86,0x43,0xd1,0x83,0xa6,0x43,0xec,
2162 0x45,0x87,0x43,0x01,0x75,0xa2,0x43,0x19,0x60,0x86,0x43,0xec,0xed,0xa3,0x43,0x09,0x06,0xd9,0xdc,0x6c,
2163 0x43,0x14,0x25,0xa4,0x43,0x08,0xa2,0xf0,0x6d,0x43,0x9f,0x7a,0xa6,0x43,0x47,0xec,0x77,0x43,0x80,0x39,
2164 0xac,0x43,0xa9,0xfe,0x77,0x43,0xb0,0x4e,0xae,0x43,0x08,0x23,0xa4,0x78,0x43,0xea,0x35,0xb0,0x43,0xd2,
2165 0x35,0x78,0x43,0xab,0x77,0xb1,0x43,0xc1,0x79,0x74,0x43,0xa2,0xa5,0xb1,0x43,0x08,0xc6,0x50,0x71,0x43,
2166 0x68,0xca,0xb1,0x43,0xab,0xce,0x6f,0x43,0xe7,0x52,0xb1,0x43,0xea,0x98,0x70,0x43,0xd4,0xa2,0xaf,0x43,
2167 0x08,0x9d,0x19,0x71,0x43,0x96,0xd8,0xae,0x43,0x9d,0x19,0x71,0x43,0xec,0x29,0xae,0x43,0xca,0x3f,0x72,
2168 0x43,0xab,0x5f,0xad,0x43,0x08,0xa6,0xf7,0x72,0x43,0xa7,0x70,0xac,0x43,0x09,0x0a,0x73,0x43,0x17,0x38,
2169 0xab,0x43,0x44,0xcd,0x6e,0x43,0x9f,0x86,0xa8,0x43,0x08,0xd4,0xed,0x6b,0x43,0xf8,0xba,0xa6,0x43,0x31,
2170 0x11,0x6b,0x43,0x2a,0xac,0xa2,0x43,0xd9,0xdc,0x6c,0x43,0x14,0x25,0xa4,0x43,0x09,0x01,0x05,0x06,0x66,
2171 0x5d,0x7a,0x43,0x74,0xeb,0xc2,0x43,0x08,0x09,0x22,0x77,0x43,0x50,0xbb,0xc7,0x43,0xe9,0xe0,0x7c,0x43,
2172 0xf5,0x86,0xc9,0x43,0x8f,0x94,0x7a,0x43,0xc5,0x95,0xcd,0x43,0x09,0x06,0x08,0x98,0x80,0x43,0x6b,0x19,
2173 0xc3,0x43,0x08,0xb7,0x35,0x82,0x43,0x79,0xf2,0xc7,0x43,0xf1,0xbe,0x7e,0x43,0x1e,0xbe,0xc9,0x43,0x73,
2174 0x7c,0x80,0x43,0xec,0xcc,0xcd,0x43,0x09,0x06,0x28,0xab,0x7d,0x43,0xae,0xde,0xc6,0x43,0x08,0x1e,0xcd,
2175 0x7b,0x43,0x8a,0xa2,0xc9,0x43,0x30,0x89,0x7f,0x43,0x5c,0x94,0xcc,0x43,0x28,0xab,0x7d,0x43,0x42,0x2a,
2176 0xcf,0x43,0x09,0x01,0x05,0x06,0x24,0x14,0xe0,0x42,0xf5,0x77,0x97,0x43,0x08,0xf7,0x1d,0xe7,0x42,0x74,
2177 0x00,0x97,0x43,0x4d,0x93,0xec,0x42,0xdb,0xf5,0x95,0x43,0x29,0x4b,0xed,0x42,0xcd,0x34,0x95,0x43,0x09,
2178 0x06,0x29,0x7b,0xf5,0x42,0x6f,0x1d,0x98,0x43,0x08,0xe4,0xf1,0xfb,0x42,0x61,0x5c,0x97,0x43,0xdb,0x7d,
2179 0x01,0x43,0xb2,0xbe,0x95,0x43,0x55,0x23,0x02,0x43,0xe7,0xaa,0x94,0x43,0x09,0x06,0x98,0xdc,0x03,0x43,
2180 0xbe,0x8b,0x98,0x43,0x08,0x66,0xdf,0x05,0x43,0x47,0xe6,0x97,0x43,0xae,0x87,0x08,0x43,0x98,0x48,0x96,
2181 0x43,0x61,0x08,0x09,0x43,0xd6,0x06,0x95,0x43,0x09,0x06,0x31,0x0b,0x0b,0x43,0x8e,0x82,0x98,0x43,0x08,
2182 0xdb,0xc5,0x0d,0x43,0x80,0xc1,0x97,0x43,0xd6,0xee,0x10,0x43,0xa9,0xec,0x95,0x43,0x79,0xcb,0x11,0x43,
2183 0x55,0x8f,0x94,0x43,0x09,0x06,0xd1,0x2f,0x18,0x43,0xdb,0x01,0x98,0x43,0x08,0xad,0xe7,0x18,0x43,0x38,
2184 0x25,0x97,0x43,0x8a,0x9f,0x19,0x43,0x80,0xb5,0x95,0x43,0xd6,0x1e,0x19,0x43,0xe0,0xd8,0x94,0x43,0x09,
2185 0x06,0x9a,0x5b,0x1d,0x43,0x58,0x8a,0x97,0x43,0x08,0x01,0x5d,0x1e,0x43,0xf1,0x88,0x96,0x43,0x2f,0x83,
2186 0x1f,0x43,0x19,0xb4,0x94,0x43,0x19,0xf0,0x1e,0x43,0x6f,0x05,0x94,0x43,0x09,0x06,0x0b,0x53,0x24,0x43,
2187 0xae,0xdb,0x96,0x43,0x08,0x25,0xd5,0x25,0x43,0x50,0xac,0x95,0x43,0x53,0xfb,0x26,0x43,0x8a,0x7b,0x93,
2188 0x43,0x76,0x43,0x26,0x43,0xb7,0x95,0x92,0x43,0x09,0x06,0x76,0x5b,0x2a,0x43,0x47,0xda,0x95,0x43,0x08,
2189 0xf3,0xef,0x2b,0x43,0x10,0xe2,0x94,0x43,0x6d,0x95,0x2c,0x43,0xae,0xc3,0x92,0x43,0x68,0xa6,0x2b,0x43,
2190 0x47,0xc2,0x91,0x43,0x09,0x06,0x36,0xc1,0x31,0x43,0x2c,0x58,0x94,0x43,0x08,0x8c,0x1e,0x33,0x43,0x31,
2191 0x3b,0x93,0x43,0x79,0x7a,0x33,0x43,0xff,0x25,0x91,0x43,0xd9,0x9d,0x32,0x43,0xc1,0x5b,0x90,0x43,0x09,
2192 0x06,0x25,0x35,0x36,0x43,0x31,0x3b,0x93,0x43,0x08,0x3f,0xb7,0x37,0x43,0xc1,0x67,0x92,0x43,0xe0,0x93,
2193 0x38,0x43,0xae,0xb7,0x90,0x43,0x7e,0x81,0x38,0x43,0x0d,0xdb,0x8f,0x43,0x09,0x06,0xb5,0x85,0x3b,0x43,
2194 0xe4,0xaf,0x91,0x43,0x08,0xcf,0x07,0x3d,0x43,0x9d,0x13,0x91,0x43,0xbc,0x63,0x3d,0x43,0x47,0xb6,0x8f,
2195 0x43,0xe5,0x9a,0x3d,0x43,0x74,0xd0,0x8e,0x43,0x09,0x06,0xae,0xc6,0x42,0x43,0xa4,0xd9,0x8e,0x43,0x08,
2196 0xca,0x48,0x44,0x43,0xfa,0x2a,0x8e,0x43,0xa2,0x11,0x44,0x43,0x9d,0xfb,0x8c,0x43,0x55,0x92,0x44,0x43,
2197 0x0d,0xc3,0x8b,0x43,0x09,0x06,0x39,0x10,0xc3,0x43,0x34,0x36,0x96,0x43,0x08,0x92,0x44,0xc1,0x43,0xe4,
2198 0xc7,0x95,0x43,0x6f,0xf0,0xbf,0x43,0x4b,0xbd,0x94,0x43,0x47,0xb9,0xbf,0x43,0x0b,0xf3,0x93,0x43,0x09,
2199 0x06,0x8f,0x49,0xbe,0x43,0xb7,0xad,0x96,0x43,0x08,0x11,0xb5,0xbc,0x43,0x77,0xe3,0x95,0x43,0x9c,0xf2,
2200 0xba,0x43,0xfa,0x4e,0x94,0x43,0xae,0x96,0xba,0x43,0x31,0x3b,0x93,0x43,0x09,0x06,0xdb,0xb0,0xb9,0x43,
2201 0x10,0xee,0x96,0x43,0x08,0x42,0xa6,0xb8,0x43,0xc8,0x51,0x96,0x43,0x50,0x5b,0xb7,0x43,0x19,0xb4,0x94,
2202 0x43,0xf7,0x1a,0xb7,0x43,0x58,0x72,0x93,0x43,0x09,0x06,0xf2,0x2b,0xb6,0x43,0x10,0xee,0x96,0x43,0x08,
2203 0x9d,0xce,0xb4,0x43,0x04,0x2d,0x96,0x43,0xed,0x30,0xb3,0x43,0x2c,0x58,0x94,0x43,0xce,0xcb,0xb2,0x43,
2204 0xd6,0xfa,0x92,0x43,0x09,0x06,0x5a,0x09,0xb1,0x43,0x19,0xc0,0x96,0x43,0x08,0x6c,0xad,0xb0,0x43,0x77,
2205 0xe3,0x95,0x43,0x7e,0x51,0xb0,0x43,0xc0,0x73,0x94,0x43,0xd8,0x91,0xb0,0x43,0x1e,0x97,0x93,0x43,0x09,
2206 0x06,0x48,0x4d,0xad,0x43,0xbe,0x7f,0x96,0x43,0x08,0x95,0xcc,0xac,0x43,0x58,0x7e,0x95,0x43,0x4d,0x30,
2207 0xac,0x43,0x80,0xa9,0x93,0x43,0xd8,0x79,0xac,0x43,0xd6,0xfa,0x92,0x43,0x09,0x06,0x90,0xd1,0xa9,0x43,
2208 0x14,0xd1,0x95,0x43,0x08,0x83,0x10,0xa9,0x43,0xb7,0xa1,0x94,0x43,0x3b,0x74,0xa8,0x43,0xf1,0x70,0x92,
2209 0x43,0x29,0xd0,0xa8,0x43,0x1e,0x8b,0x91,0x43,0x09,0x06,0x5a,0xcd,0xa6,0x43,0x8a,0x87,0x95,0x43,0x08,
2210 0x1c,0x03,0xa6,0x43,0x23,0x86,0x94,0x43,0x5f,0xb0,0xa5,0x43,0xc1,0x67,0x92,0x43,0xe1,0x27,0xa6,0x43,
2211 0x8a,0x6f,0x91,0x43,0x09,0x06,0xd4,0x5a,0xa3,0x43,0x2c,0x58,0x94,0x43,0x08,0x29,0xac,0xa2,0x43,0x31,
2212 0x3b,0x93,0x43,0x32,0x7e,0xa2,0x43,0xff,0x25,0x91,0x43,0x83,0xec,0xa2,0x43,0x8e,0x52,0x90,0x43,0x09,
2213 0x06,0xf8,0x96,0xa0,0x43,0x1e,0x97,0x93,0x43,0x08,0xeb,0xd5,0x9f,0x43,0x7b,0xba,0x92,0x43,0x99,0x67,
2214 0x9f,0x43,0x9d,0x13,0x91,0x43,0x99,0x67,0x9f,0x43,0xfa,0x36,0x90,0x43,0x09,0x06,0xeb,0xc9,0x9d,0x43,
2215 0xc8,0x39,0x92,0x43,0x08,0xde,0x08,0x9d,0x43,0xb2,0xa6,0x91,0x43,0xe6,0xda,0x9c,0x43,0x2c,0x40,0x90,
2216 0x43,0x52,0xbf,0x9c,0x43,0x5a,0x5a,0x8f,0x43,0x09,0x06,0x37,0x3d,0x9b,0x43,0x85,0x80,0x90,0x43,0x08,
2217 0x2a,0x7c,0x9a,0x43,0xdb,0xd1,0x8f,0x43,0xf0,0xa0,0x9a,0x43,0x7d,0xa2,0x8e,0x43,0x65,0x57,0x9a,0x43,
2218 0xee,0x69,0x8d,0x43,0x09,0x02,0x04,0x06,0x2a,0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x08,0x0d,0x8a,0x31,
2219 0x42,0x9f,0x0e,0x94,0x43,0xf3,0x1f,0x34,0x42,0x3d,0xfc,0x93,0x43,0x63,0xff,0x36,0x42,0xa9,0xe0,0x93,
2220 0x43,0x08,0xb5,0x34,0x5d,0x42,0x0b,0xf3,0x93,0x43,0x6d,0xa4,0x5e,0x42,0x03,0x39,0x98,0x43,0xe7,0x31,
2221 0x5b,0x42,0x93,0x89,0x9d,0x43,0x08,0x02,0x9c,0x58,0x42,0xd4,0x5a,0xa3,0x43,0x38,0x70,0x53,0x42,0x14,
2222 0x49,0xaa,0x43,0xf8,0xed,0x5e,0x42,0x83,0x28,0xad,0x43,0x08,0xea,0x68,0x68,0x42,0x20,0x22,0xaf,0x43,
2223 0x12,0xb8,0x6c,0x42,0xb5,0x49,0xb1,0x43,0x2a,0x4b,0x6d,0x42,0x0d,0x96,0xb3,0x43,0x07,0x2a,0x4b,0x6d,
2224 0x42,0xc6,0x05,0xb5,0x43,0x08,0x87,0x6e,0x6c,0x42,0x68,0xee,0xb7,0x43,0x1c,0x66,0x66,0x42,0x31,0x0e,
2225 0xbb,0x43,0x57,0x11,0x5e,0x42,0x8f,0x49,0xbe,0x43,0x08,0x66,0x96,0x54,0x42,0xb9,0x5c,0xb8,0x43,0x2c,
2226 0x2b,0x3c,0x42,0x68,0xd6,0xb3,0x43,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x07,0x2a,0xf4,0x2e,0x42,
2227 0x61,0xa4,0xa3,0x43,0x08,0x55,0x1a,0x30,0x42,0xf0,0xd0,0xa2,0x43,0xf8,0xf6,0x30,0x42,0xb2,0x06,0xa2,
2228 0x43,0x98,0xd3,0x31,0x42,0xd6,0x4e,0xa1,0x43,0x08,0x1c,0x6f,0x38,0x42,0x2a,0x94,0x9e,0x43,0xc1,0x22,
2229 0x36,0x42,0xf5,0x9b,0x9d,0x43,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x57,
2230 0xa2,0x9b,0x43,0x08,0xab,0x8f,0x35,0x42,0x8a,0xab,0x9b,0x43,0xe9,0x71,0x3a,0x42,0xb2,0xe2,0x9b,0x43,
2231 0xb7,0x74,0x3c,0x42,0x34,0x5a,0x9c,0x43,0x08,0x23,0x7d,0x42,0x42,0x0b,0x2f,0x9e,0x43,0xe5,0x9a,0x3d,
2232 0x42,0x38,0x6d,0xa3,0x43,0x36,0xd9,0x35,0x42,0xf3,0xd7,0xa7,0x43,0x08,0x12,0x61,0x2e,0x42,0xb0,0x42,
2233 0xac,0x43,0x63,0xff,0x36,0x42,0xdd,0x74,0xaf,0x43,0x1e,0xa6,0x45,0x42,0x44,0x82,0xb2,0x43,0x08,0x74,
2234 0x1b,0x4b,0x42,0x79,0x7a,0xb3,0x43,0x10,0x21,0x4f,0x42,0x2a,0x18,0xb5,0x43,0xdb,0x4c,0x54,0x42,0x91,
2235 0x19,0xb6,0x43,0x08,0xee,0x3f,0x65,0x42,0x5f,0x28,0xba,0x43,0xa7,0xaf,0x66,0x42,0xb9,0x50,0xb6,0x43,
2236 0x14,0x58,0x5c,0x42,0xca,0xdc,0xb1,0x43,0x08,0x2c,0x8b,0x4c,0x42,0x4e,0x30,0xac,0x43,0x19,0xcf,0x48,
2237 0x42,0x2a,0xd0,0xa8,0x43,0xbc,0xab,0x49,0x42,0xa9,0x4c,0xa6,0x43,0x08,0x61,0x5f,0x47,0x42,0xfa,0xa2,
2238 0xa2,0x43,0xa7,0xaf,0x66,0x42,0x85,0x98,0x94,0x43,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x07,0x2a,
2239 0xf4,0x2e,0x42,0x04,0x21,0x94,0x43,0x09,0x06,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x08,0xdc,0xe3,
2240 0xf1,0x41,0xe9,0x9e,0x92,0x43,0xd2,0xe7,0x0b,0x42,0xd6,0x06,0x95,0x43,0x2a,0xf4,0x2e,0x42,0x04,0x21,
2241 0x94,0x43,0x07,0x2a,0xf4,0x2e,0x42,0xc3,0x62,0x95,0x43,0x08,0x87,0x17,0x2e,0x42,0xc3,0x62,0x95,0x43,
2242 0xe7,0x3a,0x2d,0x42,0xf5,0x6b,0x95,0x43,0x44,0x5e,0x2c,0x42,0xf5,0x6b,0x95,0x43,0x08,0xd1,0x47,0x1c,
2243 0x42,0x19,0xc0,0x96,0x43,0x66,0xdf,0x05,0x42,0x38,0x19,0x95,0x43,0x12,0x6a,0x00,0x42,0xb2,0xbe,0x95,
2244 0x43,0x08,0xbb,0x6b,0xea,0x41,0xd6,0x12,0x97,0x43,0x2d,0x82,0xfa,0x41,0x61,0x74,0x9b,0x43,0x7e,0x72,
2245 0x06,0x42,0x8a,0xab,0x9b,0x43,0x08,0xc8,0x39,0x12,0x42,0x4e,0xd0,0x9b,0x43,0x53,0xe3,0x22,0x42,0xc3,
2246 0x86,0x9b,0x43,0x2a,0xf4,0x2e,0x42,0x57,0xa2,0x9b,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6a,0x52,0x9d,0x43,
2247 0x08,0x01,0xa5,0x2a,0x42,0xa4,0x2d,0x9d,0x43,0x96,0x9c,0x24,0x42,0x06,0x40,0x9d,0x43,0x8a,0xb7,0x1d,
2248 0x42,0x9a,0x5b,0x9d,0x43,0x08,0x6b,0x16,0x13,0x42,0xcd,0x64,0x9d,0x43,0x42,0xc7,0x0e,0x42,0x9a,0x5b,
2249 0x9d,0x43,0x23,0x26,0x04,0x42,0xcd,0x64,0x9d,0x43,0x08,0xe6,0x91,0xeb,0x41,0x38,0x49,0x9d,0x43,0x73,
2250 0x7b,0xdb,0x41,0xf5,0x83,0x99,0x43,0x7f,0x60,0xe2,0x41,0x0b,0x0b,0x98,0x43,0x08,0x7f,0x60,0xe2,0x41,
2251 0xec,0x99,0x95,0x43,0xe3,0x5a,0xde,0x41,0xbe,0x7f,0x96,0x43,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,
2252 0x07,0xd0,0xfe,0xea,0x41,0x9f,0x0e,0x94,0x43,0x09,0x06,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x08,
2253 0xd4,0x7e,0x29,0x42,0xab,0x6b,0xaf,0x43,0x4e,0x0c,0x26,0x42,0x44,0x6a,0xae,0x43,0x38,0x79,0x25,0x42,
2254 0xd4,0x96,0xad,0x43,0x08,0x25,0xbd,0x21,0x42,0xe2,0x4b,0xac,0x43,0x49,0x35,0x29,0x42,0x9a,0x97,0xa7,
2255 0x43,0x2a,0xf4,0x2e,0x42,0x61,0xa4,0xa3,0x43,0x07,0x2a,0xf4,0x2e,0x42,0x6d,0xad,0xb0,0x43,0x09,0x06,
2256 0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x08,0x86,0x20,0x80,0x43,0x57,0x41,0xe6,0x43,0x7d,0x4e,0x80,
2257 0x43,0x25,0x38,0xe6,0x43,0xa5,0x85,0x80,0x43,0xf3,0x2e,0xe6,0x43,0x08,0x35,0xca,0x83,0x43,0xd4,0xc9,
2258 0xe5,0x43,0x9c,0xd7,0x86,0x43,0x44,0x91,0xe4,0x43,0xd5,0xca,0x8a,0x43,0x91,0x1c,0xe6,0x43,0x08,0x53,
2259 0x5f,0x8c,0x43,0xf8,0x1d,0xe7,0x43,0x2f,0x17,0x8d,0x43,0x4e,0x7b,0xe8,0x43,0x92,0x29,0x8d,0x43,0x2f,
2260 0x22,0xea,0x43,0x07,0x92,0x29,0x8d,0x43,0x44,0xb5,0xea,0x43,0x08,0xfe,0x0d,0x8d,0x43,0x2a,0x4b,0xed,
2261 0x43,0xe3,0x8b,0x8b,0x43,0x55,0x7d,0xf0,0x43,0xec,0x51,0x89,0x43,0x72,0x0b,0xf4,0x43,0x08,0xcd,0xd4,
2262 0x84,0x43,0x9d,0x55,0xfb,0x43,0xc9,0xe5,0x83,0x43,0x74,0x1e,0xfb,0x43,0x73,0x94,0x84,0x43,0x5a,0x90,
2263 0xf7,0x43,0x08,0xe8,0x62,0x88,0x43,0xfd,0x30,0xee,0x43,0x39,0xc5,0x86,0x43,0xdd,0xbf,0xeb,0x43,0x35,
2264 0xbe,0x81,0x43,0x40,0xde,0xed,0x43,0x08,0x4f,0x34,0x81,0x43,0x36,0x0c,0xee,0x43,0x08,0x98,0x80,0x43,
2265 0xfd,0x30,0xee,0x43,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x40,0xec,
2266 0x43,0x08,0x35,0xbe,0x81,0x43,0x06,0xf7,0xeb,0x43,0x15,0x65,0x83,0x43,0x49,0xa4,0xeb,0x43,0x1e,0x43,
2267 0x85,0x43,0xbe,0x5a,0xeb,0x43,0x08,0xae,0x93,0x8a,0x43,0xfd,0x18,0xea,0x43,0x42,0x97,0x86,0x43,0x5f,
2268 0x67,0xf4,0x43,0xa9,0x98,0x87,0x43,0xd4,0x1d,0xf4,0x43,0x08,0x5c,0x25,0x8a,0x43,0xcf,0x16,0xef,0x43,
2269 0x46,0xaa,0x8d,0x43,0x5a,0x3c,0xe9,0x43,0x19,0x6c,0x88,0x43,0x53,0x5e,0xe7,0x43,0x08,0xc4,0x02,0x85,
2270 0x43,0x96,0x0b,0xe7,0x43,0x85,0x2c,0x82,0x43,0x83,0x67,0xe7,0x43,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,
2271 0x43,0x07,0x1d,0xe5,0x7f,0x43,0x87,0x4a,0xe6,0x43,0x09,0x06,0xfd,0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,
2272 0x08,0xfa,0x6c,0x78,0x43,0xd1,0xc2,0xe0,0x43,0x25,0x5c,0x6c,0x43,0x25,0x44,0xe8,0x43,0x1d,0xe5,0x7f,
2273 0x43,0x87,0x4a,0xe6,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x72,0xc3,0xe7,0x43,0x08,0xa6,0x27,0x7b,0x43,0x91,
2274 0x28,0xe8,0x43,0xbc,0xa2,0x77,0x43,0xb0,0x8d,0xe8,0x43,0xc6,0x68,0x75,0x43,0x57,0x4d,0xe8,0x43,0x08,
2275 0xe0,0xd2,0x72,0x43,0xab,0x9e,0xe7,0x43,0x50,0x9a,0x71,0x43,0x2a,0x27,0xe7,0x43,0xea,0x98,0x70,0x43,
2276 0x57,0x35,0xe4,0x43,0x08,0x94,0x3b,0x6f,0x43,0x14,0x7c,0xe2,0x43,0xff,0x13,0x6d,0x43,0x06,0xbb,0xe1,
2277 0x43,0xcf,0xfe,0x6a,0x43,0x06,0xbb,0xe1,0x43,0x08,0x44,0x9d,0x66,0x43,0x77,0x8e,0xe2,0x43,0x3b,0xef,
2278 0x6c,0x43,0x91,0x10,0xe4,0x43,0xfd,0x24,0x6c,0x43,0xb0,0x81,0xe6,0x43,0x08,0x96,0x23,0x6b,0x43,0xee,
2279 0x57,0xe9,0x43,0xca,0x0f,0x6a,0x43,0x5f,0x37,0xec,0x43,0x55,0x71,0x6e,0x43,0x9f,0x01,0xed,0x43,0x08,
2280 0xdb,0xfb,0x75,0x43,0x3b,0xef,0xec,0x43,0x09,0x3a,0x7b,0x43,0xb0,0xa5,0xec,0x43,0x1d,0xe5,0x7f,0x43,
2281 0x91,0x40,0xec,0x43,0x07,0x1d,0xe5,0x7f,0x43,0x91,0x4c,0xee,0x43,0x08,0xa9,0x16,0x7c,0x43,0xb0,0xb1,
2282 0xee,0x43,0x47,0xec,0x77,0x43,0xd9,0xe8,0xee,0x43,0x1e,0x9d,0x73,0x43,0xcf,0x16,0xef,0x43,0x08,0x0e,
2283 0xc9,0x6b,0x43,0xee,0x7b,0xef,0x43,0x7e,0x90,0x6a,0x43,0xfd,0x30,0xee,0x43,0x01,0xfc,0x68,0x43,0x4e,
2284 0x93,0xec,0x43,0x08,0x31,0xf9,0x66,0x43,0x4e,0x87,0xea,0x43,0x31,0x11,0x6b,0x43,0xd4,0xd5,0xe7,0x43,
2285 0xd9,0xc4,0x68,0x43,0xd4,0xc9,0xe5,0x43,0x08,0xe5,0x79,0x67,0x43,0x77,0x9a,0xe4,0x43,0x44,0x9d,0x66,
2286 0x43,0xab,0x86,0xe3,0x43,0x7e,0x78,0x66,0x43,0x0b,0xaa,0xe2,0x43,0x07,0x7e,0x78,0x66,0x43,0x57,0x29,
2287 0xe2,0x43,0x08,0xa7,0xaf,0x66,0x43,0xbe,0x1e,0xe1,0x43,0x87,0x56,0x68,0x43,0x77,0x82,0xe0,0x43,0xfd,
2288 0x24,0x6c,0x43,0xd9,0x94,0xe0,0x43,0x09,0x06,0xc4,0x41,0xbf,0x43,0x85,0xc0,0x72,0x42,0x08,0x73,0xdf,
2289 0xc0,0x43,0xf4,0x76,0x72,0x42,0x97,0x33,0xc2,0x43,0x85,0xc0,0x72,0x42,0xb2,0xb5,0xc3,0x43,0x64,0x56,
2290 0x75,0x42,0x08,0x03,0x24,0xc4,0x43,0x5e,0x7f,0x78,0x42,0xfa,0x51,0xc4,0x43,0x01,0x85,0x7c,0x42,0x5c,
2291 0x64,0xc4,0x43,0xa0,0xb3,0x80,0x42,0x07,0x5c,0x64,0xc4,0x43,0x10,0x93,0x83,0x42,0x08,0xc8,0x48,0xc4,
2292 0x43,0x1c,0x78,0x8a,0x42,0x27,0x6c,0xc3,0x43,0xaf,0xcf,0x94,0x42,0x23,0x7d,0xc2,0x43,0x99,0x9c,0xa4,
2293 0x42,0x08,0x3d,0xe7,0xbf,0x43,0xfb,0xfd,0xb5,0x42,0xb3,0x9d,0xbf,0x43,0x88,0x17,0xae,0x42,0xc4,0x41,
2294 0xbf,0x43,0x69,0x76,0xa3,0x42,0x07,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x08,0x4f,0x8b,0xbf,0x43,
2295 0xed,0x81,0x91,0x42,0xe4,0xa6,0xbf,0x43,0x5d,0x61,0x94,0x42,0xfa,0x39,0xc0,0x43,0x3b,0x49,0x9d,0x42,
2296 0x08,0x2b,0x43,0xc0,0x43,0x28,0xed,0xa9,0x42,0x61,0x3b,0xc1,0x43,0x00,0x9e,0xa5,0x42,0xe4,0xb2,0xc1,
2297 0x43,0x5d,0x91,0x9c,0x42,0x08,0x78,0xce,0xc1,0x43,0xfd,0x36,0x90,0x42,0x22,0x89,0xc4,0x43,0x81,0x72,
2298 0x86,0x42,0xae,0xc6,0xc2,0x43,0xa0,0xb3,0x80,0x42,0x08,0x54,0x86,0xc2,0x43,0x58,0xd1,0x7e,0x42,0x30,
2299 0x32,0xc1,0x43,0xce,0x5e,0x7b,0x42,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x07,0xc4,0x41,0xbf,0x43,
2300 0x85,0xc0,0x72,0x42,0x09,0x06,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x08,0x35,0xfd,0xbb,0x43,0xa4,
2301 0xa1,0x5c,0x42,0x5e,0x34,0xbc,0x43,0x9d,0x2a,0x70,0x42,0x5e,0x40,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,
2302 0x4c,0x9c,0xbe,0x43,0x0e,0x0a,0x73,0x42,0x08,0xef,0xbe,0x43,0x0e,0x0a,0x73,0x42,0xc4,0x41,0xbf,0x43,
2303 0x85,0xc0,0x72,0x42,0x07,0xc4,0x41,0xbf,0x43,0xe8,0xf1,0x7b,0x42,0x08,0xcd,0x13,0xbf,0x43,0xe8,0xf1,
2304 0x7b,0x42,0xd6,0xe5,0xbe,0x43,0x71,0x3b,0x7c,0x42,0xdf,0xb7,0xbe,0x43,0x71,0x3b,0x7c,0x42,0x08,0x08,
2305 0xe3,0xbc,0x43,0xa4,0x61,0x7d,0x42,0x28,0x3c,0xbb,0x43,0x91,0x45,0x69,0x42,0x28,0x3c,0xbb,0x43,0x58,
2306 0x71,0x6e,0x42,0x08,0xce,0xfb,0xba,0x43,0xd5,0x35,0x78,0x42,0x59,0x45,0xbb,0x43,0x58,0x23,0x82,0x42,
2307 0xa1,0xe1,0xbb,0x43,0xd7,0xbe,0x88,0x42,0x08,0xc9,0x18,0xbc,0x43,0xaf,0x9f,0x8c,0x42,0x1e,0x76,0xbd,
2308 0x43,0x51,0x7c,0x8d,0x42,0xd6,0xe5,0xbe,0x43,0xf4,0x58,0x8e,0x42,0x08,0x9c,0x0a,0xbf,0x43,0x45,0xc7,
2309 0x8e,0x42,0x30,0x26,0xbf,0x43,0x96,0x35,0x8f,0x42,0xc4,0x41,0xbf,0x43,0xac,0xc8,0x8f,0x42,0x07,0xc4,
2310 0x41,0xbf,0x43,0x69,0x76,0xa3,0x42,0x08,0x08,0xef,0xbe,0x43,0xb1,0xd6,0x99,0x42,0xe8,0x89,0xbe,0x43,
2311 0xde,0xc5,0x8d,0x42,0xc0,0x46,0xbc,0x43,0xc2,0x5b,0x90,0x42,0x08,0x9c,0xf2,0xba,0x43,0x86,0x80,0x90,
2312 0x42,0xf2,0x43,0xba,0x43,0xe8,0x73,0x87,0x42,0x8f,0x31,0xba,0x43,0xb6,0xf4,0x7d,0x42,0x07,0x8f,0x31,
2313 0xba,0x43,0x21,0xc6,0x76,0x42,0x08,0xc0,0x3a,0xba,0x43,0x5f,0x48,0x6b,0x42,0xae,0x96,0xba,0x43,0xe3,
2314 0x83,0x61,0x42,0xf6,0x32,0xbb,0x43,0x40,0xa7,0x60,0x42,0x09,0x06,0xea,0x74,0xea,0x43,0x61,0x44,0x93,
2315 0x43,0x08,0x24,0x5c,0xec,0x43,0x31,0x3b,0x93,0x43,0xfb,0x30,0xee,0x43,0x93,0x4d,0x93,0x43,0x0d,0xe1,
2316 0xef,0x43,0x80,0xa9,0x93,0x43,0x08,0x8f,0x58,0xf0,0x43,0xd1,0x17,0x94,0x43,0xb7,0x8f,0xf0,0x43,0x10,
2317 0xe2,0x94,0x43,0xea,0x98,0xf0,0x43,0xa9,0xec,0x95,0x43,0x07,0xea,0x98,0xf0,0x43,0x38,0x25,0x97,0x43,
2318 0x08,0x23,0x74,0xf0,0x43,0x9f,0x32,0x9a,0x43,0x5a,0x60,0xef,0x43,0x53,0xcb,0x9e,0x43,0x2d,0x3a,0xee,
2319 0x43,0xfd,0x91,0xa3,0x43,0x08,0xa2,0xf0,0xed,0x43,0xdd,0x38,0xa5,0x43,0x17,0xa7,0xed,0x43,0xbe,0xdf,
2320 0xa6,0x43,0x5a,0x54,0xed,0x43,0x9f,0x86,0xa8,0x43,0x08,0xfc,0x24,0xec,0x43,0xca,0xc4,0xad,0x43,0x48,
2321 0xa4,0xeb,0x43,0x40,0x6f,0xab,0x43,0x28,0x3f,0xeb,0x43,0x1c,0x0f,0xa8,0x43,0x08,0x1f,0x6d,0xeb,0x43,
2322 0x72,0x48,0xa3,0x43,0x67,0x09,0xec,0x43,0xd1,0x53,0x9e,0x43,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,0x43,
2323 0x07,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x08,0x7e,0x90,0xea,0x43,0x8a,0x9f,0x99,0x43,0x12,0xac,
2324 0xea,0x43,0xbc,0xa8,0x99,0x43,0xa7,0xc7,0xea,0x43,0xbc,0xa8,0x99,0x43,0x08,0x51,0x76,0xeb,0x43,0x9f,
2325 0x32,0x9a,0x43,0x5e,0x37,0xec,0x43,0x49,0xed,0x9c,0x43,0xb0,0xa5,0xec,0x43,0x2a,0xa0,0xa0,0x43,0x08,
2326 0x09,0xe6,0xec,0x43,0xd1,0x77,0xa4,0x43,0x28,0x4b,0xed,0x43,0x61,0xa4,0xa3,0x43,0xab,0xc2,0xed,0x43,
2327 0x8e,0xb2,0xa0,0x43,0x08,0x70,0xe7,0xed,0x43,0xde,0x08,0x9d,0x43,0x87,0x86,0xf0,0x43,0x2f,0x53,0x97,
2328 0x43,0x87,0x7a,0xee,0x43,0xec,0x99,0x95,0x43,0x08,0xca,0x27,0xee,0x43,0xff,0x3d,0x95,0x43,0x74,0xca,
2329 0xec,0x43,0x55,0x8f,0x94,0x43,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x07,0xea,0x74,0xea,0x43,0x61,
2330 0x44,0x93,0x43,0x09,0x06,0x05,0xd3,0xe5,0x43,0x19,0x9c,0x90,0x43,0x08,0x09,0xc2,0xe6,0x43,0xd1,0xff,
2331 0x8f,0x43,0x4d,0x6f,0xe6,0x43,0x74,0xe8,0x92,0x43,0x3b,0xd7,0xe8,0x43,0xc3,0x56,0x93,0x43,0x08,0x1f,
2332 0x61,0xe9,0x43,0x93,0x4d,0x93,0x43,0x05,0xeb,0xe9,0x43,0x93,0x4d,0x93,0x43,0xea,0x74,0xea,0x43,0x61,
2333 0x44,0x93,0x43,0x07,0xea,0x74,0xea,0x43,0xe7,0xaa,0x94,0x43,0x08,0x24,0x50,0xea,0x43,0xe7,0xaa,0x94,
2334 0x43,0x2d,0x22,0xea,0x43,0xe7,0xaa,0x94,0x43,0x36,0xf4,0xe9,0x43,0xe7,0xaa,0x94,0x43,0x08,0xa2,0xcc,
2335 0xe7,0x43,0xe0,0xd8,0x94,0x43,0xd4,0xc9,0xe5,0x43,0x19,0xa8,0x92,0x43,0xd4,0xc9,0xe5,0x43,0x27,0x69,
2336 0x93,0x43,0x08,0x17,0x77,0xe5,0x43,0xe0,0xd8,0x94,0x43,0x67,0xe5,0xe5,0x43,0x47,0xda,0x95,0x43,0x43,
2337 0x9d,0xe6,0x43,0xe2,0xd3,0x97,0x43,0x08,0x9d,0xdd,0xe6,0x43,0xad,0xe7,0x98,0x43,0x09,0xce,0xe8,0x43,
2338 0xff,0x55,0x99,0x43,0xea,0x74,0xea,0x43,0x8a,0x9f,0x99,0x43,0x07,0xea,0x74,0xea,0x43,0x1e,0xc7,0x9b,
2339 0x43,0x08,0x71,0xcf,0xe9,0x43,0x53,0xb3,0x9a,0x43,0xa7,0xbb,0xe8,0x43,0xdb,0x0d,0x9a,0x43,0xc6,0x14,
2340 0xe7,0x43,0xdb,0x0d,0x9a,0x43,0x08,0x48,0x80,0xe5,0x43,0xdb,0x0d,0x9a,0x43,0x0a,0xb6,0xe4,0x43,0xc3,
2341 0x6e,0x97,0x43,0x76,0x9a,0xe4,0x43,0x74,0xf4,0x94,0x43,0x07,0x76,0x9a,0xe4,0x43,0x79,0xd7,0x93,0x43,
2342 0x08,0xd8,0xac,0xe4,0x43,0x66,0x27,0x92,0x43,0x29,0x1b,0xe5,0x43,0xe0,0xc0,0x90,0x43,0x05,0xd3,0xe5,
2343 0x43,0x19,0x9c,0x90,0x43,0x09,0x06,0x1b,0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x08,0x71,0x0b,0xf4,0x42,
2344 0x00,0x0e,0x8d,0x42,0x8c,0x0f,0x01,0x43,0x3e,0xc0,0x89,0x42,0xf3,0x28,0x06,0x43,0x48,0x9e,0x8b,0x42,
2345 0x08,0x15,0x89,0x09,0x43,0x00,0x0e,0x8d,0x42,0xe0,0x9c,0x0a,0x43,0xc1,0x8b,0x98,0x42,0xa6,0xc1,0x0a,
2346 0x43,0x02,0xa5,0xaa,0x42,0x07,0xa6,0xc1,0x0a,0x43,0xf9,0xf6,0xb0,0x42,0x08,0xa6,0xc1,0x0a,0x43,0x47,
2347 0x8e,0xb4,0x42,0x42,0xaf,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0xe0,0x9c,0x0a,0x43,0xba,0x74,0xbc,0x42,0x08,
2348 0xa1,0xd2,0x09,0x43,0x40,0x47,0xd0,0x42,0x0d,0xab,0x07,0x43,0x91,0xb5,0xd0,0x42,0x3b,0xb9,0x04,0x43,
2349 0xec,0x71,0xba,0x42,0x08,0xe5,0x5b,0x03,0x43,0xe3,0x33,0xa8,0x42,0x63,0xd8,0x00,0x43,0xce,0x70,0x9f,
2350 0x42,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x07,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,0x42,0x08,0xed,
2351 0x6f,0xed,0x42,0x73,0x24,0x9d,0x42,0xd8,0x0c,0xf5,0x42,0x99,0x6c,0x9c,0x42,0x27,0xab,0xfd,0x42,0xea,
2352 0xda,0x9c,0x42,0x08,0x36,0xca,0x03,0x43,0x2b,0x94,0x9e,0x42,0x68,0xc7,0x01,0x43,0x8f,0xbe,0xa2,0x42,
2353 0xfa,0x06,0x08,0x43,0x73,0xb4,0xb5,0x42,0x08,0x8e,0x2e,0x0a,0x43,0x1f,0x6f,0xb8,0x42,0x9d,0xe3,0x08,
2354 0x43,0xd7,0x1e,0x99,0x42,0x28,0x15,0x05,0x43,0x32,0x3b,0x93,0x42,0x08,0x63,0xf0,0x04,0x43,0x70,0xed,
2355 0x8f,0x42,0x71,0x0b,0xf4,0x42,0x32,0x3b,0x93,0x42,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x07,0x1b,
2356 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x09,0x06,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,0x08,0x8e,0x55,
2357 0xc0,0x42,0xb8,0x4d,0x86,0x42,0x60,0xbf,0xd7,0x42,0x3e,0xf0,0x91,0x42,0x63,0xf6,0xe4,0x42,0x70,0xed,
2358 0x8f,0x42,0x08,0x7a,0x89,0xe5,0x42,0xac,0xc8,0x8f,0x42,0xcc,0xf7,0xe5,0x42,0xac,0xc8,0x8f,0x42,0x1b,
2359 0x66,0xe6,0x42,0xe3,0xa3,0x8f,0x42,0x07,0x1b,0x66,0xe6,0x42,0x73,0xf4,0x94,0x42,0x08,0x63,0xf6,0xe4,
2360 0x42,0x3b,0x19,0x95,0x42,0xe6,0x61,0xe3,0x42,0x00,0x3e,0x95,0x42,0xf4,0x16,0xe2,0x42,0xc4,0x62,0x95,
2361 0x42,0x08,0x6e,0x74,0xd6,0x42,0x15,0xd1,0x95,0x42,0x97,0x63,0xca,0x42,0xaf,0xcf,0x94,0x42,0xfb,0x2d,
2362 0xbe,0x42,0x86,0x80,0x90,0x42,0x08,0x97,0x03,0xba,0x42,0xce,0x10,0x8f,0x42,0x5e,0x28,0xba,0x42,0x3e,
2363 0xf0,0x91,0x42,0xf2,0x4f,0xbc,0x42,0x45,0xf7,0x96,0x42,0x08,0x27,0x54,0xbf,0x42,0x73,0x24,0x9d,0x42,
2364 0xa5,0xe8,0xc0,0x42,0x86,0xe0,0xa0,0x42,0xe4,0xca,0xc5,0x42,0xed,0x11,0xaa,0x42,0x08,0x54,0xaa,0xc8,
2365 0x42,0x86,0x40,0xb1,0x42,0x59,0x81,0xc5,0x42,0xa1,0x11,0xc4,0x42,0x3e,0xe7,0xbf,0x42,0xfb,0x8d,0xce,
2366 0x42,0x08,0xb4,0x6d,0xb7,0x42,0x30,0xc2,0xd9,0x42,0x46,0xf5,0xc9,0x42,0xdf,0x53,0xd9,0x42,0x38,0x40,
2367 0xcb,0x42,0x62,0x8f,0xcf,0x42,0x08,0x7d,0xf9,0xcc,0x42,0xec,0xa1,0xc2,0x42,0x07,0x43,0xcd,0x42,0x6c,
2368 0xdd,0xb8,0x42,0x2b,0x8b,0xcc,0x42,0x92,0xf5,0xaf,0x42,0x08,0xf9,0x8d,0xce,0x42,0x41,0x57,0xa7,0x42,
2369 0x5b,0xb8,0xd2,0x42,0xae,0x2f,0xa5,0x42,0x18,0x2f,0xd9,0x42,0x13,0x2a,0xa1,0x42,0x08,0x41,0x7e,0xdd,
2370 0x42,0xe3,0x03,0xa0,0x42,0x2e,0xf2,0xe1,0x42,0x7c,0x02,0x9f,0x42,0x1b,0x66,0xe6,0x42,0xa2,0x4a,0x9e,
2371 0x42,0x07,0x1b,0x66,0xe6,0x42,0xae,0x2f,0xa5,0x42,0x08,0x4d,0x63,0xe4,0x42,0x00,0x9e,0xa5,0x42,0xf4,
2372 0x16,0xe2,0x42,0x15,0x31,0xa6,0x42,0x99,0xca,0xdf,0x42,0x2b,0xc4,0xa6,0x42,0x08,0xc0,0x82,0xc6,0x42,
2373 0xc4,0xc2,0xa5,0x42,0x57,0xe1,0xd5,0x42,0x91,0xb5,0xd0,0x42,0x54,0xda,0xd0,0x42,0x97,0x93,0xd2,0x42,
2374 0x08,0x9c,0x3a,0xc7,0x42,0x17,0x58,0xdc,0x42,0x9c,0x0a,0xbf,0x42,0x6e,0xa4,0xde,0x42,0x90,0x25,0xb8,
2375 0x42,0xdf,0x53,0xd9,0x42,0x08,0x59,0x21,0xb5,0x42,0xf2,0xdf,0xd4,0x42,0x51,0x43,0xb3,0x42,0x91,0xb5,
2376 0xd0,0x42,0xc5,0x29,0xbb,0x42,0x0e,0x1a,0xca,0x42,0x08,0x65,0x36,0xc4,0x42,0xd0,0x07,0xbd,0x42,0x3e,
2377 0xe7,0xbf,0x42,0x37,0x09,0xbe,0x42,0x0c,0xea,0xc1,0x42,0xcd,0xd0,0xaf,0x42,0x08,0x2b,0x5b,0xc4,0x42,
2378 0x18,0x08,0xa3,0x42,0x67,0xa6,0xab,0x42,0x99,0x3c,0x94,0x42,0x5e,0x28,0xba,0x42,0x35,0xe2,0x87,0x42,
2379 0x09,];
2383 //----------------------------------------------------------------------------
2384 // Anti-Grain Geometry (AGG) - Version 2.5
2385 // A high quality rendering engine for C++
2386 // Copyright (C) 2002-2006 Maxim Shemanarev
2387 // Contact: mcseem@antigrain.com
2388 // mcseemagg@yahoo.com
2389 // http://antigrain.com
2391 // AGG is free software; you can redistribute it and/or
2392 // modify it under the terms of the GNU General Public License
2393 // as published by the Free Software Foundation; either version 2
2394 // of the License, or (at your option) any later version.
2396 // AGG is distributed in the hope that it will be useful,
2397 // but WITHOUT ANY WARRANTY; without even the implied warranty of
2398 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2399 // GNU General Public License for more details.
2401 // You should have received a copy of the GNU General Public License
2402 // along with AGG; if not, write to the Free Software
2403 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
2404 // MA 02110-1301, USA.
2405 //----------------------------------------------------------------------------
2406 private nothrow @trusted @nogc:
2409 enum PathCommand : ubyte {
2410 Stop = 0,
2411 MoveTo = 1,
2412 LineTo = 2,
2413 EndPoly = 0x0F,
2414 Mask = 0x0F,
2417 enum PathFlag : ubyte {
2418 None = 0x00,
2419 CCW = 0x10,
2420 CW = 0x20,
2421 Close = 0x40,
2422 Mask = 0xF0,
2426 bool isVertex (in uint c) pure { pragma(inline, true); return (c >= PathCommand.MoveTo && c < PathCommand.EndPoly); }
2427 bool isDrawing (in uint c) pure { pragma(inline, true); return (c >= PathCommand.LineTo && c < PathCommand.EndPoly); }
2428 bool isStop (in uint c) pure { pragma(inline, true); return (c == PathCommand.Stop); }
2429 bool isMoveTo (in uint c) pure { pragma(inline, true); return (c == PathCommand.MoveTo); }
2430 bool isLineTo (in uint c) pure { pragma(inline, true); return (c == PathCommand.LineTo); }
2431 bool isEndPoly (in uint c) pure { pragma(inline, true); return ((c&PathCommand.Mask) == PathCommand.EndPoly); }
2432 bool isClose (in uint c) pure { pragma(inline, true); return (c&~cast(uint)(PathFlag.CW|PathFlag.CCW)) == (PathCommand.EndPoly|PathFlag.Close); }
2433 bool isCW (in uint c) pure { pragma(inline, true); return ((c&PathFlag.CW) != 0); }
2434 bool isCCW (in uint c) pure { pragma(inline, true); return ((c&PathFlag.CCW) != 0); }
2435 bool isOriented (in uint c) pure { pragma(inline, true); return ((c&(PathFlag.CW|PathFlag.CCW)) != 0); }
2436 bool isClosed (in uint c) pure { pragma(inline, true); return ((c&PathFlag.Close) != 0); }
2437 uint getCloseFlag (in uint c) pure { pragma(inline, true); return (c&PathFlag.Close); }
2438 uint clearOrientation (in uint c) pure { pragma(inline, true); return (c&~cast(uint)(PathFlag.CW|PathFlag.CCW)); }
2439 uint getOrientation (in uint c) pure { pragma(inline, true); return (c&(PathFlag.CW|PathFlag.CCW)); }
2440 uint setOrientation (in uint c, uint o) pure { pragma(inline, true); return (clearOrientation(c)|o); }
2442 //enum PI = 3.14159265358979323846;
2443 //enum DBL_PI = cast(double)(0x1.921fb54442d18p+1);
2444 enum FLT_PI = cast(float)(0x1.921fb54442d18p+1);
2446 enum DEG2RAD_MULT_D = cast(double)(0x1.1df46a2529d39p-6);
2447 enum RAD2DEG_MULT_D = cast(double)(0x1.ca5dc1a63c1f8p+5);
2449 enum DEG2RAD_MULT_F = cast(float)(0x1.1df46ap-6f);
2450 enum RAD2DEG_MULT_F = cast(float)(0x1.ca5dc2p+5f);
2452 //float deg2rad (in float deg) pure nothrow @safe @nogc { pragma(inline, true); return deg*cast(float)(PI/180.0f); }
2453 //float rad2deg (in float rad) pure nothrow @safe @nogc { pragma(inline, true); return rad*cast(float)(180.0f/PI); }
2454 public T deg2rad(T) (in T deg) if (__traits(isFloating, T) && (T.sizeof == 4 || T.sizeof == 8)) {
2455 pragma(inline, true);
2456 static if (T.sizeof == 4) {
2457 return deg*DEG2RAD_MULT_F;
2458 } else {
2459 return deg*DEG2RAD_MULT_D;
2463 public T rad2deg(T) (in T rad) if (__traits(isFloating, T) && (T.sizeof == 4 || T.sizeof == 8)) {
2464 pragma(inline, true);
2465 static if (T.sizeof == 4) {
2466 return rad*RAD2DEG_MULT_F;
2467 } else {
2468 return rad*RAD2DEG_MULT_D;
2473 float calcPolygonArea(Storage) (in ref Storage st) {
2474 float sum = 0;
2475 float x = st[0].x;
2476 float y = st[0].y;
2477 float xs = x;
2478 float ys = y;
2479 foreach (immutable uint i; 1..st.length) {
2480 auto v = &st[i];
2481 sum += x*v.y-y*v.x;
2482 x = v.x;
2483 y = v.y;
2485 return (sum+x*ys-y*xs)*0.5f;
2489 void shortenPath(VertexSequence) (ref VertexSequence vs, float s, in uint closed=0) {
2490 alias VertexType = VertexSequence.ValueType;
2491 if (s > 0 && vs.length > 1) {
2492 //float d;
2493 int n = cast(int)(vs.length-2);
2494 while (n) {
2495 immutable float d = vs[n].dist;
2496 if (d > s) break;
2497 vs.removeLast();
2498 s -= d;
2499 --n;
2501 if (vs.length < 2) {
2502 vs.removeAll();
2503 } else {
2504 n = vs.length-1;
2505 VertexType* prev = &vs[n-1];
2506 VertexType* last = &vs[n];
2507 immutable float d = (prev.dist-s)/prev.dist;
2508 immutable float x = prev.x+(last.x-prev.x)*d;
2509 immutable float y = prev.y+(last.y-prev.y)*d;
2510 last.x = x;
2511 last.y = y;
2512 if (!(*prev)(*last)) vs.removeLast();
2513 vs.close(closed != 0);
2519 // ////////////////////////////////////////////////////////////////////////// //
2520 // Vertex (x, y) with the distance to the next one. The last vertex has
2521 // distance between the last and the first points if the polygon is closed
2522 // and 0.0 if it's a polyline.
2523 struct VertexDist {
2524 public nothrow @trusted @nogc:
2525 float x = 0.0f;
2526 float y = 0.0f;
2527 float dist = 0.0f;
2529 this (in float ax, in float ay) pure { x = ax; y = ay; /*dist = 0.0f;*/ }
2531 bool opCall() (in auto ref VertexDist val) {
2532 //enum VertexDistEPS = cast(double)1e-14; // Coinciding points maximal distance (Epsilon)
2533 enum VertexDistEPS = cast(float)0.00001f; // Coinciding points maximal distance (Epsilon)
2534 immutable bool ret = (dist = distance(x, y, val.x, val.y)) > VertexDistEPS;
2535 if (!ret) dist = 1.0f/VertexDistEPS;
2536 return ret;
2541 // ////////////////////////////////////////////////////////////////////////// //
2542 struct SimpleVector(T) {
2543 public nothrow @trusted @nogc:
2544 alias ValueType = T;
2545 T* pvec;
2546 uint pvecAllot, pvecSize;
2548 mixin(DisableCopyingMixin);
2550 ~this () {
2551 import core.stdc.stdlib : free;
2552 if (pvec !is null) free(pvec);
2553 pvec = null;
2554 pvecAllot = pvecSize = 0;
2557 @property uint length () const pure { pragma(inline, true); return pvecSize; }
2559 ref inout(T) opIndex (in uint idx) inout pure { pragma(inline, true);return pvec[idx]; }
2561 ref inout(T) curr (in uint idx) inout pure { pragma(inline, true);return pvec[idx]; }
2562 ref inout(T) prev (in uint idx) inout pure { pragma(inline, true);return pvec[(idx+pvecSize-1)%pvecSize]; }
2563 ref inout(T) next (in uint idx) inout pure { pragma(inline, true);return pvec[(idx+1)%pvecSize]; }
2565 void removeAll () {
2566 pragma(inline, true);
2567 pvecSize = 0;
2570 void removeLast () {
2571 pragma(inline, true);
2572 if (pvecSize > 0 ) --pvecSize;
2575 void add() (in auto ref T val) {
2576 import core.stdc.stdlib : realloc;
2577 import core.stdc.string : memcpy;
2578 if (pvecSize+1 > pvecAllot) {
2579 uint newsz = (pvecAllot|0xff)+1;
2580 pvec = cast(T*)realloc(pvec, T.sizeof*newsz);
2581 if (pvec is null) assert(0, "out of memory");
2582 pvecAllot = newsz;
2584 assert(pvecSize < pvecAllot);
2585 memcpy(pvec+pvecSize, &val, T.sizeof);
2586 ++pvecSize;
2591 // ////////////////////////////////////////////////////////////////////////// //
2592 struct VertexSequence(T) {
2593 public nothrow @trusted @nogc:
2594 SimpleVector!T me;
2595 alias me this;
2596 alias ValueType = T;
2598 mixin(DisableCopyingMixin);
2600 void add() (in auto ref T val) {
2601 import core.stdc.stdlib : realloc;
2602 import core.stdc.string : memcpy;
2603 if (pvecSize > 1) {
2604 if (!pvec[pvecSize-2](pvec[pvecSize-1])) removeLast();
2606 me.add(val);
2609 void modifyLast() (in auto ref T val) {
2610 if (pvecSize > 0) removeLast();
2611 add(val);
2614 // closed aka remove_flag
2615 void close (in bool closed) {
2616 while (pvecSize > 1) {
2617 if (pvec[pvecSize-2](pvec[pvecSize-1])) break;
2618 T t = pvec[pvecSize-1];
2619 removeLast();
2620 modifyLast(t);
2622 if (closed) {
2623 while (pvecSize > 1) {
2624 if (pvec[pvecSize-1](pvec[0])) break;
2625 removeLast();
2632 // ////////////////////////////////////////////////////////////////////////// //
2633 // process vertex source [src] with generator [dest]
2634 void feedWith(VSD, VSS) (ref VSD dest, ref VSS src) {
2635 dest.removeAll();
2636 src.rewind();
2637 float x, y;
2638 for (;;) {
2639 immutable cmd = src.vertex(&x, &y);
2640 dest.addVertex(x, y, cmd);
2641 if (isStop(cmd)) break;
2646 // ////////////////////////////////////////////////////////////////////////// //
2647 // this connects vertex source to vertex generator
2648 struct PathPoint {
2649 float x = 0.0f, y = 0.0f;
2650 bool moveto;
2651 @property bool lineto () const pure nothrow @safe @nogc { pragma(inline, true); return !moveto; }
2655 struct PathPointRange {
2656 private:
2657 SimpleVector!PathPoint pts;
2658 uint idx = 0;
2660 public nothrow @trusted @nogc:
2661 mixin(DisableCopyingMixin);
2663 this(VG) (ref VG vg) {
2664 // feed dest
2665 vg.rewind();
2666 float x, y;
2667 float sx, sy;
2668 bool first = true;
2669 for (;;) {
2670 auto cmd = vs.vertex(&x, &y);
2671 if (isStop(cmd)) break;
2672 if (isMoveTo(cmd)) {
2673 sx = x;
2674 sy = y;
2675 first = true;
2676 continue;
2678 if (isLineTo(cmd)) {
2680 import std.math : isNaN;
2681 assert(!sx.isNaN);
2682 assert(!sy.isNaN);
2684 if (first) { pts.add(PathPoint(sx, sy, true)); }
2685 pts.add(PathPoint(x, y, false));
2686 continue;
2688 if (isEndPoly(cmd)) {
2689 if (isClose(cmd) && !first) {
2690 //writeln("s=(", sx, ",", sy, "); c=(", x, ",", y, ")");
2691 pts.add(PathPoint(sx, sy, false));
2694 first = true;
2698 void rewind () { pragma(inline, true); idx = 0; }
2700 @property bool empty () const pure { pragma(inline, true); return (idx >= pts.length); }
2701 @property ref inout(PathPoint) front () inout pure { pragma(inline, true); return pts[idx]; }
2702 @property int length () const pure { pragma(inline, true); return pts.length-idx; }
2703 void popFront () { if (idx < pts.length) ++idx; }
2707 // ////////////////////////////////////////////////////////////////////////// //
2708 bool intersection (in float ax, in float ay, in float bx, in float by, in float cx, in float cy, in float dx, in float dy, float* x, float* y) /*pure*/ nothrow @trusted @nogc {
2709 //enum IntersectionEPS = cast(double)1.0e-30; // See calc_intersection
2710 enum IntersectionEPS = cast(float)1.0e-16; // See calc_intersection
2711 //version(aliced) pragma(inline, true);
2712 //import std.math : abs;
2713 import core.stdc.math : fabsf;
2714 immutable float num = (ay-cy)*(dx-cx)-(ax-cx)*(dy-cy);
2715 immutable float den = (bx-ax)*(dy-cy)-(by-ay)*(dx-cx);
2716 if (fabsf(den) < IntersectionEPS) return false;
2717 //if ((den < 0.0f ? (-den < IntersectionEPS) : (den < IntersectionEPS))) return false;
2718 immutable float r = num/den;
2719 if (x !is null) *x = ax+r*(bx-ax);
2720 if (y !is null) *y = ay+r*(by-ay);
2721 return true;
2724 float cross (in float x1, in float y1, in float x2, in float y2, in float x, in float y) pure nothrow @safe @nogc {
2725 pragma(inline, true);
2726 return (x-x2)*(y2-y1)-(y-y2)*(x2-x1);
2729 float distance (in float x1, in float y1, in float x2, in float y2) /*pure*/ nothrow @safe @nogc {
2730 pragma(inline, true);
2731 import core.stdc.math : sqrtf;
2732 immutable float dx = x2-x1;
2733 immutable float dy = y2-y1;
2734 return sqrtf(dx*dx+dy*dy);
2737 float distance() (in auto ref VertexDist v0, in auto ref VertexDist v1) /*pure*/ nothrow @safe @nogc {
2738 pragma(inline, true);
2739 import core.stdc.math : sqrtf;
2740 immutable float dx = v1.x-v0.x;
2741 immutable float dy = v1.y-v0.y;
2742 return sqrtf(dx*dx+dy*dy);
2747 * VertexConsumer API:
2748 * alias value_type = VertexType; // shoud support `VertexType(x, y)`
2749 * vc.remove_all(); -- start new polygon
2750 * vc.add(VT); -- add point to the current polygon
2752 struct StrokeCalc(VertexConsumer) {
2753 private:
2754 float mWidth = 0.5f;
2755 float mWidthAbs = 0.5f;
2756 float mWidthEps = 0.5f/1024.0f;
2757 int mWidthSign = 1;
2758 float mMiterLimit = 4.0f;
2759 float mInnerMiterLimit = 1.01f;
2760 float mApproxScale = 1.0f;
2761 LineCap mLineCap = LineCap.Butt;
2762 LineJoin mLineJoin = LineJoin.Miter;
2763 InnerJoin mInnerJoin = InnerJoin.Miter;
2765 public nothrow @trusted @nogc:
2766 alias CoordType = VertexConsumer.ValueType;
2768 mixin(DisableCopyingMixin);
2770 @property void lineCap (in LineCap lc) { pragma(inline, true); mLineCap = lc; }
2771 @property void lineJoin (in LineJoin lj) { pragma(inline, true); mLineJoin = lj; }
2772 @property void innerJoin (in InnerJoin ij) { pragma(inline, true); mInnerJoin = ij; }
2774 @property LineCap lineCap () const pure { pragma(inline, true); return mLineCap; }
2775 @property LineJoin lineJoin () const pure { pragma(inline, true); return mLineJoin; }
2776 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mInnerJoin; }
2778 @property float width () const pure { pragma(inline, true); return mWidth*2.0f; }
2779 @property void width (in float w) {
2780 mWidth = w*0.5f;
2781 if (mWidth < 0.0f) {
2782 mWidthAbs = -mWidth;
2783 mWidthSign = -1;
2784 } else {
2785 mWidthAbs = mWidth;
2786 mWidthSign = 1;
2788 mWidthEps = mWidth/1024.0f;
2791 @property void miterLimit (in float ml) { pragma(inline, true); mMiterLimit = ml; }
2792 @property void miterLimitTheta (in float t) { pragma(inline, true); import core.stdc.math : sinf; mMiterLimit = 1.0f/sinf(t*0.5f); }
2793 @property void innerMiterLimit (in float ml) { pragma(inline, true); mInnerMiterLimit = ml; }
2794 @property void approximationScale (in float as) { pragma(inline, true); mApproxScale = as; }
2796 @property float miterLimit () const pure { pragma(inline, true); return mMiterLimit; }
2797 @property float innerMiterLimit () const pure { pragma(inline, true); return mInnerMiterLimit; }
2798 @property float approximationScale () const pure { pragma(inline, true); return mApproxScale; }
2800 void calcCap() (ref VertexConsumer vc, in auto ref VertexDist v0, in auto ref VertexDist v1, in float len) {
2801 import core.stdc.math : acosf, atan2f, cosf, sinf;
2803 vc.removeAll();
2805 float dx1 = (v1.y-v0.y)/len;
2806 float dy1 = (v1.x-v0.x)/len;
2807 float dx2 = 0.0f;
2808 float dy2 = 0.0f;
2810 dx1 *= mWidth;
2811 dy1 *= mWidth;
2813 if (mLineCap != LineCap.Round) {
2814 if (mLineCap == LineCap.Square) {
2815 dx2 = dy1*mWidthSign;
2816 dy2 = dx1*mWidthSign;
2818 addVertex(vc, v0.x-dx1-dx2, v0.y+dy1-dy2);
2819 addVertex(vc, v0.x+dx1-dx2, v0.y-dy1-dy2);
2820 } else {
2821 float da = acosf(mWidthAbs/(mWidthAbs+0.125f/mApproxScale))*2.0f;
2822 immutable int n = cast(int)(FLT_PI/da);
2823 da = FLT_PI/(n+1);
2824 addVertex(vc, v0.x-dx1, v0.y+dy1);
2825 if (mWidthSign > 0.0f) {
2826 float a1 = atan2f(dy1, -dx1);
2827 a1 += da;
2828 foreach (immutable int i; 0..n) {
2829 addVertex(vc, v0.x+cosf(a1)*mWidth, v0.y+sinf(a1)*mWidth);
2830 a1 += da;
2832 } else {
2833 float a1 = atan2f(-dy1, dx1);
2834 a1 -= da;
2835 foreach (immutable int i; 0..n) {
2836 addVertex(vc, v0.x+cosf(a1)*mWidth, v0.y+sinf(a1)*mWidth);
2837 a1 -= da;
2840 addVertex(vc, v0.x+dx1, v0.y-dy1);
2844 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) {
2845 import core.stdc.math : sqrtf;
2847 immutable float dx1 = mWidth*(v1.y-v0.y)/len1;
2848 immutable float dy1 = mWidth*(v1.x-v0.x)/len1;
2849 immutable float dx2 = mWidth*(v2.y-v1.y)/len2;
2850 immutable float dy2 = mWidth*(v2.x-v1.x)/len2;
2852 vc.removeAll();
2854 float cp = cross(v0.x, v0.y, v1.x, v1.y, v2.x, v2.y);
2855 if (cp != 0.0f && (cp > 0.0f) == (mWidth > 0.0f)) {
2856 // Inner join
2857 float limit = (len1 < len2 ? len1 : len2)/mWidthAbs;
2858 if (limit < mInnerMiterLimit) limit = mInnerMiterLimit;
2860 switch (mInnerJoin) {
2861 default: // inner_bevel
2862 addVertex(vc, v1.x+dx1, v1.y-dy1);
2863 addVertex(vc, v1.x+dx2, v1.y-dy2);
2864 break;
2865 case InnerJoin.Miter:
2866 calcMiter(vc, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0.0f);
2867 break;
2868 case InnerJoin.Jag:
2869 case InnerJoin.Round:
2870 cp = (dx1-dx2)*(dx1-dx2)+(dy1-dy2)*(dy1-dy2);
2871 if (cp < len1*len1 && cp < len2*len2) {
2872 calcMiter(vc, v0, v1, v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0.0f);
2873 } else {
2874 if (mInnerJoin == InnerJoin.Jag) {
2875 addVertex(vc, v1.x+dx1, v1.y-dy1);
2876 addVertex(vc, v1.x, v1.y);
2877 addVertex(vc, v1.x+dx2, v1.y-dy2);
2878 } else {
2879 addVertex(vc, v1.x+dx1, v1.y-dy1);
2880 addVertex(vc, v1.x, v1.y);
2881 calcArc(vc, v1.x, v1.y, dx2, -dy2, dx1, -dy1);
2882 addVertex(vc, v1.x, v1.y);
2883 addVertex(vc, v1.x+dx2, v1.y-dy2);
2886 break;
2888 } else {
2889 // Outer join
2891 // Calculate the distance between v1 and
2892 // the central point of the bevel line segment
2893 float dx = (dx1+dx2)*0.5f;
2894 float dy = (dy1+dy2)*0.5f;
2895 immutable float dbevel = sqrtf(dx*dx+dy*dy);
2897 if (mLineJoin == LineJoin.Round || mLineJoin == LineJoin.Bevel) {
2898 // This is an optimization that reduces the number of points
2899 // in cases of almost collinear segments. If there's no
2900 // visible difference between bevel and miter joins we'd rather
2901 // use miter join because it adds only one point instead of two.
2903 // Here we calculate the middle point between the bevel points
2904 // and then, the distance between v1 and this middle point.
2905 // At outer joins this distance always less than stroke width,
2906 // because it's actually the height of an isosceles triangle of
2907 // v1 and its two bevel points. If the difference between this
2908 // width and this value is small (no visible bevel) we can
2909 // add just one point.
2911 // The constant in the expression makes the result approximately
2912 // the same as in round joins and caps. You can safely comment
2913 // out this entire "if".
2914 if (mApproxScale*(mWidthAbs-dbevel) < mWidthEps) {
2915 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)) {
2916 addVertex(vc, dx, dy);
2917 } else {
2918 addVertex(vc, v1.x+dx1, v1.y-dy1);
2920 return;
2924 switch (mLineJoin) {
2925 case LineJoin.Miter:
2926 case LineJoin.MiterRevert:
2927 case LineJoin.MiterRound:
2928 calcMiter(vc, v0, v1, v2, dx1, dy1, dx2, dy2, mLineJoin, mMiterLimit, dbevel);
2929 break;
2930 case LineJoin.Round:
2931 calcArc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
2932 break;
2933 default: // Bevel join
2934 addVertex(vc, v1.x+dx1, v1.y-dy1);
2935 addVertex(vc, v1.x+dx2, v1.y-dy2);
2936 break;
2941 private:
2942 void addVertex (ref VertexConsumer vc, in float x, in float y) {
2943 pragma(inline, true);
2944 vc.add(CoordType(x, y));
2947 void calcArc (ref VertexConsumer vc, in float x, in float y, in float dx1, in float dy1, in float dx2, in float dy2) {
2948 import core.stdc.math : acosf, atan2f, cosf, sinf;
2950 float a1 = atan2f(dy1*mWidthSign, dx1*mWidthSign);
2951 float a2 = atan2f(dy2*mWidthSign, dx2*mWidthSign);
2953 immutable float da = acosf(mWidthAbs/(mWidthAbs+0.125f/mApproxScale))*2.0f;
2955 addVertex(vc, x+dx1, y+dy1);
2956 if (mWidthSign > 0.0f) {
2957 if (a1 > a2) a2 += 2.0f*FLT_PI;
2958 immutable int n = cast(int)((a2-a1)/da);
2959 immutable float daa = (a2-a1)/(n+1);
2960 a1 += daa;
2961 foreach (immutable int i; 0..n) {
2962 addVertex(vc, x+cosf(a1)*mWidth, y+sinf(a1)*mWidth);
2963 a1 += daa;
2965 } else {
2966 if (a1 < a2) a2 -= 2.0f*FLT_PI;
2967 immutable int n = cast(int)((a1-a2)/da);
2968 immutable float daa = (a1-a2)/(n+1);
2969 a1 -= daa;
2970 foreach (immutable int i; 0..n) {
2971 addVertex(vc, x+cosf(a1)*mWidth, y+sinf(a1)*mWidth);
2972 a1 -= daa;
2975 addVertex(vc, x+dx2, y+dy2);
2978 void calcMiter (ref VertexConsumer vc, in ref VertexDist v0, in ref VertexDist v1, in ref VertexDist v2,
2979 in float dx1, in float dy1, in float dx2, in float dy2, in LineJoin lj,
2980 in float mlimit, in float dbevel)
2982 float xi = v1.x;
2983 float yi = v1.y;
2984 float di = 1.0f;
2985 immutable float lim = mWidthAbs*mlimit;
2986 bool miterLimitExceeded = true; // assume the worst
2987 bool intersectionFailed = true; // assume the worst
2989 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)) {
2990 // Calculation of the intersection succeeded
2991 di = distance(v1.x, v1.y, xi, yi);
2992 if (di <= lim) {
2993 // Inside the miter limit
2994 addVertex(vc, xi, yi);
2995 miterLimitExceeded = false;
2997 intersectionFailed = false;
2998 } else {
2999 // Calculation of the intersection failed, most probably
3000 // the three points lie one straight line.
3001 // First check if v0 and v2 lie on the opposite sides of vector:
3002 // (v1.x, v1.y) -> (v1.x+dx1, v1.y-dy1), that is, the perpendicular
3003 // to the line determined by vertices v0 and v1.
3004 // This condition determines whether the next line segments continues
3005 // the previous one or goes back.
3006 immutable float x2 = v1.x+dx1;
3007 immutable float y2 = v1.y-dy1;
3008 if ((cross(v0.x, v0.y, v1.x, v1.y, x2, y2) < 0) == (cross(v1.x, v1.y, v2.x, v2.y, x2, y2) < 0)) {
3009 // This case means that the next segment continues
3010 // the previous one (straight line)
3011 addVertex(vc, v1.x+dx1, v1.y-dy1);
3012 miterLimitExceeded = false;
3016 if (miterLimitExceeded) {
3017 // Miter limit exceeded
3018 //------------------------
3019 switch (lj) {
3020 case LineJoin.MiterRevert:
3021 // For the compatibility with SVG, PDF, etc,
3022 // we use a simple bevel join instead of
3023 // "smart" bevel
3024 addVertex(vc, v1.x+dx1, v1.y-dy1);
3025 addVertex(vc, v1.x+dx2, v1.y-dy2);
3026 break;
3027 case LineJoin.MiterRound:
3028 calcArc(vc, v1.x, v1.y, dx1, -dy1, dx2, -dy2);
3029 break;
3030 default:
3031 // If no miter-revert, calculate new dx1, dy1, dx2, dy2
3032 if (intersectionFailed) {
3033 immutable float mlimitM = mlimit*mWidthSign;
3034 addVertex(vc, v1.x+dx1+dy1*mlimitM, v1.y-dy1+dx1*mlimitM);
3035 addVertex(vc, v1.x+dx2-dy2*mlimitM, v1.y-dy2-dx2*mlimitM);
3036 } else {
3037 immutable float x1 = v1.x+dx1;
3038 immutable float y1 = v1.y-dy1;
3039 immutable float x2 = v1.x+dx2;
3040 immutable float y2 = v1.y-dy2;
3041 di = (lim-dbevel)/(di-dbevel);
3042 addVertex(vc, x1+(xi-x1)*di, y1+(yi-y1)*di);
3043 addVertex(vc, x2+(xi-x2)*di, y2+(yi-y2)*di);
3045 break;
3052 // ////////////////////////////////////////////////////////////////////////// //
3053 struct Stroker {
3054 public nothrow @trusted @nogc:
3055 alias VertexStorage = VertexSequence!VertexDist;
3056 alias CoordStorage = SimpleVector!AGGPoint;
3058 private:
3059 enum State {
3060 Initial,
3061 Ready,
3062 Cap1,
3063 Cap2,
3064 Outline1,
3065 CloseFirst,
3066 Outline2,
3067 OutVertices,
3068 End_poly1,
3069 End_poly2,
3070 Stop,
3073 private:
3074 StrokeCalc!CoordStorage mStroker;
3075 VertexStorage mSrcVertices;
3076 CoordStorage mOutVertices;
3077 float mShorten = 0;
3078 uint mClosed = 0;
3079 State mStatus = State.Initial;
3080 State mPrevStatus;
3081 uint mSrcVertex = 0;
3082 uint mOutVertex = 0;
3084 public:
3085 mixin(DisableCopyingMixin);
3087 @property void lineCap (LineCap lc) { pragma(inline, true); mStroker.lineCap(lc); }
3088 @property void lineJoin (LineJoin lj) { pragma(inline, true); mStroker.lineJoin(lj); }
3089 @property void innerJoin (InnerJoin ij) { pragma(inline, true); mStroker.innerJoin(ij); }
3091 @property LineCap lineCap () const pure { pragma(inline, true); return mStroker.lineCap(); }
3092 @property LineJoin lineJoin () const pure { pragma(inline, true); return mStroker.lineJoin(); }
3093 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mStroker.innerJoin(); }
3095 @property void width (float w) { pragma(inline, true); mStroker.width(w); }
3096 @property void miterLimit (float ml) { pragma(inline, true); mStroker.miterLimit(ml); }
3097 @property void miterLimitTheta (float t) { pragma(inline, true); mStroker.miterLimitTheta(t); }
3098 @property void innerMiterLimit (float ml) { pragma(inline, true); mStroker.innerMiterLimit(ml); }
3099 @property void approximationScale (float as) { pragma(inline, true); mStroker.approximationScale(as); }
3101 @property float width () const pure { pragma(inline, true); return mStroker.width(); }
3102 @property float miterLimit () const pure { pragma(inline, true); return mStroker.miterLimit(); }
3103 @property float innerMiterLimit () const pure { pragma(inline, true); return mStroker.innerMiterLimit(); }
3104 @property float approximationScale () const pure { pragma(inline, true); return mStroker.approximationScale(); }
3106 @property void shorten (float s) { pragma(inline, true); mShorten = s; }
3107 @property float shorten () const pure { pragma(inline, true); return mShorten; }
3109 // Generator interface
3110 void removeAll () {
3111 mSrcVertices.removeAll();
3112 mClosed = 0;
3113 mStatus = State.Initial;
3116 void addVertex (float x, float y, uint cmd) {
3117 mStatus = State.Initial;
3118 if (isMoveTo(cmd)) {
3119 mSrcVertices.modifyLast(VertexDist(x, y));
3120 } else {
3121 if (isVertex(cmd)) {
3122 mSrcVertices.add(VertexDist(x, y));
3123 } else {
3124 mClosed = getCloseFlag(cmd);
3129 // Vertex Source Interface
3130 void rewind () {
3131 if (mStatus == State.Initial) {
3132 mSrcVertices.close(mClosed != 0);
3133 shortenPath(mSrcVertices, mShorten, mClosed);
3134 if (mSrcVertices.length < 3) mClosed = 0;
3136 mStatus = State.Ready;
3137 mSrcVertex = 0;
3138 mOutVertex = 0;
3141 uint vertex (float* x, float* y) {
3142 uint cmd = PathCommand.LineTo;
3143 while (!isStop(cmd)) {
3144 final switch (mStatus) {
3145 case State.Initial:
3146 rewind();
3147 goto case;
3149 case State.Ready:
3150 if (mSrcVertices.length < 2+cast(uint)(mClosed != 0)) {
3151 cmd = PathCommand.Stop;
3152 break;
3154 mStatus = (mClosed ? State.Outline1 : State.Cap1);
3155 cmd = PathCommand.MoveTo;
3156 mSrcVertex = 0;
3157 mOutVertex = 0;
3158 break;
3160 case State.Cap1:
3161 mStroker.calcCap(mOutVertices, mSrcVertices[0], mSrcVertices[1], mSrcVertices[0].dist);
3162 mSrcVertex = 1;
3163 mPrevStatus = State.Outline1;
3164 mStatus = State.OutVertices;
3165 mOutVertex = 0;
3166 break;
3168 case State.Cap2:
3169 mStroker.calcCap(mOutVertices, mSrcVertices[mSrcVertices.length-1], mSrcVertices[mSrcVertices.length-2], mSrcVertices[mSrcVertices.length-2].dist);
3170 mPrevStatus = State.Outline2;
3171 mStatus = State.OutVertices;
3172 mOutVertex = 0;
3173 break;
3175 case State.Outline1:
3176 if (mClosed) {
3177 if (mSrcVertex >= mSrcVertices.length) {
3178 mPrevStatus = State.CloseFirst;
3179 mStatus = State.End_poly1;
3180 break;
3182 } else {
3183 if (mSrcVertex >= mSrcVertices.length-1) {
3184 mStatus = State.Cap2;
3185 break;
3188 mStroker.calcJoin(mOutVertices, mSrcVertices.prev(mSrcVertex), mSrcVertices.curr(mSrcVertex), mSrcVertices.next(mSrcVertex), mSrcVertices.prev(mSrcVertex).dist, mSrcVertices.curr(mSrcVertex).dist);
3189 ++mSrcVertex;
3190 mPrevStatus = mStatus;
3191 mStatus = State.OutVertices;
3192 mOutVertex = 0;
3193 break;
3195 case State.CloseFirst:
3196 mStatus = State.Outline2;
3197 cmd = PathCommand.MoveTo;
3198 goto case;
3200 case State.Outline2:
3201 if (mSrcVertex <= uint(mClosed == 0)) {
3202 mStatus = State.End_poly2;
3203 mPrevStatus = State.Stop;
3204 break;
3207 --mSrcVertex;
3208 mStroker.calcJoin(mOutVertices, mSrcVertices.next(mSrcVertex), mSrcVertices.curr(mSrcVertex), mSrcVertices.prev(mSrcVertex), mSrcVertices.curr(mSrcVertex).dist, mSrcVertices.prev(mSrcVertex).dist);
3210 mPrevStatus = mStatus;
3211 mStatus = State.OutVertices;
3212 mOutVertex = 0;
3213 break;
3215 case State.OutVertices:
3216 if (mOutVertex >= mOutVertices.length) {
3217 mStatus = mPrevStatus;
3218 } else {
3219 const(AGGPoint)* c = &mOutVertices[mOutVertex++];
3220 *x = c.x;
3221 *y = c.y;
3222 return cmd;
3224 break;
3226 case State.End_poly1:
3227 mStatus = mPrevStatus;
3228 return PathCommand.EndPoly|PathFlag.Close|PathFlag.CCW;
3230 case State.End_poly2:
3231 mStatus = mPrevStatus;
3232 return PathCommand.EndPoly|PathFlag.Close|PathFlag.CW;
3234 case State.Stop:
3235 cmd = PathCommand.Stop;
3236 break;
3239 return cmd;
3244 // ////////////////////////////////////////////////////////////////////////// //
3245 struct Contourer {
3246 public nothrow @trusted @nogc:
3247 alias VertexStorage = VertexSequence!VertexDist;
3248 alias CoordStorage = SimpleVector!AGGPoint;
3250 enum State {
3251 Initial,
3252 Ready,
3253 Outline,
3254 OutVertices,
3255 EndPoly,
3256 Stop,
3259 private:
3260 StrokeCalc!CoordStorage mStroker;
3261 float mWidth = 1;
3262 VertexStorage mSrcVertices;
3263 CoordStorage mOutVertices;
3264 State mStatus;
3265 uint mSrcVertex = 0;
3266 uint mOutVertex;
3267 uint mClosed = 0;
3268 uint mOrientation = 0;
3269 bool mAutoDetect = false;
3271 public:
3272 mixin(DisableCopyingMixin);
3274 @property void lineCap (LineCap lc) { pragma(inline, true); mStroker.lineCap(lc); }
3275 @property void lineJoin (LineJoin lj) { pragma(inline, true); mStroker.lineJoin(lj); }
3276 @property void innerJoin (InnerJoin ij) { pragma(inline, true); mStroker.innerJoin(ij); }
3278 @property LineCap lineCap () const pure { pragma(inline, true); return mStroker.lineCap(); }
3279 @property LineJoin lineJoin () const pure { pragma(inline, true); return mStroker.lineJoin(); }
3280 @property InnerJoin innerJoin () const pure { pragma(inline, true); return mStroker.innerJoin(); }
3282 @property void width (float w) { pragma(inline, true); mStroker.width(mWidth = w); }
3283 @property void miterLimit (float ml) { pragma(inline, true); mStroker.miterLimit(ml); }
3284 @property void miterLimitTheta (float t) { pragma(inline, true); mStroker.miterLimitTheta(t); }
3285 @property void innerMiterLimit (float ml) { pragma(inline, true); mStroker.innerMiterLimit(ml); }
3286 @property void approximationScale (float as) { pragma(inline, true); mStroker.approximationScale(as); }
3288 @property float width () const pure { pragma(inline, true); return mWidth; }
3289 @property float miterLimit () const pure { pragma(inline, true); return mStroker.miterLimit(); }
3290 @property float innerMiterLimit () const pure { pragma(inline, true); return mStroker.innerMiterLimit(); }
3291 @property float approximationScale () const pure { pragma(inline, true); return mStroker.approximationScale(); }
3293 @property void autoDetectOrientation (bool v) { pragma(inline, true); mAutoDetect = v; }
3294 @property bool autoDetectOrientation () const pure { pragma(inline, true); return mAutoDetect; }
3296 // Generator interface
3297 void removeAll () {
3298 mSrcVertices.removeAll();
3299 mClosed = 0;
3300 mOrientation = 0;
3301 mStatus = State.Initial;
3304 void addVertex (float x, float y, uint cmd) {
3305 mStatus = State.Initial;
3306 if (isMoveTo(cmd)) {
3307 mSrcVertices.modifyLast(VertexDist(x, y));
3308 } else {
3309 if (isVertex(cmd)) {
3310 mSrcVertices.add(VertexDist(x, y));
3311 } else {
3312 if (isEndPoly(cmd)) {
3313 mClosed = getCloseFlag(cmd);
3314 if (mOrientation == PathFlag.None) {
3315 mOrientation = getOrientation(cmd);
3322 // Vertex Source Interface
3323 void rewind () {
3324 if (mStatus == State.Initial) {
3325 mSrcVertices.close(true);
3326 if (mAutoDetect) {
3327 if (!isOriented(mOrientation)) {
3328 mOrientation = (calcPolygonArea(mSrcVertices) > 0 ? PathFlag.CCW : PathFlag.CW);
3331 if (isOriented(mOrientation)) {
3332 mStroker.width(isCCW(mOrientation) ? mWidth : -mWidth);
3335 mStatus = State.Ready;
3336 mSrcVertex = 0;
3339 uint vertex (float* x, float* y) {
3340 uint cmd = PathCommand.LineTo;
3341 while (!isStop(cmd)) {
3342 final switch (mStatus) {
3343 case State.Initial:
3344 rewind();
3345 goto case;
3347 case State.Ready:
3348 if (mSrcVertices.length < 2+cast(uint)(mClosed != 0)) {
3349 cmd = PathCommand.Stop;
3350 break;
3352 mStatus = State.Outline;
3353 cmd = PathCommand.MoveTo;
3354 mSrcVertex = 0;
3355 mOutVertex = 0;
3356 goto case;
3358 case State.Outline:
3359 if (mSrcVertex >= mSrcVertices.length) {
3360 mStatus = State.EndPoly;
3361 break;
3363 mStroker.calcJoin(mOutVertices, mSrcVertices.prev(mSrcVertex), mSrcVertices.curr(mSrcVertex), mSrcVertices.next(mSrcVertex), mSrcVertices.prev(mSrcVertex).dist, mSrcVertices.curr(mSrcVertex).dist);
3364 ++mSrcVertex;
3365 mStatus = State.OutVertices;
3366 mOutVertex = 0;
3367 goto case;
3369 case State.OutVertices:
3370 if (mOutVertex >= mOutVertices.length) {
3371 mStatus = State.Outline;
3372 } else {
3373 const(AGGPoint)* c = &mOutVertices[mOutVertex++];
3374 *x = c.x;
3375 *y = c.y;
3376 return cmd;
3378 break;
3380 case State.EndPoly:
3381 if (!mClosed) return PathCommand.Stop;
3382 mStatus = State.Stop;
3383 return PathCommand.EndPoly|PathFlag.Close|PathFlag.CCW;
3385 case State.Stop:
3386 return PathCommand.Stop;
3389 return cmd;
3394 // ////////////////////////////////////////////////////////////////////////// //
3395 struct Dasher {
3396 private nothrow @trusted @nogc:
3397 alias VertexStorage = VertexSequence!VertexDist;
3398 enum MaxDashes = 32;
3400 enum State {
3401 Initial,
3402 Ready,
3403 Polyline,
3404 Stop,
3407 private:
3408 float[MaxDashes] mDashes = 0;
3409 float mTotalDashLen = 0;
3410 uint mNumDashes = 0;
3411 float mDashStart = 0;
3412 float mShorten = 0;
3413 float mCurrDashStart = 0;
3414 uint mCurrDash = 0;
3415 float mCurrRest = 0;
3416 const(VertexDist)* m_v1 = null;
3417 const(VertexDist)* m_v2 = null;
3419 VertexStorage mSrcVertices;
3420 uint mClosed = 0;
3421 State mStatus = State.Initial;
3422 uint mSrcVertex = 0;
3424 public:
3425 mixin(DisableCopyingMixin);
3427 @property void shorten (float s) { mShorten = s; }
3428 @property float shorten () const pure { return mShorten; }
3430 void removeAllDashes () {
3431 mTotalDashLen = 0;
3432 mNumDashes = 0;
3433 mCurrDashStart = 0;
3434 mCurrDash = 0;
3437 void addDash (float dashLen, float gapLen) {
3438 if (mNumDashes < MaxDashes) {
3439 mTotalDashLen += dashLen+gapLen;
3440 mDashes[mNumDashes++] = dashLen;
3441 mDashes[mNumDashes++] = gapLen;
3445 void dashStart (float ds) {
3446 //import std.math : abs;
3447 import core.stdc.math : fabsf;
3448 mDashStart = ds;
3449 calcDashStart(fabsf(ds));
3452 // Vertex Generator Interface
3453 void removeAll () {
3454 mStatus = State.Initial;
3455 mSrcVertices.removeAll();
3456 mClosed = 0;
3459 void addVertex (float x, float y, uint cmd) {
3460 mStatus = State.Initial;
3461 if (isMoveTo(cmd)) {
3462 mSrcVertices.modifyLast(VertexDist(x, y));
3463 } else {
3464 if (isVertex(cmd)) {
3465 mSrcVertices.add(VertexDist(x, y));
3466 } else {
3467 mClosed = getCloseFlag(cmd);
3472 // Vertex Source Interface
3473 void rewind () {
3474 if (mStatus == State.Initial) {
3475 mSrcVertices.close(mClosed != 0);
3476 shortenPath(mSrcVertices, mShorten, mClosed);
3478 mStatus = State.Ready;
3479 mSrcVertex = 0;
3482 uint vertex (float* x, float* y) {
3483 uint cmd = PathCommand.MoveTo;
3485 while (!isStop(cmd)) {
3486 final switch (mStatus) {
3487 case State.Initial:
3488 rewind();
3489 goto case;
3491 case State.Ready:
3492 if (mNumDashes < 2 || mSrcVertices.length < 2) {
3493 cmd = PathCommand.Stop;
3494 break;
3496 mStatus = State.Polyline;
3497 mSrcVertex = 1;
3498 m_v1 = &mSrcVertices[0];
3499 m_v2 = &mSrcVertices[1];
3500 mCurrRest = m_v1.dist;
3501 *x = m_v1.x;
3502 *y = m_v1.y;
3503 if (mDashStart >= 0) calcDashStart(mDashStart);
3504 return PathCommand.MoveTo;
3506 case State.Polyline:
3507 immutable float dashRest = mDashes[mCurrDash]-mCurrDashStart;
3508 cmd = (mCurrDash&1 ? PathCommand.MoveTo : PathCommand.LineTo);
3509 if (mCurrRest > dashRest) {
3510 mCurrRest -= dashRest;
3511 ++mCurrDash;
3512 if (mCurrDash >= mNumDashes) mCurrDash = 0;
3513 mCurrDashStart = 0;
3514 *x = m_v2.x-(m_v2.x-m_v1.x)*mCurrRest/m_v1.dist;
3515 *y = m_v2.y-(m_v2.y-m_v1.y)*mCurrRest/m_v1.dist;
3516 } else {
3517 mCurrDashStart += mCurrRest;
3518 *x = m_v2.x;
3519 *y = m_v2.y;
3520 ++mSrcVertex;
3521 m_v1 = m_v2;
3522 mCurrRest = m_v1.dist;
3523 if (mClosed) {
3524 if (mSrcVertex > mSrcVertices.length) {
3525 mStatus = State.Stop;
3526 } else {
3527 m_v2 = &mSrcVertices[mSrcVertex >= mSrcVertices.length ? 0 :mSrcVertex];
3529 } else {
3530 if (mSrcVertex >= mSrcVertices.length) {
3531 mStatus = State.Stop;
3532 } else {
3533 m_v2 = &mSrcVertices[mSrcVertex];
3537 return cmd;
3539 case State.Stop:
3540 cmd = PathCommand.Stop;
3541 break;
3544 return PathCommand.Stop;
3547 private:
3548 void calcDashStart (float ds) {
3549 mCurrDash = 0;
3550 mCurrDashStart = 0;
3551 while (ds > 0) {
3552 if (ds > mDashes[mCurrDash]) {
3553 ds -= mDashes[mCurrDash];
3554 ++mCurrDash;
3555 mCurrDashStart = 0;
3556 if (mCurrDash >= mNumDashes) mCurrDash = 0;
3557 } else {
3558 mCurrDashStart = ds;
3559 ds = 0;
3566 // ////////////////////////////////////////////////////////////////////////// //
3567 // Matrices and Transformations
3569 // matrix class
3570 public align(1) struct AGGMatrix {
3571 align(1):
3572 private:
3573 static immutable float[6] IdentityMat = [
3574 1.0f, 0.0f,
3575 0.0f, 1.0f,
3576 0.0f, 0.0f,
3579 public:
3580 /// Matrix values. Initial value is identity matrix.
3581 float[6] mat = [
3582 1.0f, 0.0f,
3583 0.0f, 1.0f,
3584 0.0f, 0.0f,
3587 public nothrow @trusted @nogc:
3588 /// Create Matrix with the given values.
3589 this (const(float)[] amat...) {
3590 pragma(inline, true);
3591 if (amat.length >= 6) {
3592 mat.ptr[0..6] = amat.ptr[0..6];
3593 } else {
3594 mat.ptr[0..6] = 0.0f;
3595 mat.ptr[0..amat.length] = amat[];
3599 /// Can be used to check validity of [inverted] result
3600 @property bool valid () const { import core.stdc.math : isfinite; return (isfinite(mat.ptr[0]) != 0); }
3602 /// Returns `true` if this matrix is identity matrix.
3603 @property bool isIdentity () const { version(aliced) pragma(inline, true); return (mat[] == IdentityMat[]); }
3605 /// Returns new inverse matrix.
3606 /// If inverted matrix cannot be calculated, `res.valid` fill be `false`.
3607 AGGMatrix inverted () const {
3608 AGGMatrix res = this;
3609 res.invert;
3610 return res;
3613 /// Inverts this matrix.
3614 /// If inverted matrix cannot be calculated, `this.valid` fill be `false`.
3615 ref AGGMatrix invert () {
3616 float[6] inv = void;
3617 immutable double det = cast(double)mat.ptr[0]*mat.ptr[3]-cast(double)mat.ptr[2]*mat.ptr[1];
3618 if (det > -1e-6 && det < 1e-6) {
3619 inv[] = float.nan;
3620 } else {
3621 immutable double invdet = 1.0/det;
3622 inv.ptr[0] = cast(float)(mat.ptr[3]*invdet);
3623 inv.ptr[2] = cast(float)(-mat.ptr[2]*invdet);
3624 inv.ptr[4] = cast(float)((cast(double)mat.ptr[2]*mat.ptr[5]-cast(double)mat.ptr[3]*mat.ptr[4])*invdet);
3625 inv.ptr[1] = cast(float)(-mat.ptr[1]*invdet);
3626 inv.ptr[3] = cast(float)(mat.ptr[0]*invdet);
3627 inv.ptr[5] = cast(float)((cast(double)mat.ptr[1]*mat.ptr[4]-cast(double)mat.ptr[0]*mat.ptr[5])*invdet);
3629 mat.ptr[0..6] = inv.ptr[0..6];
3630 return this;
3633 /// Sets this matrix to identity matrix.
3634 ref AGGMatrix identity () { version(aliced) pragma(inline, true); mat[] = IdentityMat[]; return this; }
3636 /// Translate this matrix.
3637 ref AGGMatrix translate (in float tx, in float ty) {
3638 version(aliced) pragma(inline, true);
3639 return this.mul(Translated(tx, ty));
3642 /// Scale this matrix.
3643 ref AGGMatrix scale (in float sx, in float sy) {
3644 version(aliced) pragma(inline, true);
3645 return this.mul(Scaled(sx, sy));
3648 /// Rotate this matrix.
3649 ref AGGMatrix rotate (in float a) {
3650 version(aliced) pragma(inline, true);
3651 return this.mul(Rotated(a));
3654 /// Skew this matrix by X axis.
3655 ref AGGMatrix skewX (in float a) {
3656 version(aliced) pragma(inline, true);
3657 return this.mul(SkewedX(a));
3660 /// Skew this matrix by Y axis.
3661 ref AGGMatrix skewY (in float a) {
3662 version(aliced) pragma(inline, true);
3663 return this.mul(SkewedY(a));
3666 /// Skew this matrix by both axes.
3667 ref AGGMatrix skewY (in float ax, in float ay) {
3668 version(aliced) pragma(inline, true);
3669 return this.mul(SkewedXY(ax, ay));
3672 /// Transform point with this matrix. `null` destinations are allowed.
3673 /// [sx] and [sy] is the source point. [dx] and [dy] may point to the same variables.
3674 void point (float* dx, float* dy, float sx, float sy) {
3675 version(aliced) pragma(inline, true);
3676 if (dx !is null) *dx = sx*mat.ptr[0]+sy*mat.ptr[2]+mat.ptr[4];
3677 if (dy !is null) *dy = sx*mat.ptr[1]+sy*mat.ptr[3]+mat.ptr[5];
3680 /// Transform point with this matrix.
3681 void point (ref float x, ref float y) {
3682 version(aliced) pragma(inline, true);
3683 immutable float nx = x*mat.ptr[0]+y*mat.ptr[2]+mat.ptr[4];
3684 immutable float ny = x*mat.ptr[1]+y*mat.ptr[3]+mat.ptr[5];
3685 x = nx;
3686 y = ny;
3689 /// Sets this matrix to the result of multiplication of `this` and [s] (this * S).
3690 ref AGGMatrix mul() (in auto ref AGGMatrix s) {
3691 immutable float t0 = mat.ptr[0]*s.mat.ptr[0]+mat.ptr[1]*s.mat.ptr[2];
3692 immutable float t2 = mat.ptr[2]*s.mat.ptr[0]+mat.ptr[3]*s.mat.ptr[2];
3693 immutable float t4 = mat.ptr[4]*s.mat.ptr[0]+mat.ptr[5]*s.mat.ptr[2]+s.mat.ptr[4];
3694 mat.ptr[1] = mat.ptr[0]*s.mat.ptr[1]+mat.ptr[1]*s.mat.ptr[3];
3695 mat.ptr[3] = mat.ptr[2]*s.mat.ptr[1]+mat.ptr[3]*s.mat.ptr[3];
3696 mat.ptr[5] = mat.ptr[4]*s.mat.ptr[1]+mat.ptr[5]*s.mat.ptr[3]+s.mat.ptr[5];
3697 mat.ptr[0] = t0;
3698 mat.ptr[2] = t2;
3699 mat.ptr[4] = t4;
3700 return this;
3703 /// Sets this matrix to the result of multiplication of [s] and `this` (S * this).
3704 /// Sets the transform to the result of multiplication of two transforms, of A = B*A.
3705 /// Group: matrices
3706 ref AGGMatrix premul() (in auto ref AGGMatrix s) {
3707 AGGMatrix s2 = s;
3708 s2.mul(this);
3709 mat[] = s2.mat[];
3710 return this;
3713 /// Multiply this matrix by [s], return result as new matrix.
3714 /// Performs operations in this left-to-right order.
3715 AGGMatrix opBinary(string op="*") (in auto ref AGGMatrix s) const {
3716 version(aliced) pragma(inline, true);
3717 AGGMatrix res = this;
3718 res.mul(s);
3719 return res;
3722 /// Multiply this matrix by [s].
3723 /// Performs operations in this left-to-right order.
3724 ref AGGMatrix opOpAssign(string op="*") (in auto ref AGGMatrix s) {
3725 version(aliced) pragma(inline, true);
3726 return this.mul(s);
3729 float scaleX () const { pragma(inline, true); import core.stdc.math : sqrtf; return sqrtf(mat.ptr[0]*mat.ptr[0]+mat.ptr[2]*mat.ptr[2]); } /// Returns x scaling of this matrix.
3730 float scaleY () const { pragma(inline, true); import core.stdc.math : sqrtf; return sqrtf(mat.ptr[1]*mat.ptr[1]+mat.ptr[3]*mat.ptr[3]); } /// Returns y scaling of this matrix.
3731 float rotation () const { pragma(inline, true); import core.stdc.math : atan2f; return atan2f(mat.ptr[1], mat.ptr[0]); } /// Returns rotation of this matrix.
3732 float tx () const { pragma(inline, true); return mat.ptr[4]; } /// Returns x translation of this matrix.
3733 float ty () const { pragma(inline, true); return mat.ptr[5]; } /// Returns y translation of this matrix.
3735 ref AGGMatrix scaleX (in float v) { pragma(inline, true); return scaleRotateTransform(v, scaleY, rotation, tx, ty); } /// Sets x scaling of this matrix.
3736 ref AGGMatrix scaleY (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, v, rotation, tx, ty); } /// Sets y scaling of this matrix.
3737 ref AGGMatrix rotation (in float v) { pragma(inline, true); return scaleRotateTransform(scaleX, scaleY, v, tx, ty); } /// Sets rotation of this matrix.
3738 ref AGGMatrix tx (in float v) { pragma(inline, true); mat.ptr[4] = v; return this; } /// Sets x translation of this matrix.
3739 ref AGGMatrix ty (in float v) { pragma(inline, true); mat.ptr[5] = v; return this; } /// Sets y translation of this matrix.
3741 /// Utility function to be used in `setXXX()`.
3742 /// This is the same as doing: `mat.identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
3743 ref AGGMatrix scaleRotateTransform (in float xscale, in float yscale, in float a, in float tx, in float ty) {
3744 import core.stdc.math : sinf, cosf;
3745 immutable float cs = cosf(a), sn = sinf(a);
3746 mat.ptr[0] = xscale*cs; mat.ptr[1] = yscale*sn;
3747 mat.ptr[2] = xscale*-sn; mat.ptr[3] = yscale*cs;
3748 mat.ptr[4] = tx; mat.ptr[5] = ty;
3749 return this;
3752 /// This is the same as doing: `mat.identity.rotate(a).translate(tx, ty)`, only faster
3753 ref AGGMatrix rotateTransform (in float a, in float tx, in float ty) {
3754 import core.stdc.math : sinf, cosf;
3755 immutable float cs = cosf(a), sn = sinf(a);
3756 mat.ptr[0] = cs; mat.ptr[1] = sn;
3757 mat.ptr[2] = -sn; mat.ptr[3] = cs;
3758 mat.ptr[4] = tx; mat.ptr[5] = ty;
3759 return this;
3762 /// Returns new identity matrix.
3763 static AGGMatrix Identity () { pragma(inline, true); return AGGMatrix.init; }
3765 /// Returns new translation matrix.
3766 static AGGMatrix Translated (in float tx, in float ty) {
3767 version(aliced) pragma(inline, true);
3768 AGGMatrix res = void;
3769 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
3770 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
3771 res.mat.ptr[4] = tx; res.mat.ptr[5] = ty;
3772 return res;
3775 /// Returns new scaling matrix.
3776 static AGGMatrix Scaled (in float sx, in float sy) {
3777 version(aliced) pragma(inline, true);
3778 AGGMatrix res = void;
3779 res.mat.ptr[0] = sx; res.mat.ptr[1] = 0.0f;
3780 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = sy;
3781 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3782 return res;
3785 /// Returns new rotation matrix. Angle is specified in radians.
3786 static AGGMatrix Rotated (in float a) {
3787 version(aliced) pragma(inline, true);
3788 import core.stdc.math : sinf, cosf;
3789 immutable float cs = cosf(a), sn = sinf(a);
3790 AGGMatrix res = void;
3791 res.mat.ptr[0] = cs; res.mat.ptr[1] = sn;
3792 res.mat.ptr[2] = -sn; res.mat.ptr[3] = cs;
3793 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3794 return res;
3797 /// Returns new x-skewing matrix. Angle is specified in radians.
3798 static AGGMatrix SkewedX (in float a) {
3799 version(aliced) pragma(inline, true);
3800 import core.stdc.math : tanf;
3801 AGGMatrix res = void;
3802 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = 0.0f;
3803 res.mat.ptr[2] = tanf(a); res.mat.ptr[3] = 1.0f;
3804 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3805 return res;
3808 /// Returns new y-skewing matrix. Angle is specified in radians.
3809 static AGGMatrix SkewedY (in float a) {
3810 version(aliced) pragma(inline, true);
3811 import core.stdc.math : tanf;
3812 AGGMatrix res = void;
3813 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = tanf(a);
3814 res.mat.ptr[2] = 0.0f; res.mat.ptr[3] = 1.0f;
3815 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3816 return res;
3819 /// Returns new xy-skewing matrix. Angles are specified in radians.
3820 static AGGMatrix SkewedXY (in float ax, in float ay) {
3821 version(aliced) pragma(inline, true);
3822 import core.stdc.math : tanf;
3823 AGGMatrix res = void;
3824 res.mat.ptr[0] = 1.0f; res.mat.ptr[1] = tanf(ay);
3825 res.mat.ptr[2] = tanf(ax); res.mat.ptr[3] = 1.0f;
3826 res.mat.ptr[4] = 0.0f; res.mat.ptr[5] = 0.0f;
3827 return res;
3830 /// Utility function to be used in `setXXX()`.
3831 /// This is the same as doing: `AGGMatrix.Identity.rotate(a).scale(xs, ys).translate(tx, ty)`, only faster
3832 static AGGMatrix ScaledRotatedTransformed (in float xscale, in float yscale, in float a, in float tx, in float ty) {
3833 AGGMatrix res = void;
3834 res.scaleRotateTransform(xscale, yscale, a, tx, ty);
3835 return res;
3838 /// This is the same as doing: `AGGMatrix.Identity.rotate(a).translate(tx, ty)`, only faster
3839 static AGGMatrix RotatedTransformed (in float a, in float tx, in float ty) {
3840 AGGMatrix res = void;
3841 res.rotateTransform(a, tx, ty);
3842 return res;
3845 public:
3846 // premultiplies current coordinate system by specified matrix
3847 ref AGGMatrix transform() (in auto ref AGGMatrix mt) { pragma(inline, true); this *= mt; return this; }
3849 // translates current coordinate system
3850 ref AGGMatrix translatePre (in float x, in float y) { pragma(inline, true); return premul(AGGMatrix.Translated(x, y)); }
3852 // rotates current coordinate system; angle is specified in radians
3853 ref AGGMatrix rotatePre (in float angle) { pragma(inline, true); return premul(AGGMatrix.Rotated(angle)); }
3855 // skews the current coordinate system along X axis; angle is specified in radians
3856 ref AGGMatrix skewXPre (in float angle) { pragma(inline, true); return premul(AGGMatrix.SkewedX(angle)); }
3858 // skews the current coordinate system along Y axis; angle is specified in radians
3859 ref AGGMatrix skewYPre (in float angle) { pragma(inline, true); return premul(AGGMatrix.SkewedY(angle)); }
3861 // scales the current coordinate system
3862 ref AGGMatrix scalePre (in float x, in float y) { pragma(inline, true); return premul(AGGMatrix.Scaled(x, y)); }