editor: another undo fix
[iv.d.git] / nanovega / simplefont.d
blobcf9834424e2d087df2c7e553a0e661c2907ed6ec
1 /* Invisible Vector Library
2 * ported by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 // by technosaurus, https://github.com/memononen/nanosvg/issues/87#issue-231530483
18 module iv.nanovega.simplefont;
19 private:
21 import iv.nanovega.nanovega;
22 import iv.strex;
25 // ////////////////////////////////////////////////////////////////////////// //
26 static immutable string[96] simpleChars = [
27 /* " " */ "m8 0",
28 /* "!" */ "m4 1v9m0 1v1m4-12",
29 /*"\"" */ "m3 2v3m2-3v3m3-5",
30 /* "#" */ "m2 3v8m4-8v8m-5-2h6m-6-4h6m1-5",
31 /* "$" */ "m2 11a2 3 0 1 0 2-4a2 3 0 1 1 2-4m-2-2v12m4-15",
32 /* "%" */ "m2 3a1 1 0 1 1 0 .1zm4-2l-4 11m2-2a1 1 0 1 1 0 .1zm2-14",
33 /* "&" */ "m7 12l-3-6a2 2.5 0 1 1 .1 0a3 3 0 1 0 3 2m1-8",
34 /* "'" */ "m4 2v3m4-5",
35 /* "(" */ "m4 12q-3-6 0-11m4-1",
36 /* ")" */ "m4 12q3-6 0-11m4-1",
37 /* "*" */ "m1 6h6m-1 4l-4-8m2 0v8m-2 0l4-8m2-2",
38 /* "+" */ "m1 6h6m-3-3v6m4-9",
39 /* "," */ "m5 11q1 1-2 3m5-14",
40 /* "-" */ "m2 6h4m2-6",
41 /* "." */ "m3 12a.5.5 0 0 0-.1-.1zm5-12",
42 /* "/" */ "m2 12l4-11m2-1",
43 /* "0" */ "m2 5.5a2 4 0 1 1 4 0v2a2 4 0 1 1-4 0zm2 0v2m4-8",
44 /* "1" */ "m3 3l1-1v10m4-12",
45 /* "2" */ "m2 3a2 5 30 0 1 0 9h5m1-12",
46 /* "3" */ "m2 2a2 2 0 1 1 3 4m0 0h-1a3 3 0 1 1-3 4m7-10",
47 /* "4" */ "m2 1v5h4v6-11m2-1",
48 /* "5" */ "m2 10a2 4 0 1 0 0-4v-5h4m2-1",
49 /* "6" */ "m3 6a2 3 0 1 1 -1 1l4-6m2-1",
50 /* "7" */ "m1 1h5l-4 11m6-12",
51 /* "8" */ "m3.5 6a2 2.5 0 1 1 1 0a2 3 0 1 1 -1 0zm4.5-6",
52 /* "9" */ "m2 12l4-6a2.5 3 0 1 0 -1 1m3-7",
53 /* ":" */ "m3 3a.5.5 0 1 0 0-.1zm0 6a.5.5 0 1 0 0-.1zm5-9",
54 /* ";" */ "m3 3a.5.5 0 1 0 0-.1zm0 6a.5.5 0 1 0 0-.1zq2 0 -1 2m6-11",
55 /* "<" */ "m6 12l-4-6 4-5m2-1",
56 /* "=" */ "m2 5h4m-4 2h4m2-7",
57 /* ">" */ "m2 12l4-6-4-5m6-1",
58 /* "?" */ "m2 4a2 2 0 1 1 2 2v2m0 3a.5.5 0 1 0-.1 0zm4-11",
59 /* "@" */ "m5 8a1 1 0 1 1 0 -1a1 2 0 1 0 2-0a3 5 0 1 0 -2 4m3-11",
60 /* "A" */ "m1 12l3-10l3 10m-1-4h-4m6-8",
61 /* "B" */ "m2 1h1a2 2 0 1 1 0 5h-1h2a2 2 0 1 1 -2 6zm6-1",
62 /* "C" */ "m5.5 3a2 5 0 1 0 0 7m2.5-10",
63 /* "D" */ "m2 1h1a3 5 0 1 1 0 11h-1zm6-1",
64 /* "E" */ "m6 12h-4v-11h4m-4 5h3m3-6",
65 /* "F" */ "m2 12v-11h4m-4 5h3m3-6",
66 /* "G" */ "m6 4a2 5 0 1 0 0 5v-2h-2m4-7",
67 /* "H" */ "m2 1v11-6h4v6-11m2-1",
68 /* "I" */ "m2 1h4-2v11h2-4m6-12",
69 /* "J" */ "m4 1h2v7q0 4-4 4m6-12",
70 /* "K" */ "m2 1v11-6l5-5-4 4 4 7m1-12",
71 /* "L" */ "m2 1v11h4m2-12",
72 /* "M" */ "m1 12v-11l3 6 3-6v11m1-12",
73 /* "N" */ "m2 12v-11l4 11v-11m2-1",
74 /* "O" */ "m4 1a2 5.5 0 1 0 .1 0zm4-1",
75 /* "P" */ "m2 12v-11h1a3 2 0 1 1 0 5h-1m6-6",
76 /* "Q" */ "m4 1a2 5.5 0 1 0 .1 0zm0 7l3 4m1-12",
77 /* "R" */ "m2 12v-11h1a3 2 0 1 1 0 5h-1 1l4 6m1-12",
78 /* "S" */ "m2 10a2 3 0 1 0 2-4a2 2.5 0 1 1 2-4m2-2",
79 /* "T" */ "m1 1h6-3v11m4-12",
80 /* "U" */ "m2 1v9a2 2 0 0 0 4 0v-9m2-1",
81 /* "V" */ "m2 1l2 11 2-11m2-1",
82 /* "W" */ "m1 1l2 11 1-6 1 6 2-11m1-1",
83 /* "X" */ "m2 1l4 11m-4 0l4-11m2-1",
84 /* "Y" */ "m2 1l2 6v5-5l2-6m2-1",
85 /* "Z" */ "m6 12h-4l4-11h-4m6-1",
86 /* "[" */ "m5 12h-2v-11h2m3-1",
87 /*"\\" */ "m2 1l4 11m2-12",
88 /* "]" */ "m3 12h2v-11h-2m5-1",
89 /* "^" */ "m2 4l2-2 2 2m2-4",
90 /* "_" */ "m1 14h6m1-14",
91 /* "`" */ "m3 2l2 1m3-3",
92 /* "a" */ "m6 7a2 3 0 1 0 0 3v2-7m2-5",
93 /* "b" */ "m2 7a2 3 0 1 1 0 3v2-11m6-1",
94 /* "c" */ "m6 7a2 3 0 1 0 0 3m2-10",
95 /* "d" */ "m6 7a2 3 0 1 0 0 3v2-11m2-1",
96 /* "e" */ "m6 10a2 3 0 1 1 0-2h-4m6-8",
97 /* "f" */ "m2 12v-6h2-2q0-4 4-4m2-2",
98 /* "g" */ "m6 7a2 3 0 1 0 0 3v-5 8a2 2 0 0 1 -4 0m6-13",
99 /* "h" */ "m2 1v11-4a2 2 0 1 1 4 0v4m2-12",
100 /* "i" */ "m4 4v1m0 1v6m4-12",
101 /* "j" */ "m4 4v1m0 1v5q0 2-2 2m6-13",
102 /* "k" */ "m2 1v11-3l3 -3-2 2 3 4m2-12",
103 /* "l" */ "m4 1v11m4-12",
104 /* "m" */ "m2 6v6-4a1 2 0 1 1 2 0v4-4a1 2 0 1 1 2 0v4m2-12",
105 /* "n" */ "m2 6v6-4a2 2 0 1 1 4 0v4m2-12",
106 /* "o" */ "m6 9a2 3 0 1 0 0 .1zm2-9",
107 /* "p" */ "m2 7a2 3 0 1 1 0 3v5-10m6-5",
108 /* "q" */ "m6 7a2 3 0 1 0 0 3v5-10m2-5",
109 /* "r" */ "m2 6v6-4a2 2 0 1 1 4 0m2-8",
110 /* "s" */ "m2 10a2 2 0 1 0 2-2h-1a2 1 0 0 1 3-2m2-6",
111 /* "t" */ "m4 4v8-6h2-4m6-6",
112 /* "u" */ "m2 6v4a2 2 0 1 0 4 0v2-6m2-6",
113 /* "v" */ "m2 6l2 5 2-5m2-6",
114 /* "w" */ "m2 6l1 6 1-4 1 4 1-6m2-6",
115 /* "x" */ "m2 6l4 6m-4 0l4-6m2-6",
116 /* "y" */ "m2 6l2 4l2-4-4 8m6-14",
117 /* "z" */ "m2 6h4l-4 6h4m2-12",
118 /* "{" */ "m5 13a1 6 0 0 1-1-5l-1-1 1-1a1 6 0 0 1 1-5m3-1",
119 /* "|" */ "m4 13v-12m4-1",
120 /* "}" */ "m3 13a1 6 0 0 0 1-5l1-1-1-1a1 6 0 0 0 -1-5m5-1",
121 /* "~" */ "m2 6q1-1 2 0t2 0m2-6",
122 /* ??? */ "m1 1h6v14h-6zm7-1",
126 // ////////////////////////////////////////////////////////////////////////// //
127 // returns advance
128 public float simpleCharHeight (const(float)[] xform=null) nothrow @trusted @nogc {
129 float[6] xf = void;
130 if (xform.length < 6) xf[] = nvgIdentity[]; else xf[] = xform[0..6];
131 float cx = 8, cy = 14;
132 nvgTransformPoint(cx, cy, xf[]);
133 return cy;
136 public float simpleCharWidth (const(float)[] xform=null) nothrow @trusted @nogc {
137 float[6] xf = void;
138 if (xform.length < 6) xf[] = nvgIdentity[]; else xf[] = xform[0..6];
139 float cx = 8, cy = 14;
140 nvgTransformPoint(cx, cy, xf[]);
141 return cx;
144 // returns advance
145 public float drawSimpleChar (NVGContext ctx, float x0, float y0, char ch, const(float)[] xform=null) nothrow @trusted @nogc {
146 float[6] xf = void;
147 if (xform.length < 6) xf[] = nvgIdentity[]; else xf[] = xform[0..6];
149 float firstx = 0, firsty = 0;
150 bool firstPoint = true;
152 static float nsvg__sqr() (in float x) { pragma(inline, true); return x*x; }
153 static float nsvg__vmag() (in float x, float y) { pragma(inline, true); import std.math : sqrt; return sqrt(x*x+y*y); }
155 static float nsvg__vecrat (float ux, float uy, float vx, float vy) {
156 pragma(inline, true);
157 return (ux*vx+uy*vy)/(nsvg__vmag(ux, uy)*nsvg__vmag(vx, vy));
160 static float nsvg__vecang (float ux, float uy, float vx, float vy) {
161 import std.math : acos;
162 float r = nsvg__vecrat(ux, uy, vx, vy);
163 if (r < -1.0f) r = -1.0f;
164 if (r > 1.0f) r = 1.0f;
165 return (ux*vy < uy*vx ? -1.0f : 1.0f)*acos(r);
168 static void nsvg__xformPoint (float* dx, float* dy, in float x, in float y, const(float)* t) {
169 if (dx !is null) *dx = x*t[0]+y*t[2]+t[4];
170 if (dy !is null) *dy = x*t[1]+y*t[3]+t[5];
173 static void nsvg__xformVec (float* dx, float* dy, in float x, in float y, const(float)* t) {
174 if (dx !is null) *dx = x*t[0]+y*t[2];
175 if (dy !is null) *dy = x*t[1]+y*t[3];
178 // Ported from canvg (https://code.google.com/p/canvg/)
179 void nsvg__pathArcTo (float* cpx, float* cpy, const(float)[] args, bool relative) {
180 enum NSVG_PI = 3.14159265358979323846264338327f;
181 import std.math : fabsf = abs, cosf = cos, sinf = sin, sqrtf = sqrt;
182 assert(args.length >= 7);
184 float px = 0, py = 0, ptanx = 0, ptany = 0;
185 float[6] t = void;
186 float x2 = args[5], y2 = args[6]; // end point
188 float rx = fabsf(args[0]); // y radius
189 float ry = fabsf(args[1]); // x radius
190 immutable float rotx = args[2]/180.0f*NSVG_PI; // x rotation engle
191 immutable float fa = (fabsf(args[3]) > 1e-6 ? 1 : 0); // Large arc
192 immutable float fs = (fabsf(args[4]) > 1e-6 ? 1 : 0); // Sweep direction
193 immutable float x1 = *cpx; // start point
194 immutable float y1 = *cpy;
195 // end point
196 if (relative) {
197 x2 += *cpx;
198 y2 += *cpy;
201 float dx = x1-x2;
202 float dy = y1-y2;
203 float d = sqrtf(dx*dx+dy*dy);
204 if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) {
205 // The arc degenerates to a line
206 if (firstPoint) { firstx = x2; firsty = y2; firstPoint = false; }
207 float vgx, vgy;
208 nvgTransformPoint(&vgx, &vgy, xf[], x2, y2);
209 ctx.lineTo(x0+vgx, y0+vgy);
210 //nsvg__lineTo(p, x2, y2);
211 *cpx = x2;
212 *cpy = y2;
213 return;
216 immutable float sinrx = sinf(rotx);
217 immutable float cosrx = cosf(rotx);
219 // Convert to center point parameterization.
220 // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes
221 // 1) Compute x1', y1'
222 immutable float x1p = cosrx*dx/2.0f+sinrx*dy/2.0f;
223 immutable float y1p = -sinrx*dx/2.0f+cosrx*dy/2.0f;
224 d = nsvg__sqr(x1p)/nsvg__sqr(rx)+nsvg__sqr(y1p)/nsvg__sqr(ry);
225 if (d > 1) {
226 d = sqrtf(d);
227 rx *= d;
228 ry *= d;
230 // 2) Compute cx', cy'
231 float s = 0.0f;
232 float sa = nsvg__sqr(rx)*nsvg__sqr(ry)-nsvg__sqr(rx)*nsvg__sqr(y1p)-nsvg__sqr(ry)*nsvg__sqr(x1p);
233 immutable float sb = nsvg__sqr(rx)*nsvg__sqr(y1p)+nsvg__sqr(ry)*nsvg__sqr(x1p);
234 if (sa < 0.0f) sa = 0.0f;
235 if (sb > 0.0f) s = sqrtf(sa/sb);
236 if (fa == fs) s = -s;
237 immutable float cxp = s*rx*y1p/ry;
238 immutable float cyp = s*-ry*x1p/rx;
240 // 3) Compute cx,cy from cx',cy'
241 immutable float cx = (x1+x2)/2.0f+cosrx*cxp-sinrx*cyp;
242 immutable float cy = (y1+y2)/2.0f+sinrx*cxp+cosrx*cyp;
244 // 4) Calculate theta1, and delta theta.
245 immutable float ux = (x1p-cxp)/rx;
246 immutable float uy = (y1p-cyp)/ry;
247 immutable float vx = (-x1p-cxp)/rx;
248 immutable float vy = (-y1p-cyp)/ry;
249 immutable float a1 = nsvg__vecang(1.0f, 0.0f, ux, uy); // Initial angle
250 float da = nsvg__vecang(ux, uy, vx, vy); // Delta angle
252 if (fs == 0 && da > 0) da -= 2*NSVG_PI;
253 else if (fs == 1 && da < 0) da += 2*NSVG_PI;
255 // Approximate the arc using cubic spline segments.
256 t[0] = cosrx; t[1] = sinrx;
257 t[2] = -sinrx; t[3] = cosrx;
258 t[4] = cx; t[5] = cy;
260 // Split arc into max 90 degree segments.
261 // The loop assumes an iteration per end point (including start and end), this +1.
262 immutable ndivs = cast(int)(fabsf(da)/(NSVG_PI*0.5f)+1.0f);
263 immutable float hda = (da/cast(float)ndivs)/2.0f;
264 float kappa = fabsf(4.0f/3.0f*(1.0f-cosf(hda))/sinf(hda));
265 if (da < 0.0f) kappa = -kappa;
267 immutable float ndivsf = cast(float)ndivs;
268 foreach (int i; 0..ndivs+1) {
269 float x = void, y = void, tanx = void, tany = void;
270 immutable float a = a1+da*(i/ndivsf);
271 dx = cosf(a);
272 dy = sinf(a);
273 nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t.ptr); // position
274 nsvg__xformVec(&tanx, &tany, -dy*rx*kappa, dx*ry*kappa, t.ptr); // tangent
275 if (i > 0) {
276 if (firstPoint) { firstx = px+ptanx; firsty = py+ptany; firstPoint = false; }
277 float vgx1, vgy1, vgx2, vgy2, vgx3, vgy3;
278 nvgTransformPoint(&vgx1, &vgy1, xf[], px+ptanx, py+ptany);
279 nvgTransformPoint(&vgx2, &vgy2, xf[], x-tanx, y-tany);
280 nvgTransformPoint(&vgx3, &vgy3, xf[], x, y);
281 ctx.bezierTo(x0+vgx1, y0+vgy1, x0+vgx2, y0+vgy2, x0+vgx3, y0+vgy3);
282 //nsvg__cubicBezTo(p, px+ptanx, py+ptany, x-tanx, y-tany, x, y);
284 px = x;
285 py = y;
286 ptanx = tanx;
287 ptany = tany;
290 *cpx = x2;
291 *cpy = y2;
294 if (ch < ' ' || ch >= 0x80) {
295 float x = 8, y = 0;
296 nvgTransformPoint(x, y, xf[]);
297 return x;
298 } else {
299 string cmd = simpleChars[cast(int)ch-32];
300 float[8] args = void;
301 float cx = 0, cy = 0;
302 float qx1, qy1;
303 enum Code { MoveTo, LineTo, HorizTo, VertTo, ArcTo, QuadTo, ShortQuadTo, Close }
304 Code code = Code.Close;
305 int argc = 0, argn = 0;
306 ctx.moveTo(x0, y0);
307 while (cmd.length) {
308 if (cmd[0] <= ' ' || cmd[0] == ',') { cmd = cmd[1..$]; continue; }
309 switch (cmd[0]) {
310 case 'm': code = Code.MoveTo; cmd = cmd[1..$]; argc = 2; argn = 0; break;
311 case 'l': code = Code.LineTo; cmd = cmd[1..$]; argc = 2; argn = 0; break;
312 case 'a': code = Code.ArcTo; cmd = cmd[1..$]; argc = 7; argn = 0; break;
313 case 'h': code = Code.HorizTo; cmd = cmd[1..$]; argc = 1; argn = 0; break;
314 case 'v': code = Code.VertTo; cmd = cmd[1..$]; argc = 1; argn = 0; break;
315 case 'q': code = Code.QuadTo; cmd = cmd[1..$]; argc = 4; argn = 0; break;
316 case 't': code = Code.ShortQuadTo; cmd = cmd[1..$]; argc = 2; argn = 0; break;
317 case 'z':
318 assert(argn == 0);
319 float vgx, vgy;
320 nvgTransformPoint(&vgx, &vgy, xf[], firstx, firsty);
321 //conwriteln("Z: cx=", cx, "; cy=", cy, "; fx=", firstx, "; fy=", firsty);
322 ctx.lineTo(x0+vgx, y0+vgy);
323 code = Code.Close;
324 cmd = cmd[1..$];
325 cx = qx1 = firstx;
326 cy = qy1 = firsty;
327 firstPoint = true;
328 break;
329 default:
330 import std.math : isNaN;
331 if (cmd[0].isalpha) assert(0, "unknown command"); //: '"~cmd[0]~"'");
332 usize end = 0;
333 if (cmd[0] == '+' || cmd[0] == '-') ++end;
334 while (end < cmd.length && cmd[end].isdigit) ++end;
335 if (end < cmd.length && cmd[end] == '.') {
336 ++end;
337 while (end < cmd.length && cmd[end].isdigit) ++end;
339 auto arg = atof(cmd[0..end]);
340 //static assert(is(typeof(arg) == float));
341 if (arg.isNaN) assert(0, "ooops"); //: <"~cmd[0..end].idup~"> : "~cmd.idup);
342 cmd = cmd[end..$];
343 args[argn++] = arg;
344 assert(argn <= argc);
345 if (argn == argc) {
346 argn = 0;
347 final switch (code) {
348 case Code.MoveTo:
349 case Code.LineTo:
350 //if (code == Code.MoveTo && ch == '%') { import core.stdc.stdio; printf("$-M: %g %g (cur: %g %g)\n", cast(double)args[0], cast(double)args[1], cx, cy); }
351 cx += args[0];
352 cy += args[1];
353 float vgx, vgy;
354 nvgTransformPoint(&vgx, &vgy, xf[], cx, cy);
355 if (code == Code.MoveTo) ctx.moveTo(x0+vgx, y0+vgy); else ctx.lineTo(x0+vgx, y0+vgy);
356 if (firstPoint || code == Code.MoveTo) { firstx = cx; firsty = cy; firstPoint = false; }
357 code = Code.LineTo;
358 break;
359 case Code.HorizTo:
360 case Code.VertTo:
361 float vgx, vgy;
362 if (code == Code.HorizTo) {
363 nvgTransformPoint(&vgx, &vgy, xf[], cx+args[0], cy);
364 cx += args[0];
365 } else {
366 nvgTransformPoint(&vgx, &vgy, xf[], cx, cy+args[0]);
367 cy += args[0];
369 ctx.lineTo(x0+vgx, y0+vgy);
370 if (firstPoint) { firstx = cx; firsty = cy; firstPoint = false; }
371 break;
372 case Code.ArcTo:
373 nsvg__pathArcTo(&cx, &cy, args[0..argc], true);
374 break;
375 case Code.QuadTo:
376 immutable float x1 = cx;
377 immutable float y1 = cy;
378 cx += args[0];
379 cy += args[1];
380 immutable float x2 = x1+args[2];
381 immutable float y2 = y1+args[3];
382 immutable float cx1 = x1+2.0f/3.0f*(cx-x1);
383 immutable float cy1 = y1+2.0f/3.0f*(cy-y1);
384 immutable float cx2 = x2+2.0f/3.0f*(cx-x2);
385 immutable float cy2 = y2+2.0f/3.0f*(cy-y2);
386 qx1 = cx;
387 qy1 = cy;
388 cx = x2;
389 cy = y2;
390 args[0] = cx1;
391 args[1] = cy1;
392 args[2] = cx2;
393 args[3] = cy2;
394 args[4] = x2;
395 args[5] = y2;
396 if (firstPoint) { firstx = cx1; firsty = cy1; firstPoint = false; }
397 foreach (immutable pidx; 0..3) nvgTransformPoint(args[pidx*2+0], args[pidx*2+1], xf[]);
398 ctx.bezierTo(x0+args[0], y0+args[1], x0+args[2], y0+args[3], x0+args[4], y0+args[5]);
399 break;
400 case Code.ShortQuadTo:
401 immutable float x1 = cx;
402 immutable float y1 = cy;
403 immutable float x2 = cx+args[0];
404 immutable float y2 = cy+args[1];
406 cx = 2*x1-qx1;
407 cy = 2*y1-qy1;
409 // Convert to cubix bezier
410 immutable float cx1 = x1+2.0f/3.0f*(cx-x1);
411 immutable float cy1 = y1+2.0f/3.0f*(cy-y1);
412 immutable float cx2 = x2+2.0f/3.0f*(cx-x2);
413 immutable float cy2 = y2+2.0f/3.0f*(cy-y2);
415 cx = x2;
416 cy = y2;
418 args[0] = cx1;
419 args[1] = cy1;
420 args[2] = cx2;
421 args[3] = cy2;
422 args[4] = x2;
423 args[5] = y2;
424 if (firstPoint) { firstx = cx1; firsty = cy1; firstPoint = false; }
425 foreach (immutable pidx; 0..3) nvgTransformPoint(args[pidx*2+0], args[pidx*2+1], xf[]);
426 ctx.bezierTo(x0+args[0], y0+args[1], x0+args[2], y0+args[3], x0+args[4], y0+args[5]);
427 break;
428 case Code.Close:
429 assert(0, "ooops");
431 assert(!firstPoint);
433 break;
436 //conwriteln("cx=", cx, "; cy=", cy);
437 version(none) {
438 import std.math : abs;
439 if (abs(cx-8) > 0.0001) { import core.stdc.stdio; printf("char=%c; cx=%g\n", cast(int)ch, cx); }
440 if (abs(cy) > 0.0001) { import core.stdc.stdio; printf("char=%c; cy=%g\n", cast(int)ch, cy); }
441 } else {
442 cx = 8;
443 cy = 0;
445 nvgTransformPoint(cx, cy, xf[]);
446 //nvg.moveTo(cx, cy);
447 return cx;