7#include "GuiGeometry.h"
14const float kPi = 3.14159265358979f;
16float clampf(
float v,
float lo,
float hi)
18 return v < lo ? lo : (v > hi ? hi : v);
24 std::vector<GuiPoint> pts;
27typedef std::function<Ring(
float)> RingFn;
30int arcCount(
float radius,
float tol)
34 float ang = acosf(clampf(1.0f - tol / radius, -1.0f, 1.0f));
37 return (
int)clampf(ceilf((kPi * 0.5f) / ang), 2.0f, 24.0f);
41int circleCount(
float radius,
float tol)
45 float ang = acosf(clampf(1.0f - tol / radius, -1.0f, 1.0f));
48 return (
int)clampf(ceilf((2.0f * kPi) / ang), 12.0f, 96.0f);
56 float ux, uy, t0, invSpan;
58 FillEval(
const GuiFill& f,
const GuiRect& b) :
59 from(f.from), to(f.to), gradient(f.gradient), ux(0), uy(0), t0(0), invSpan(0)
63 float rad = f.
angle * kPi / 180.0f;
66 float d0 = b.
x * ux + b.
y * uy;
67 float d1 = (b.
x + b.
w) * ux + b.
y * uy;
68 float d2 = b.
x * ux + (b.
y + b.
h) * uy;
69 float d3 = (b.
x + b.
w) * ux + (b.
y + b.
h) * uy;
70 float lo = std::min(std::min(d0, d1), std::min(d2, d3));
71 float hi = std::max(std::max(d0, d1), std::max(d2, d3));
73 invSpan = (hi - lo) > 1e-6f ? 1.0f / (hi - lo) : 0.0f;
76 static uint8_t mix(uint8_t a, uint8_t b,
float t)
78 return (uint8_t)clampf(a + (b - a) * t + 0.5f, 0.0f, 255.0f);
81 PixelColor at(GuiPoint p,
float alphaScale)
const
86 float t = clampf((p.
x * ux + p.
y * uy - t0) * invSpan, 0.0f, 1.0f);
87 c.
r = mix(from.r, to.r, t);
88 c.
g = mix(from.g, to.g, t);
89 c.
b = mix(from.b, to.b, t);
90 c.
a = mix(from.a, to.a, t);
92 c.
a = (uint8_t)clampf(c.
a * alphaScale + 0.5f, 0.0f, 255.0f);
111 float s = (b.
x - a.
x) * (c.
y - a.
y) - (c.
x - a.
x) * (b.
y - a.
y);
112 pushVertex(out, a, ca);
115 pushVertex(out, c, cc);
116 pushVertex(out, b, cb);
120 pushVertex(out, b, cb);
121 pushVertex(out, c, cc);
127void emitFan(std::vector<MeshVertex>& out,
GuiPoint centre,
const Ring& r,
const FillEval& e,
float a)
129 size_t n = r.pts.size();
133 for(
size_t i = 0; i < n; i++)
136 const GuiPoint& p1 = r.pts[(i + 1) % n];
137 pushTri(out, centre, cc, p0, e.at(p0, a), p1, e.at(p1, a));
142void emitBand(std::vector<MeshVertex>& out,
const Ring& A,
float aA,
const Ring& B,
float aB,
const FillEval& e)
144 size_t n = A.pts.size();
145 if(n < 3 || B.pts.size() != n)
147 for(
size_t i = 0; i < n; i++)
149 size_t j = (i + 1) % n;
150 PixelColor ca0 = e.at(A.pts[i], aA), ca1 = e.at(A.pts[j], aA);
151 PixelColor cb0 = e.at(B.pts[i], aB), cb1 = e.at(B.pts[j], aB);
152 pushTri(out, A.pts[i], ca0, B.pts[i], cb0, B.pts[j], cb1);
153 pushTri(out, A.pts[i], ca0, B.pts[j], cb1, A.pts[j], ca1);
160 float half = 0.5f * std::min(r.
w, r.
h);
161 d = std::max(d, -half);
163 clampf(c.
tl, 0.0f, half), clampf(c.
tr, 0.0f, half),
164 clampf(c.
br, 0.0f, half), clampf(c.
bl, 0.0f, half)
167 { r.
x + rad[0], r.
y + rad[0] },
168 { r.
x + r.
w - rad[1], r.
y + rad[1] },
169 { r.
x + r.
w - rad[2], r.
y + r.
h - rad[2] },
170 { r.
x + rad[3], r.
y + r.
h - rad[3] }
174 { r.
x - d, r.
y - d },
175 { r.
x + r.
w + d, r.
y - d },
176 { r.
x + r.
w + d, r.
y + r.
h + d },
177 { r.
x - d, r.
y + r.
h + d }
179 const float start[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
181 ring.pts.reserve(4 * (n + 1));
182 for(
int k = 0; k < 4; k++)
184 float rr = rad[k] + d;
185 for(
int i = 0; i <= n; i++)
189 float th = (start[k] + 90.0f * (float)i / (float)n) * kPi / 180.0f;
190 GuiPoint p = { centre[k].
x + rr * cosf(th), centre[k].
y + rr * sinf(th) };
191 ring.pts.push_back(p);
194 ring.pts.push_back(sharp[k]);
200Ring ellipseRing(
const GuiRect& r,
float d,
int count)
203 float cx = r.
x + r.
w * 0.5f, cy = r.
y + r.
h * 0.5f;
204 float rx = std::max(0.0f, r.
w * 0.5f + d), ry = std::max(0.0f, r.
h * 0.5f + d);
205 ring.pts.reserve(count);
206 for(
int i = 0; i < count; i++)
208 float t = 2.0f * kPi * (float)i / (
float)count;
209 GuiPoint p = { cx + rx * cosf(t), cy + ry * sinf(t) };
210 ring.pts.push_back(p);
217 std::vector<GuiPoint> p;
218 std::vector<GuiPoint> m;
219 std::vector<GuiPoint> edge;
220 std::vector<GuiPoint> len;
225bool buildPoly(
const std::vector<GuiPoint>& in, Poly& out)
231 for(
size_t i = 0; i < in.size(); i++)
234 if(!out.p.empty() && fabsf(q.
x - out.p.back().x) < 1e-4f && fabsf(q.
y - out.p.back().y) < 1e-4f)
238 while(out.p.size() > 1 && fabsf(out.p.front().x - out.p.back().x) < 1e-4f && fabsf(out.p.front().y - out.p.back().y) < 1e-4f)
240 size_t n = out.p.size();
245 for(
size_t i = 0; i < n; i++)
248 const GuiPoint& b = out.p[(i + 1) % n];
249 area += a.
x * b.
y - b.
x * a.
y;
251 if(fabsf(area) < 1e-6f)
254 std::reverse(out.p.begin(), out.p.end());
256 float minx = out.p[0].x, maxx = minx, miny = out.p[0].y, maxy = miny, sx = 0, sy = 0;
257 for(
size_t i = 0; i < n; i++)
259 minx = std::min(minx, out.p[i].x);
260 maxx = std::max(maxx, out.p[i].x);
261 miny = std::min(miny, out.p[i].y);
262 maxy = std::max(maxy, out.p[i].y);
268 out.bounds.
w = maxx - minx;
269 out.bounds.
h = maxy - miny;
270 out.centre.
x = sx / (float)n;
271 out.centre.
y = sy / (float)n;
273 for(
size_t i = 0; i < n; i++)
275 const GuiPoint& a = out.p[(i + n - 1) % n];
277 const GuiPoint& c = out.p[(i + 1) % n];
278 float e1x = b.
x - a.
x, e1y = b.
y - a.
y, l1 = sqrtf(e1x * e1x + e1y * e1y);
279 float e2x = c.
x - b.
x, e2y = c.
y - b.
y, l2 = sqrtf(e2x * e2x + e2y * e2y);
280 float n1x = e1y / l1, n1y = -e1x / l1;
281 float n2x = e2y / l2, n2y = -e2x / l2;
282 float denom = 1.0f + n1x * n2x + n1y * n2y;
285 GuiPoint m = { (n1x + n2x) / denom, (n1y + n2y) / denom };
287 GuiPoint along = { (m.
x * e1x + m.
y * e1y) / l1, (m.
x * e2x + m.
y * e2y) / l2 };
289 out.edge.push_back(along);
290 out.len.push_back(lens);
295Ring polyRing(
const Poly& poly,
float d)
298 ring.pts.reserve(poly.p.size());
299 for(
size_t i = 0; i < poly.p.size(); i++)
304 float toNext = d * poly.edge[i].y;
305 float toPrev = -d * poly.edge[i].x;
306 if(toNext > 0.5f * poly.len[i].y)
307 k = 0.5f * poly.len[i].y / toNext;
308 if(toPrev > 0.5f * poly.len[i].x)
309 k = std::min(k, 0.5f * poly.len[i].x / toPrev);
310 GuiPoint q = { poly.p[i].x + k * d * poly.m[i].x, poly.p[i].y + k * d * poly.m[i].y };
311 ring.pts.push_back(q);
316float smoothstep01(
float t)
318 t = clampf(t, 0.0f, 1.0f);
319 return t * t * (3.0f - 2.0f * t);
324bool isConvex(
const std::vector<GuiPoint>& p)
328 for(
size_t i = 0; i < n; i++)
333 float cr = (b.
x - a.
x) * (c.y - b.
y) - (b.
y - a.
y) * (c.x - b.
x);
334 if(fabsf(cr) < 1e-6f)
337 sign = cr > 0.0f ? 1.0f : -1.0f;
338 else if((cr > 0.0f ? 1.0f : -1.0f) != sign)
346 float d1 = ((b.
x - a.
x) * (p.
y - a.
y) - (b.
y - a.
y) * (p.
x - a.
x)) * sgn;
347 float d2 = ((c.
x - b.
x) * (p.
y - b.
y) - (c.
y - b.
y) * (p.
x - b.
x)) * sgn;
348 float d3 = ((a.
x - c.
x) * (p.
y - c.
y) - (a.
y - c.
y) * (p.
x - c.
x)) * sgn;
349 return d1 >= 0.0f && d2 >= 0.0f && d3 >= 0.0f;
355void triangulate(
const std::vector<GuiPoint>& p, std::vector<size_t>& tri)
361 for(
size_t i = 0; i < n; i++)
365 area += a.
x * b.
y - b.
x * a.
y;
367 const float sgn = area >= 0.0f ? 1.0f : -1.0f;
369 std::vector<size_t> idx(n);
370 for(
size_t i = 0; i < n; i++)
373 size_t i = 0, sinceClip = 0;
374 while(idx.size() > 3)
376 size_t m = idx.size();
380 for(
size_t k = 1; k + 1 < m; k++)
382 tri.push_back(idx[0]);
383 tri.push_back(idx[k]);
384 tri.push_back(idx[k + 1]);
389 const size_t ia = idx[(i + m - 1) % m], ib = idx[i], ic = idx[(i + 1) % m];
390 const GuiPoint &a = p[ia], &b = p[ib], &c = p[ic];
391 float cr = ((b.x - a.
x) * (c.y - b.y) - (b.y - a.
y) * (c.x - b.x)) * sgn;
392 bool clip =
false, emit =
false;
393 if(fabsf(cr) < 1e-6f)
398 for(
size_t k = 0; k < m && empty; k++)
401 if(ik == ia || ik == ib || ik == ic)
404 if((q.
x == a.
x && q.
y == a.
y) || (q.
x == b.x && q.
y == b.y) || (q.
x == c.x && q.
y == c.y))
406 if(inTriangle(q, a, b, c, sgn))
420 idx.erase(idx.begin() + (
long)i);
431 tri.push_back(idx[0]);
432 tri.push_back(idx[1]);
433 tri.push_back(idx[2]);
439void fillConcave(std::vector<MeshVertex>& out,
float feather,
const Poly& poly,
const GuiFill& fill,
bool edgeFringe)
441 FillEval ev(fill, poly.bounds);
442 const float h = feather * 0.5f;
443 Ring inner = polyRing(poly, -h);
444 std::vector<size_t> tri;
445 triangulate(inner.pts, tri);
446 for(
size_t k = 0; k + 2 < tri.size(); k += 3)
448 const GuiPoint &a = inner.pts[tri[k]], &b = inner.pts[tri[k + 1]], &c = inner.pts[tri[k + 2]];
449 pushTri(out, a, ev.at(a, 1.0f), b, ev.at(b, 1.0f), c, ev.at(c, 1.0f));
451 if(feather > 0.0f && edgeFringe)
452 emitBand(out, inner, 1.0f, polyRing(poly, h), 0.0f, ev);
478 feather(featherWidth < 0.0f ? 0.0f : featherWidth),
479 tolerance(curveTolerance < 0.01f ? 0.01f : curveTolerance)
489static void fillShape(std::vector<MeshVertex>& out,
float feather,
const RingFn& ring,
GuiPoint centre,
const GuiRect& bounds,
const GuiFill& fill,
bool edgeFringe)
491 FillEval ev(fill, bounds);
492 float h = feather * 0.5f;
493 Ring inner = ring(-h);
494 emitFan(out, centre, inner, ev, 1.0f);
495 if(feather > 0.0f && edgeFringe)
496 emitBand(out, inner, 1.0f, ring(h), 0.0f, ev);
502static void strokeShape(std::vector<MeshVertex>& out,
float feather,
const RingFn& ring,
const GuiRect& bounds,
float width,
STROKE align,
const GuiFill& fill)
511 default: d1 = -width * 0.5f; d2 = width * 0.5f;
break;
513 FillEval ev(fill, bounds);
514 float h = feather * 0.5f;
517 emitBand(out, ring(d1), 1.0f, ring(d2), 1.0f, ev);
524 float mid = (d1 + d2) * 0.5f;
525 float a = std::min(1.0f, 2.0f * width / feather);
526 float hb = width / a;
528 emitBand(out, ring(mid - hb), 0.0f, rm, a, ev);
529 emitBand(out, rm, a, ring(mid + hb), 0.0f, ev);
532 Ring a0 = ring(d1 - h), a1 = ring(d1 + h), b1 = ring(d2 - h), b0 = ring(d2 + h);
533 emitBand(out, a0, 0.0f, a1, 1.0f, ev);
534 emitBand(out, a1, 1.0f, b1, 1.0f, ev);
535 emitBand(out, b1, 1.0f, b0, 0.0f, ev);
540 std::vector<GuiPoint> pts(4);
541 pts[0].x = r.
x; pts[0].y = r.
y;
542 pts[1].x = r.
x + r.
w; pts[1].y = r.
y;
543 pts[2].x = r.
x + r.
w; pts[2].y = r.
y + r.
h;
544 pts[3].x = r.
x; pts[3].y = r.
y + r.
h;
550 if(r.
w <= 0.0f || r.
h <= 0.0f)
552 if(c.
tl <= 0.0f && c.
tr <= 0.0f && c.
br <= 0.0f && c.
bl <= 0.0f)
557 float maxR = std::max(std::max(c.
tl, c.
tr), std::max(c.
br, c.
bl));
558 maxR = std::min(maxR, 0.5f * std::min(r.
w, r.
h));
559 int n = arcCount(maxR + feather, tolerance);
560 GuiPoint centre = { r.
x + r.
w * 0.5f, r.
y + r.
h * 0.5f };
561 RingFn ring = [=](
float d) {
return roundRectRing(r, c, d, n); };
562 fillShape(verts, feather, ring, centre, r, fill, edgeFringe);
567 if(r.
w <= 0.0f || r.
h <= 0.0f)
569 int n = circleCount(0.5f * std::max(r.
w, r.
h) + feather, tolerance);
570 GuiPoint centre = { r.
x + r.
w * 0.5f, r.
y + r.
h * 0.5f };
571 RingFn ring = [=](
float d) {
return ellipseRing(r, d, n); };
572 fillShape(verts, feather, ring, centre, r, fill, edgeFringe);
578 if(!buildPoly(pts, poly))
580 if(!isConvex(poly.p))
582 fillConcave(verts, feather, poly, fill, edgeFringe);
585 RingFn ring = [&poly](
float d) {
return polyRing(poly, d); };
586 fillShape(verts, feather, ring, poly.centre, poly.bounds, fill, edgeFringe);
591 float dx = b.
x - a.
x, dy = b.
y - a.
y;
592 float len = sqrtf(dx * dx + dy * dy);
593 if(len < 1e-4f || width <= 0.0f)
595 float nx = dy / len * width * 0.5f, ny = -dx / len * width * 0.5f;
596 std::vector<GuiPoint> pts(4);
597 pts[0].x = a.
x + nx; pts[0].y = a.
y + ny;
598 pts[1].x = b.
x + nx; pts[1].y = b.
y + ny;
599 pts[2].x = b.
x - nx; pts[2].y = b.
y - ny;
600 pts[3].x = a.
x - nx; pts[3].y = a.
y - ny;
606 if(r.
w <= 0.0f || r.
h <= 0.0f || width <= 0.0f)
608 float half = 0.5f * std::min(r.
w, r.
h);
610 width = std::min(width, half);
612 width = std::min(width, 2.0f * half);
613 float maxR = std::min(std::max(std::max(c.
tl, c.
tr), std::max(c.
br, c.
bl)), half);
614 int n = arcCount(maxR + width + feather, tolerance);
615 RingFn ring = [=](
float d) {
return roundRectRing(r, c, d, n); };
616 strokeShape(verts, feather, ring, r, width, align, fill);
621 if(r.
w <= 0.0f || r.
h <= 0.0f || width <= 0.0f)
623 float half = 0.5f * std::min(r.
w, r.
h);
625 width = std::min(width, half);
627 width = std::min(width, 2.0f * half);
628 int n = circleCount(0.5f * std::max(r.
w, r.
h) + width + feather, tolerance);
629 RingFn ring = [=](
float d) {
return ellipseRing(r, d, n); };
630 strokeShape(verts, feather, ring, r, width, align, fill);
636 if(width <= 0.0f || !buildPoly(pts, poly))
638 RingFn ring = [&poly](
float d) {
return polyRing(poly, d); };
639 strokeShape(verts, feather, ring, poly.bounds, width, align, fill);
644 if(radius <= 0.0f || width <= 0.0f || sweepDeg == 0.0f)
648 startDeg += sweepDeg;
649 sweepDeg = -sweepDeg;
651 sweepDeg = std::min(sweepDeg, 360.0f);
653 float h = feather * 0.5f;
657 if(feather > 0.0f && width < feather)
659 float a = std::min(1.0f, 2.0f * width / feather);
660 float hb = width / a;
661 radii[0] = radius - hb; alphas[0] = 0.0f;
662 radii[1] = radius; alphas[1] = a;
663 radii[2] = radius + hb; alphas[2] = 0.0f;
666 else if(feather > 0.0f)
668 radii[0] = radius - width * 0.5f - h; alphas[0] = 0.0f;
669 radii[1] = radius - width * 0.5f + h; alphas[1] = 1.0f;
670 radii[2] = radius + width * 0.5f - h; alphas[2] = 1.0f;
671 radii[3] = radius + width * 0.5f + h; alphas[3] = 0.0f;
676 radii[0] = radius - width * 0.5f; alphas[0] = 1.0f;
677 radii[1] = radius + width * 0.5f; alphas[1] = 1.0f;
681 float outer = radius + width * 0.5f + h;
682 float step = acosf(clampf(1.0f - tolerance / outer, -1.0f, 1.0f));
685 int segs = (int)clampf(ceilf((sweepDeg * kPi / 180.0f) / step), 1.0f, 128.0f);
687 GuiRect bounds = { centre.
x - outer, centre.
y - outer, 2.0f * outer, 2.0f * outer };
690 std::vector<Ring> samples((
size_t)rings);
691 for(
int k = 0; k < rings; k++)
693 for(
int i = 0; i <= segs; i++)
695 float th = (startDeg + sweepDeg * (float)i / (float)segs) * kPi / 180.0f;
696 GuiPoint p = { centre.
x + radii[k] * cosf(th), centre.
y + radii[k] * sinf(th) };
697 samples[(size_t)k].pts.push_back(p);
700 for(
int k = 0; k + 1 < rings; k++)
702 const Ring& A = samples[(size_t)k];
703 const Ring& B = samples[(size_t)k + 1];
704 for(
int i = 0; i < segs; i++)
706 PixelColor ca0 = ev.at(A.pts[i], alphas[k]), ca1 = ev.at(A.pts[i + 1], alphas[k]);
707 PixelColor cb0 = ev.at(B.pts[i], alphas[k + 1]), cb1 = ev.at(B.pts[i + 1], alphas[k + 1]);
708 pushTri(verts, A.pts[i], ca0, B.pts[i], cb0, B.pts[i + 1], cb1);
709 pushTri(verts, A.pts[i], ca0, B.pts[i + 1], cb1, A.pts[i + 1], ca1);
716 if(r.
w <= 0.0f || r.
h <= 0.0f || color.a == 0)
718 blur = std::max(blur, 0.0f);
722 const int maxSteps = 6;
723 const int steps = (int)clampf(ceilf(blur / 1.5f), 2.0f, (
float)maxSteps);
724 float maxR = std::min(std::max(std::max(c.
tl, c.
tr), std::max(c.
br, c.
bl)), 0.5f * std::min(r.
w, r.
h));
725 int n = arcCount(maxR + std::max(spread, 0.0f) + blur, tolerance * 2.0f);
727 GuiPoint centre = { s.
x + s.
w * 0.5f, s.
y + s.
h * 0.5f };
729 std::vector<Ring> rings;
730 float alpha[maxSteps + 1];
731 for(
int k = 0; k <= steps; k++)
733 float t = (float)k / (
float)steps;
734 rings.push_back(roundRectRing(s, c, spread - blur * 0.5f + blur * t, n));
735 alpha[k] = 1.0f - smoothstep01(t);
737 emitFan(verts, centre, rings[0], ev, alpha[0]);
738 for(
int k = 0; k < steps; k++)
739 emitBand(verts, rings[(
size_t)k], alpha[k], rings[(
size_t)k + 1], alpha[k + 1], ev);
744 std::vector<GuiPoint> out;
745 if(r.
w <= 0.0f || r.
h <= 0.0f)
748 const float half = 0.5f * std::min(r.
w, r.
h);
749 const float rad = clampf(radius, 0.0f, half);
750 const int n = arcCount(rad, tolerance < 0.01f ? 0.01f : tolerance);
751 Ring ring = roundRectRing(r,
GuiCorners(rad), 0.0f, n);
753 const float a = angleDeg * kPi / 180.0f;
754 const float c = cosf(a), s = sinf(a);
755 const float cx = r.
x + r.
w * 0.5f, cy = r.
y + r.
h * 0.5f;
756 out.reserve(ring.pts.size());
757 for(
size_t i = 0; i < ring.pts.size(); i++)
759 const float dx = ring.pts[i].x - cx, dy = ring.pts[i].y - cy;
760 GuiPoint p = { cx + dx * c - dy * s, cy + dx * s + dy * c };
772 float dx = p3.
x - p0.
x, dy = p3.
y - p0.
y;
773 float len2 = dx * dx + dy * dy;
777 d1 = sqrtf((p1.
x - p0.
x) * (p1.
x - p0.
x) + (p1.
y - p0.
y) * (p1.
y - p0.
y));
778 d2 = sqrtf((p2.
x - p0.
x) * (p2.
x - p0.
x) + (p2.
y - p0.
y) * (p2.
y - p0.
y));
782 float inv = 1.0f / sqrtf(len2);
783 d1 = fabsf((p1.
x - p0.
x) * dy - (p1.
y - p0.
y) * dx) * inv;
784 d2 = fabsf((p2.
x - p0.
x) * dy - (p2.
y - p0.
y) * dx) * inv;
787 if(depth >= 12 || 0.75f * std::max(d1, d2) <= tol)
792 GuiPoint p01 = { (p0.
x + p1.
x) * 0.5f, (p0.
y + p1.
y) * 0.5f };
793 GuiPoint p12 = { (p1.
x + p2.
x) * 0.5f, (p1.
y + p2.
y) * 0.5f };
794 GuiPoint p23 = { (p2.
x + p3.
x) * 0.5f, (p2.
y + p3.
y) * 0.5f };
795 GuiPoint a = { (p01.
x + p12.
x) * 0.5f, (p01.
y + p12.
y) * 0.5f };
796 GuiPoint b = { (p12.
x + p23.
x) * 0.5f, (p12.
y + p23.
y) * 0.5f };
797 GuiPoint m = { (a.
x + b.
x) * 0.5f, (a.
y + b.
y) * 0.5f };
798 flattenCubic(out, p0, p01, a, m, tol, depth + 1);
799 flattenCubic(out, m, b, p23, p3, tol, depth + 1);
806 std::vector<std::vector<GuiPoint> > contours;
807 std::vector<GuiPoint> cur;
808 if(tolerance < 0.001f)
811 auto pt = [&](
size_t i) {
GuiPoint p = { path.
pts[i * 2] * scale, path.
pts[i * 2 + 1] * scale };
return p; };
812 auto finish = [&]() {
814 if(cur.size() > 1 && fabsf(cur.front().x - cur.back().x) < 1e-5f && fabsf(cur.front().y - cur.back().y) < 1e-5f)
817 contours.push_back(cur);
820 for(
size_t c = 0; c < path.
cmds.size(); c++)
826 if((k + 1) * 2 > path.
pts.size())
return contours;
827 cur.push_back(pt(k++));
830 if((k + 1) * 2 > path.
pts.size())
return contours;
831 cur.push_back(pt(k++));
834 if((k + 3) * 2 > path.
pts.size() || cur.empty())
return contours;
835 flattenCubic(cur, cur.back(), pt(k), pt(k + 1), pt(k + 2), tolerance, 0);
852 if(path.
pts.size() < 2)
854 float minx = path.
pts[0], maxx = minx, miny = path.
pts[1], maxy = miny;
855 for(
size_t i = 0; i + 1 < path.
pts.size(); i += 2)
857 minx = std::min(minx, path.
pts[i]);
858 maxx = std::max(maxx, path.
pts[i]);
859 miny = std::min(miny, path.
pts[i + 1]);
860 maxy = std::max(maxy, path.
pts[i + 1]);
864 r.
w = (maxx - minx) * scale;
865 r.
h = (maxy - miny) * scale;
void addShadow(const GuiRect &r, const GuiCorners &corners, float blur, float spread, float dx, float dy, PixelColor color)
Adds a soft drop shadow or glow for a rounded rectangle.
void clear()
Removes all geometry (keeps the feather and tolerance settings)
GuiMesh(float featherWidth=1.0f, float curveTolerance=0.3f)
Constructor.
void addLine(GuiPoint a, GuiPoint b, float width, const GuiFill &fill)
Adds a straight line with flat ends.
void strokePolygon(const std::vector< GuiPoint > &pts, float width, STROKE align, const GuiFill &fill)
Adds a polygon outline with mitered corners (the miter is capped on very sharp corners).
void strokeEllipse(const GuiRect &r, float width, STROKE align, const GuiFill &fill)
Adds an ellipse outline.
void addRoundRect(const GuiRect &r, const GuiCorners &corners, const GuiFill &fill, bool edgeFringe=true)
Adds a filled rounded rectangle.
void strokeArc(GuiPoint centre, float radius, float startDeg, float sweepDeg, float width, PixelColor color)
Adds an arc stroke centered on a radius, with flat ends.
void addPolygon(const std::vector< GuiPoint > &pts, const GuiFill &fill, bool edgeFringe=true)
Adds a filled polygon (triangle, chevron, a controller silhouette, ...).
void addRect(const GuiRect &r, const GuiFill &fill, bool edgeFringe=true)
Adds a filled rectangle.
void strokeRoundRect(const GuiRect &r, const GuiCorners &corners, float width, STROKE align, const GuiFill &fill)
Adds a rounded-rectangle outline.
void addEllipse(const GuiRect &r, const GuiFill &fill, bool edgeFringe=true)
Adds a filled ellipse inscribed in a rectangle (a circle when w == h)
std::vector< GuiPoint > GuiRoundRectPolygon(const GuiRect &r, float radius, float angleDeg, float tolerance)
A rounded rectangle as a convex polygon, rotated about its center.
STROKE
Where a stroke sits relative to the shape's geometric edge.
std::vector< std::vector< GuiPoint > > GuiFlattenPath(const GuiPath &path, float scale, float tolerance)
Turns a path into one polygon per contour, multiplied by scale.
GuiRect GuiPathBounds(const GuiPath &path, float scale)
Gets the bounding box of a path's points (control points included, so it can be a little larger than ...
@ INSIDE
Entirely inside the edge (the shape keeps its size)
@ CENTRE
Centered on the edge.
@ OUTSIDE
Entirely outside the edge.
Per-corner radii, in the order top-left, top-right, bottom-right, bottom-left.
float bl
Bottom-left radius.
float tr
Top-right radius.
float br
Bottom-right radius.
A solid color or a two-color linear gradient.
bool gradient
true for a gradient, false for a solid color.
PixelColor from
Solid color, or the gradient's start color.
static GuiFill linear(PixelColor a, PixelColor b, float angleDeg)
A linear gradient across the shape's bounding box.
static GuiFill solid(PixelColor c)
A single flat color.
PixelColor to
The gradient's end color (unused when solid).
float angle
Gradient direction in degrees: 0 = left to right, 90 = top to bottom.
A closed outline made of straight lines and cubic Beziers, in whatever units the caller likes.
std::vector< uint8_t > cmds
One CMD per segment.
std::vector< float > pts
x, y pairs, as many as the commands consume.
A 2D point in design-canvas pixels.
An axis-aligned rectangle (top-left origin) in design-canvas pixels.
A single 2D vertex with its own color, in design-canvas coordinates.
float y
Y position (design-canvas pixels).
float x
X position (design-canvas pixels).
PixelColor c
Straight (non-premultiplied) RGBA color.
8-bit-per-channel RGBA color used by the video interfaces.
uint8_t g
Green color component.
uint8_t r
Red color component.
uint8_t b
Blue color component.
uint8_t a
Alpha component.