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;
33const float kCurveMargin = 0.5f;
36int arcCount(
float radius,
float tol)
41 float ang = 2.0f * acosf(clampf(1.0f - tol / radius, -1.0f, 1.0f));
44 return (
int)clampf(ceilf((kPi * 0.5f) / ang), 2.0f, 24.0f);
48int circleCount(
float radius,
float tol)
53 float ang = 2.0f * acosf(clampf(1.0f - tol / radius, -1.0f, 1.0f));
56 return (
int)clampf(ceilf((2.0f * kPi) / ang), 12.0f, 96.0f);
64 float ux, uy, t0, invSpan;
66 FillEval(
const GuiFill& f,
const GuiRect& b) :
67 from(f.from), to(f.to), gradient(f.gradient), ux(0), uy(0), t0(0), invSpan(0)
71 float rad = f.
angle * kPi / 180.0f;
74 float d0 = b.
x * ux + b.
y * uy;
75 float d1 = (b.
x + b.
w) * ux + b.
y * uy;
76 float d2 = b.
x * ux + (b.
y + b.
h) * uy;
77 float d3 = (b.
x + b.
w) * ux + (b.
y + b.
h) * uy;
78 float lo = std::min(std::min(d0, d1), std::min(d2, d3));
79 float hi = std::max(std::max(d0, d1), std::max(d2, d3));
81 invSpan = (hi - lo) > 1e-6f ? 1.0f / (hi - lo) : 0.0f;
84 static uint8_t mix(uint8_t a, uint8_t b,
float t)
86 return (uint8_t)clampf(a + (b - a) * t + 0.5f, 0.0f, 255.0f);
89 PixelColor at(GuiPoint p,
float alphaScale)
const
94 float t = clampf((p.
x * ux + p.
y * uy - t0) * invSpan, 0.0f, 1.0f);
95 c.
r = mix(from.r, to.r, t);
96 c.
g = mix(from.g, to.g, t);
97 c.
b = mix(from.b, to.b, t);
98 c.
a = mix(from.a, to.a, t);
100 c.
a = (uint8_t)clampf(c.
a * alphaScale + 0.5f, 0.0f, 255.0f);
119 float s = (b.
x - a.
x) * (c.
y - a.
y) - (c.
x - a.
x) * (b.
y - a.
y);
120 pushVertex(out, a, ca);
123 pushVertex(out, c, cc);
124 pushVertex(out, b, cb);
128 pushVertex(out, b, cb);
129 pushVertex(out, c, cc);
135void emitFan(std::vector<MeshVertex>& out,
GuiPoint centre,
const Ring& r,
const FillEval& e,
float a)
137 size_t n = r.pts.size();
141 for(
size_t i = 0; i < n; i++)
144 const GuiPoint& p1 = r.pts[(i + 1) % n];
145 pushTri(out, centre, cc, p0, e.at(p0, a), p1, e.at(p1, a));
150void emitBand(std::vector<MeshVertex>& out,
const Ring& A,
float aA,
const Ring& B,
float aB,
const FillEval& e)
152 size_t n = A.pts.size();
153 if(n < 3 || B.pts.size() != n)
155 for(
size_t i = 0; i < n; i++)
157 size_t j = (i + 1) % n;
158 PixelColor ca0 = e.at(A.pts[i], aA), ca1 = e.at(A.pts[j], aA);
159 PixelColor cb0 = e.at(B.pts[i], aB), cb1 = e.at(B.pts[j], aB);
160 pushTri(out, A.pts[i], ca0, B.pts[i], cb0, B.pts[j], cb1);
161 pushTri(out, A.pts[i], ca0, B.pts[j], cb1, A.pts[j], ca1);
168 float half = 0.5f * std::min(r.
w, r.
h);
169 d = std::max(d, -half);
171 clampf(c.
tl, 0.0f, half), clampf(c.
tr, 0.0f, half),
172 clampf(c.
br, 0.0f, half), clampf(c.
bl, 0.0f, half)
175 { r.
x + rad[0], r.
y + rad[0] },
176 { r.
x + r.
w - rad[1], r.
y + rad[1] },
177 { r.
x + r.
w - rad[2], r.
y + r.
h - rad[2] },
178 { r.
x + rad[3], r.
y + r.
h - rad[3] }
182 { r.
x - d, r.
y - d },
183 { r.
x + r.
w + d, r.
y - d },
184 { r.
x + r.
w + d, r.
y + r.
h + d },
185 { r.
x - d, r.
y + r.
h + d }
187 const float start[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
189 ring.pts.reserve(4 * (n + 1));
190 for(
int k = 0; k < 4; k++)
192 float rr = rad[k] + d;
193 for(
int i = 0; i <= n; i++)
197 float th = (start[k] + 90.0f * (float)i / (float)n) * kPi / 180.0f;
198 GuiPoint p = { centre[k].
x + rr * cosf(th), centre[k].
y + rr * sinf(th) };
199 ring.pts.push_back(p);
202 ring.pts.push_back(sharp[k]);
208Ring ellipseRing(
const GuiRect& r,
float d,
int count)
211 float cx = r.
x + r.
w * 0.5f, cy = r.
y + r.
h * 0.5f;
212 float rx = std::max(0.0f, r.
w * 0.5f + d), ry = std::max(0.0f, r.
h * 0.5f + d);
213 ring.pts.reserve(count);
214 for(
int i = 0; i < count; i++)
216 float t = 2.0f * kPi * (float)i / (
float)count;
217 GuiPoint p = { cx + rx * cosf(t), cy + ry * sinf(t) };
218 ring.pts.push_back(p);
225 std::vector<GuiPoint> p;
226 std::vector<GuiPoint> m;
227 std::vector<GuiPoint> edge;
228 std::vector<GuiPoint> len;
233bool buildPoly(
const std::vector<GuiPoint>& in, Poly& out)
239 for(
size_t i = 0; i < in.size(); i++)
242 if(!out.p.empty() && fabsf(q.
x - out.p.back().x) < 1e-4f && fabsf(q.
y - out.p.back().y) < 1e-4f)
246 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)
248 size_t n = out.p.size();
253 for(
size_t i = 0; i < n; i++)
256 const GuiPoint& b = out.p[(i + 1) % n];
257 area += a.
x * b.
y - b.
x * a.
y;
259 if(fabsf(area) < 1e-6f)
262 std::reverse(out.p.begin(), out.p.end());
264 float minx = out.p[0].x, maxx = minx, miny = out.p[0].y, maxy = miny, sx = 0, sy = 0;
265 for(
size_t i = 0; i < n; i++)
267 minx = std::min(minx, out.p[i].x);
268 maxx = std::max(maxx, out.p[i].x);
269 miny = std::min(miny, out.p[i].y);
270 maxy = std::max(maxy, out.p[i].y);
276 out.bounds.
w = maxx - minx;
277 out.bounds.
h = maxy - miny;
278 out.centre.
x = sx / (float)n;
279 out.centre.
y = sy / (float)n;
281 for(
size_t i = 0; i < n; i++)
283 const GuiPoint& a = out.p[(i + n - 1) % n];
285 const GuiPoint& c = out.p[(i + 1) % n];
286 float e1x = b.
x - a.
x, e1y = b.
y - a.
y, l1 = sqrtf(e1x * e1x + e1y * e1y);
287 float e2x = c.
x - b.
x, e2y = c.
y - b.
y, l2 = sqrtf(e2x * e2x + e2y * e2y);
288 float n1x = e1y / l1, n1y = -e1x / l1;
289 float n2x = e2y / l2, n2y = -e2x / l2;
290 float denom = 1.0f + n1x * n2x + n1y * n2y;
293 GuiPoint m = { (n1x + n2x) / denom, (n1y + n2y) / denom };
295 GuiPoint along = { (m.
x * e1x + m.
y * e1y) / l1, (m.
x * e2x + m.
y * e2y) / l2 };
297 out.edge.push_back(along);
298 out.len.push_back(lens);
303Ring polyRing(
const Poly& poly,
float d)
306 ring.pts.reserve(poly.p.size());
307 for(
size_t i = 0; i < poly.p.size(); i++)
312 float toNext = d * poly.edge[i].y;
313 float toPrev = -d * poly.edge[i].x;
314 if(toNext > 0.5f * poly.len[i].y)
315 k = 0.5f * poly.len[i].y / toNext;
316 if(toPrev > 0.5f * poly.len[i].x)
317 k = std::min(k, 0.5f * poly.len[i].x / toPrev);
318 GuiPoint q = { poly.p[i].x + k * d * poly.m[i].x, poly.p[i].y + k * d * poly.m[i].y };
319 ring.pts.push_back(q);
324float smoothstep01(
float t)
326 t = clampf(t, 0.0f, 1.0f);
327 return t * t * (3.0f - 2.0f * t);
332bool isConvex(
const std::vector<GuiPoint>& p)
336 for(
size_t i = 0; i < n; i++)
341 float cr = (b.
x - a.
x) * (c.y - b.
y) - (b.
y - a.
y) * (c.x - b.
x);
342 if(fabsf(cr) < 1e-6f)
345 sign = cr > 0.0f ? 1.0f : -1.0f;
346 else if((cr > 0.0f ? 1.0f : -1.0f) != sign)
354 float d1 = ((b.
x - a.
x) * (p.
y - a.
y) - (b.
y - a.
y) * (p.
x - a.
x)) * sgn;
355 float d2 = ((c.
x - b.
x) * (p.
y - b.
y) - (c.
y - b.
y) * (p.
x - b.
x)) * sgn;
356 float d3 = ((a.
x - c.
x) * (p.
y - c.
y) - (a.
y - c.
y) * (p.
x - c.
x)) * sgn;
357 return d1 >= 0.0f && d2 >= 0.0f && d3 >= 0.0f;
363void triangulate(
const std::vector<GuiPoint>& p, std::vector<size_t>& tri)
369 for(
size_t i = 0; i < n; i++)
373 area += a.
x * b.
y - b.
x * a.
y;
375 const float sgn = area >= 0.0f ? 1.0f : -1.0f;
377 std::vector<size_t> idx(n);
378 for(
size_t i = 0; i < n; i++)
381 size_t i = 0, sinceClip = 0;
382 while(idx.size() > 3)
384 size_t m = idx.size();
388 for(
size_t k = 1; k + 1 < m; k++)
390 tri.push_back(idx[0]);
391 tri.push_back(idx[k]);
392 tri.push_back(idx[k + 1]);
397 const size_t ia = idx[(i + m - 1) % m], ib = idx[i], ic = idx[(i + 1) % m];
398 const GuiPoint &a = p[ia], &b = p[ib], &c = p[ic];
399 float cr = ((b.x - a.
x) * (c.y - b.y) - (b.y - a.
y) * (c.x - b.x)) * sgn;
400 bool clip =
false, emit =
false;
401 if(fabsf(cr) < 1e-6f)
406 for(
size_t k = 0; k < m && empty; k++)
409 if(ik == ia || ik == ib || ik == ic)
412 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))
414 if(inTriangle(q, a, b, c, sgn))
428 idx.erase(idx.begin() + (
long)i);
439 tri.push_back(idx[0]);
440 tri.push_back(idx[1]);
441 tri.push_back(idx[2]);
447void fillConcave(std::vector<MeshVertex>& out,
float feather,
const Poly& poly,
const GuiFill& fill,
bool edgeFringe)
449 FillEval ev(fill, poly.bounds);
450 const float h = feather * 0.5f;
451 Ring inner = polyRing(poly, -h);
452 std::vector<size_t> tri;
453 triangulate(inner.pts, tri);
454 for(
size_t k = 0; k + 2 < tri.size(); k += 3)
456 const GuiPoint &a = inner.pts[tri[k]], &b = inner.pts[tri[k + 1]], &c = inner.pts[tri[k + 2]];
457 pushTri(out, a, ev.at(a, 1.0f), b, ev.at(b, 1.0f), c, ev.at(c, 1.0f));
459 if(feather > 0.0f && edgeFringe)
460 emitBand(out, inner, 1.0f, polyRing(poly, h), 0.0f, ev);
486 feather(featherWidth < 0.0f ? 0.0f : featherWidth),
487 tolerance(curveTolerance < 0.01f ? 0.01f : curveTolerance)
497static void fillShape(std::vector<MeshVertex>& out,
float feather,
const RingFn& ring,
GuiPoint centre,
const GuiRect& bounds,
const GuiFill& fill,
bool edgeFringe)
499 FillEval ev(fill, bounds);
500 float h = feather * 0.5f;
501 Ring inner = ring(-h);
502 emitFan(out, centre, inner, ev, 1.0f);
503 if(feather > 0.0f && edgeFringe)
504 emitBand(out, inner, 1.0f, ring(h), 0.0f, ev);
510static void strokeShape(std::vector<MeshVertex>& out,
float feather,
const RingFn& ring,
const GuiRect& bounds,
float width,
STROKE align,
const GuiFill& fill)
519 default: d1 = -width * 0.5f; d2 = width * 0.5f;
break;
521 FillEval ev(fill, bounds);
522 float h = feather * 0.5f;
525 emitBand(out, ring(d1), 1.0f, ring(d2), 1.0f, ev);
532 float mid = (d1 + d2) * 0.5f;
533 float a = std::min(1.0f, 2.0f * width / feather);
534 float hb = width / a;
536 emitBand(out, ring(mid - hb), 0.0f, rm, a, ev);
537 emitBand(out, rm, a, ring(mid + hb), 0.0f, ev);
540 Ring a0 = ring(d1 - h), a1 = ring(d1 + h), b1 = ring(d2 - h), b0 = ring(d2 + h);
541 emitBand(out, a0, 0.0f, a1, 1.0f, ev);
542 emitBand(out, a1, 1.0f, b1, 1.0f, ev);
543 emitBand(out, b1, 1.0f, b0, 0.0f, ev);
548 std::vector<GuiPoint> pts(4);
549 pts[0].x = r.
x; pts[0].y = r.
y;
550 pts[1].x = r.
x + r.
w; pts[1].y = r.
y;
551 pts[2].x = r.
x + r.
w; pts[2].y = r.
y + r.
h;
552 pts[3].x = r.
x; pts[3].y = r.
y + r.
h;
558 if(r.
w <= 0.0f || r.
h <= 0.0f)
560 if(c.
tl <= 0.0f && c.
tr <= 0.0f && c.
br <= 0.0f && c.
bl <= 0.0f)
565 float maxR = std::max(std::max(c.
tl, c.
tr), std::max(c.
br, c.
bl));
566 maxR = std::min(maxR, 0.5f * std::min(r.
w, r.
h));
567 int n = arcCount(maxR + feather, tolerance);
568 GuiPoint centre = { r.
x + r.
w * 0.5f, r.
y + r.
h * 0.5f };
569 RingFn ring = [=](
float d) {
return roundRectRing(r, c, d, n); };
570 fillShape(verts, feather, ring, centre, r, fill, edgeFringe);
575 if(r.
w <= 0.0f || r.
h <= 0.0f)
577 int n = circleCount(0.5f * std::max(r.
w, r.
h) + feather, tolerance);
578 GuiPoint centre = { r.
x + r.
w * 0.5f, r.
y + r.
h * 0.5f };
579 RingFn ring = [=](
float d) {
return ellipseRing(r, d, n); };
580 fillShape(verts, feather, ring, centre, r, fill, edgeFringe);
586 if(!buildPoly(pts, poly))
588 if(!isConvex(poly.p))
590 fillConcave(verts, feather, poly, fill, edgeFringe);
593 RingFn ring = [&poly](
float d) {
return polyRing(poly, d); };
594 fillShape(verts, feather, ring, poly.centre, poly.bounds, fill, edgeFringe);
599 float dx = b.
x - a.
x, dy = b.
y - a.
y;
600 float len = sqrtf(dx * dx + dy * dy);
601 if(len < 1e-4f || width <= 0.0f)
603 float nx = dy / len * width * 0.5f, ny = -dx / len * width * 0.5f;
604 std::vector<GuiPoint> pts(4);
605 pts[0].x = a.
x + nx; pts[0].y = a.
y + ny;
606 pts[1].x = b.
x + nx; pts[1].y = b.
y + ny;
607 pts[2].x = b.
x - nx; pts[2].y = b.
y - ny;
608 pts[3].x = a.
x - nx; pts[3].y = a.
y - ny;
614 if(r.
w <= 0.0f || r.
h <= 0.0f || width <= 0.0f)
616 float half = 0.5f * std::min(r.
w, r.
h);
618 width = std::min(width, half);
620 width = std::min(width, 2.0f * half);
621 float maxR = std::min(std::max(std::max(c.
tl, c.
tr), std::max(c.
br, c.
bl)), half);
622 int n = arcCount(maxR + width + feather, tolerance);
623 RingFn ring = [=](
float d) {
return roundRectRing(r, c, d, n); };
624 strokeShape(verts, feather, ring, r, width, align, fill);
629 if(r.
w <= 0.0f || r.
h <= 0.0f || width <= 0.0f)
631 float half = 0.5f * std::min(r.
w, r.
h);
633 width = std::min(width, half);
635 width = std::min(width, 2.0f * half);
636 int n = circleCount(0.5f * std::max(r.
w, r.
h) + width + feather, tolerance);
637 RingFn ring = [=](
float d) {
return ellipseRing(r, d, n); };
638 strokeShape(verts, feather, ring, r, width, align, fill);
644 if(width <= 0.0f || !buildPoly(pts, poly))
646 RingFn ring = [&poly](
float d) {
return polyRing(poly, d); };
647 strokeShape(verts, feather, ring, poly.bounds, width, align, fill);
652 if(radius <= 0.0f || width <= 0.0f || sweepDeg == 0.0f)
656 startDeg += sweepDeg;
657 sweepDeg = -sweepDeg;
659 sweepDeg = std::min(sweepDeg, 360.0f);
661 float h = feather * 0.5f;
665 if(feather > 0.0f && width < feather)
667 float a = std::min(1.0f, 2.0f * width / feather);
668 float hb = width / a;
669 radii[0] = radius - hb; alphas[0] = 0.0f;
670 radii[1] = radius; alphas[1] = a;
671 radii[2] = radius + hb; alphas[2] = 0.0f;
674 else if(feather > 0.0f)
676 radii[0] = radius - width * 0.5f - h; alphas[0] = 0.0f;
677 radii[1] = radius - width * 0.5f + h; alphas[1] = 1.0f;
678 radii[2] = radius + width * 0.5f - h; alphas[2] = 1.0f;
679 radii[3] = radius + width * 0.5f + h; alphas[3] = 0.0f;
684 radii[0] = radius - width * 0.5f; alphas[0] = 1.0f;
685 radii[1] = radius + width * 0.5f; alphas[1] = 1.0f;
689 float outer = radius + width * 0.5f + h;
690 float step = acosf(clampf(1.0f - tolerance / outer, -1.0f, 1.0f));
693 int segs = (int)clampf(ceilf((sweepDeg * kPi / 180.0f) / step), 1.0f, 128.0f);
695 GuiRect bounds = { centre.
x - outer, centre.
y - outer, 2.0f * outer, 2.0f * outer };
698 std::vector<Ring> samples((
size_t)rings);
699 for(
int k = 0; k < rings; k++)
701 for(
int i = 0; i <= segs; i++)
703 float th = (startDeg + sweepDeg * (float)i / (float)segs) * kPi / 180.0f;
704 GuiPoint p = { centre.
x + radii[k] * cosf(th), centre.
y + radii[k] * sinf(th) };
705 samples[(size_t)k].pts.push_back(p);
708 for(
int k = 0; k + 1 < rings; k++)
710 const Ring& A = samples[(size_t)k];
711 const Ring& B = samples[(size_t)k + 1];
712 for(
int i = 0; i < segs; i++)
714 PixelColor ca0 = ev.at(A.pts[i], alphas[k]), ca1 = ev.at(A.pts[i + 1], alphas[k]);
715 PixelColor cb0 = ev.at(B.pts[i], alphas[k + 1]), cb1 = ev.at(B.pts[i + 1], alphas[k + 1]);
716 pushTri(verts, A.pts[i], ca0, B.pts[i], cb0, B.pts[i + 1], cb1);
717 pushTri(verts, A.pts[i], ca0, B.pts[i + 1], cb1, A.pts[i + 1], ca1);
724 if(r.
w <= 0.0f || r.
h <= 0.0f || color.a == 0)
726 blur = std::max(blur, 0.0f);
730 const int maxSteps = 6;
731 const int steps = (int)clampf(ceilf(blur / 1.5f), 2.0f, (
float)maxSteps);
732 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));
733 int n = arcCount(maxR + std::max(spread, 0.0f) + blur, tolerance * 2.0f);
735 GuiPoint centre = { s.
x + s.
w * 0.5f, s.
y + s.
h * 0.5f };
737 std::vector<Ring> rings;
738 float alpha[maxSteps + 1];
739 for(
int k = 0; k <= steps; k++)
741 float t = (float)k / (
float)steps;
742 rings.push_back(roundRectRing(s, c, spread - blur * 0.5f + blur * t, n));
743 alpha[k] = 1.0f - smoothstep01(t);
745 emitFan(verts, centre, rings[0], ev, alpha[0]);
746 for(
int k = 0; k < steps; k++)
747 emitBand(verts, rings[(
size_t)k], alpha[k], rings[(
size_t)k + 1], alpha[k + 1], ev);
752 std::vector<GuiPoint> out;
753 if(r.
w <= 0.0f || r.
h <= 0.0f)
756 const float half = 0.5f * std::min(r.
w, r.
h);
757 const float rad = clampf(radius, 0.0f, half);
758 const int n = arcCount(rad, tolerance < 0.01f ? 0.01f : tolerance);
759 Ring ring = roundRectRing(r,
GuiCorners(rad), 0.0f, n);
761 const float a = angleDeg * kPi / 180.0f;
762 const float c = cosf(a), s = sinf(a);
763 const float cx = r.
x + r.
w * 0.5f, cy = r.
y + r.
h * 0.5f;
764 out.reserve(ring.pts.size());
765 for(
size_t i = 0; i < ring.pts.size(); i++)
767 const float dx = ring.pts[i].x - cx, dy = ring.pts[i].y - cy;
768 GuiPoint p = { cx + dx * c - dy * s, cy + dx * s + dy * c };
780 float dx = p3.
x - p0.
x, dy = p3.
y - p0.
y;
781 float len2 = dx * dx + dy * dy;
785 d1 = sqrtf((p1.
x - p0.
x) * (p1.
x - p0.
x) + (p1.
y - p0.
y) * (p1.
y - p0.
y));
786 d2 = sqrtf((p2.
x - p0.
x) * (p2.
x - p0.
x) + (p2.
y - p0.
y) * (p2.
y - p0.
y));
790 float inv = 1.0f / sqrtf(len2);
791 d1 = fabsf((p1.
x - p0.
x) * dy - (p1.
y - p0.
y) * dx) * inv;
792 d2 = fabsf((p2.
x - p0.
x) * dy - (p2.
y - p0.
y) * dx) * inv;
795 if(depth >= 12 || 0.75f * std::max(d1, d2) <= tol)
800 GuiPoint p01 = { (p0.
x + p1.
x) * 0.5f, (p0.
y + p1.
y) * 0.5f };
801 GuiPoint p12 = { (p1.
x + p2.
x) * 0.5f, (p1.
y + p2.
y) * 0.5f };
802 GuiPoint p23 = { (p2.
x + p3.
x) * 0.5f, (p2.
y + p3.
y) * 0.5f };
803 GuiPoint a = { (p01.
x + p12.
x) * 0.5f, (p01.
y + p12.
y) * 0.5f };
804 GuiPoint b = { (p12.
x + p23.
x) * 0.5f, (p12.
y + p23.
y) * 0.5f };
805 GuiPoint m = { (a.
x + b.
x) * 0.5f, (a.
y + b.
y) * 0.5f };
806 flattenCubic(out, p0, p01, a, m, tol, depth + 1);
807 flattenCubic(out, m, b, p23, p3, tol, depth + 1);
814 std::vector<std::vector<GuiPoint> > contours;
815 std::vector<GuiPoint> cur;
816 if(tolerance < 0.001f)
819 auto pt = [&](
size_t i) {
GuiPoint p = { path.
pts[i * 2] * scale, path.
pts[i * 2 + 1] * scale };
return p; };
820 auto finish = [&]() {
822 if(cur.size() > 1 && fabsf(cur.front().x - cur.back().x) < 1e-5f && fabsf(cur.front().y - cur.back().y) < 1e-5f)
825 contours.push_back(cur);
828 for(
size_t c = 0; c < path.
cmds.size(); c++)
834 if((k + 1) * 2 > path.
pts.size())
return contours;
835 cur.push_back(pt(k++));
838 if((k + 1) * 2 > path.
pts.size())
return contours;
839 cur.push_back(pt(k++));
842 if((k + 3) * 2 > path.
pts.size() || cur.empty())
return contours;
843 flattenCubic(cur, cur.back(), pt(k), pt(k + 1), pt(k + 2), tolerance, 0);
860 if(path.
pts.size() < 2)
862 float minx = path.
pts[0], maxx = minx, miny = path.
pts[1], maxy = miny;
863 for(
size_t i = 0; i + 1 < path.
pts.size(); i += 2)
865 minx = std::min(minx, path.
pts[i]);
866 maxx = std::max(maxx, path.
pts[i]);
867 miny = std::min(miny, path.
pts[i + 1]);
868 maxy = std::max(maxy, path.
pts[i + 1]);
872 r.
w = (maxx - minx) * scale;
873 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.