30 if (!
p.setLength(
len)) {
37#define setLength(p, len) checkSetLength(p, len, __FILE__, __LINE__)
39constexpr uint64_t choose(uint64_t n, uint64_t k) {
42 for (uint64_t
i = 1;
i <= k;
i++) {
56 inline static constexpr int kDegreeInvalid = -1;
59 ScalarBezCurve() : fDegree(kDegreeInvalid) {}
62 explicit ScalarBezCurve(
int degree) : fDegree(degree) {
64 fWeights.resize(degree + 1, {0});
68 ScalarBezCurve(
int degree,
const std::vector<float>& weights) : ScalarBezCurve(degree) {
70 SkASSERT(weights.size() == (
size_t)degree + 1);
71 fWeights.insert(fWeights.begin(), weights.begin(), weights.end());
75 float extremumWeight()
const {
78 for (
float w : fWeights) {
81 sign =
w >= 0 ? 1 : -1;
88 float eval(
float t)
const {
return Eval(*
this, t); }
91 static float Eval(
const ScalarBezCurve& curve,
float t) {
93 ScalarBezCurve
result = curve;
95 for (
int k = 1; k <= curve.fDegree; k++) {
97 for (
int i = curve.fDegree;
i >= k;
i--) {
102 return result.fWeights[curve.fDegree];
106 void split(
float t, ScalarBezCurve*
left, ScalarBezCurve*
right)
const {
111 void split(
float tmin,
float tmax, ScalarBezCurve*
result)
const {
113 const float tRel = tmin / tmax;
114 ScalarBezCurve ll, rl, rr;
115 this->split(tmax, &rl, &rr);
116 rl.split(tRel, &ll,
result);
120 static void Split(
const ScalarBezCurve& curve,
122 ScalarBezCurve*
left,
123 ScalarBezCurve*
right) {
125 const int degree = curve.fDegree;
126 ScalarBezCurve
result = curve;
127 *
left = ScalarBezCurve(degree);
128 *
right = ScalarBezCurve(degree);
129 left->fWeights[0] = curve.fWeights[0];
130 right->fWeights[degree] = curve.fWeights[degree];
132 for (
int k = 1; k <= degree; k++) {
134 for (
int i = degree;
i >= k;
i--) {
139 right->fWeights[degree - k] =
result.fWeights[degree];
149 void elevateDegree(
int newDegree) {
150 if (newDegree == fDegree) {
154 fWeights = ElevateDegree(*
this, newDegree).fWeights;
164 static ScalarBezCurve ElevateDegree(
const ScalarBezCurve& curve,
int newDegree) {
165 SkASSERT(newDegree >= curve.degree());
166 if (newDegree == curve.degree()) {
171 ScalarBezCurve elevated(newDegree);
172 const int r = newDegree - curve.fDegree;
173 const int n = curve.fDegree;
175 for (
int i = 0;
i <= n + r;
i++) {
176 elevated.fWeights[
i] = 0;
179 (choose(n, j) * choose(r,
i - j)) /
static_cast<float>(choose(n + r,
i));
180 elevated.fWeights[
i] += curve.fWeights[j] *
f;
190 std::vector<float> zeroSet()
const {
return ZeroSet(*
this); }
195 static std::vector<float> ZeroSet(
const ScalarBezCurve& curve) {
196 constexpr float kTol = 0.001f;
197 std::vector<float>
result;
198 ZeroSetRec(curve, 0, 1, kTol, &
result);
203 static ScalarBezCurve Mul(
const ScalarBezCurve& curve,
float f) {
204 ScalarBezCurve
result = curve;
205 for (
int k = 0; k <= curve.fDegree; k++) {
216 static ScalarBezCurve Mul(
const ScalarBezCurve&
a,
const ScalarBezCurve&
b) {
219 const int n =
a.degree(),
m =
b.degree();
220 const int newDegree = n +
m;
221 ScalarBezCurve
result(newDegree);
223 for (
int k = 0; k <= newDegree; k++) {
227 (choose(
m,
i) * choose(n, k -
i)) /
static_cast<float>(choose(
m + n, k));
228 result.fWeights[k] +=
a.fWeights[
i] *
b.fWeights[k -
i] *
f;
236 static ScalarBezCurve AddSquares(
const ScalarBezCurve&
a,
const ScalarBezCurve&
b) {
237 const int n =
a.degree(),
m =
b.degree();
238 const int newDegree = n +
m;
239 ScalarBezCurve
result(newDegree);
241 for (
int k = 0; k <= newDegree; k++) {
242 float aSq = 0, bSq = 0;
245 (choose(
m,
i) * choose(n, k -
i)) /
static_cast<float>(choose(
m + n, k));
246 aSq +=
a.fWeights[
i] *
a.fWeights[k -
i] *
f;
247 bSq +=
b.fWeights[
i] *
b.fWeights[k -
i] *
f;
249 result.fWeights[k] = aSq + bSq;
256 static ScalarBezCurve Sub(
const ScalarBezCurve&
a,
const ScalarBezCurve&
b) {
263 void sub(
const ScalarBezCurve& other) {
265 for (
int k = 0; k <= fDegree; k++) {
266 fWeights[k] -= other.fWeights[k];
272 for (
int k = 0; k <= fDegree; k++) {
278 int degree()
const {
return fDegree; }
281 const std::vector<float>& weights()
const {
return fWeights; }
283 float operator[](
size_t i)
const {
return fWeights[
i]; }
284 float& operator[](
size_t i) {
return fWeights[
i]; }
288 static void ZeroSetRec(
const ScalarBezCurve& curve,
292 std::vector<float>*
result) {
294 bool allPos = curve.fWeights[0] >= 0, allNeg = curve.fWeights[0] < 0;
295 for (
int i = 1;
i <= curve.fDegree;
i++) {
296 lenP +=
std::abs(curve.fWeights[
i] - curve.fWeights[
i - 1]);
297 allPos &= curve.fWeights[
i] >= 0;
298 allNeg &= curve.fWeights[
i] < 0;
301 result->push_back((tmin + tmax) * 0.5);
303 }
else if (allPos || allNeg) {
310 ScalarBezCurve
left(curve.fDegree),
right(curve.fDegree);
313 const float tmid = (tmin + tmax) * 0.5;
320 std::vector<float> fWeights;
326class PathVerbMeasure {
332 SkScalar currentVerbLength() {
return fMeas.getLength(); }
345SkScalar PathVerbMeasure::totalLength()
const {
347 return meas.getLength();
350void PathVerbMeasure::nextVerb() {
356 fFirstPointInContour = pts[0];
357 fPreviousPoint = fFirstPointInContour;
359 verb =
fIter.next(pts);
363 fCurrVerb.moveTo(fPreviousPoint);
366 fCurrVerb.lineTo(pts[1]);
369 fCurrVerb.quadTo(pts[1], pts[2]);
372 fCurrVerb.cubicTo(pts[1], pts[2], pts[3]);
375 fCurrVerb.conicTo(pts[1], pts[2],
fIter.conicWeight());
385 fCurrVerb.getLastPt(&fPreviousPoint);
386 fMeas.setPath(&fCurrVerb,
false);
393std::unique_ptr<ScalarBezCurve> outerErr;
411class SkVarWidthStroker {
414 enum class LengthMetric {
426 return getFillPath(
path,
paint, identityVarWidth(
paint.getStrokeWidth()),
427 identityVarWidth(
paint.getStrokeWidth()));
435 const ScalarBezCurve& varWidth,
436 const ScalarBezCurve& varWidthInner,
437 LengthMetric lengthMetric = LengthMetric::kNumSegments);
443 std::array<SkPoint, 4> fPoints;
446 struct OffsetSegments {
447 std::vector<PathSegment> fInner;
448 std::vector<PathSegment> fOuter;
455 OffsetSegments strokeSegment(
const PathSegment& segment,
456 const ScalarBezCurve& varWidth,
457 const ScalarBezCurve& varWidthInner,
466 std::vector<PathSegment> strokeSegment(
const PathSegment& seg,
467 const ScalarBezCurve& distanceFunc)
const;
470 enum class CapLocation { Start, End };
471 void endcap(CapLocation loc);
477 const OffsetSegments&
prev,
478 const OffsetSegments& curr);
496 static void approximateSegment(
const PathSegment& seg,
497 const ScalarBezCurve& distFnc,
501 static ScalarBezCurve identityVarWidth(
float strokeWidth) {
509 ScalarBezCurve fVarWidth, fVarWidthInner;
514 fRadius =
paint.getStrokeWidth() / 2;
515 fCap =
paint.getStrokeCap();
524 const ScalarBezCurve& varWidth,
525 const ScalarBezCurve& varWidthInner,
526 LengthMetric lengthMetric) {
527 const auto appendStrokes = [
this](
const OffsetSegments& strokes,
bool needsMove) {
529 fOuter.
moveTo(strokes.fOuter.front().fPoints[0]);
530 fInner.
moveTo(strokes.fInner.front().fPoints[0]);
534 fOuter.
quadTo(seg.fPoints[1], seg.fPoints[2]);
538 fInner.
quadTo(seg.fPoints[1], seg.fPoints[2]);
543 fVarWidth = varWidth;
544 fVarWidthInner = varWidthInner;
547 PathVerbMeasure meas(
path);
548 const float totalPathLength = lengthMetric == LengthMetric::kPathLength
550 : (
path.countVerbs() - 1);
556 OffsetSegments offsetSegs, prevOffsetSegs;
557 bool firstSegment =
true, prevWasFirst =
false;
559 float lenTraveled = 0;
561 const float verbLength = lengthMetric == LengthMetric::kPathLength
562 ? (meas.currentVerbLength() / totalPathLength)
563 : (1.0f / totalPathLength);
564 const float tmin = lenTraveled;
565 const float tmax = lenTraveled + verbLength;
568 ScalarBezCurve partVarWidth, partVarWidthInner;
569 fVarWidth.split(tmin, tmax, &partVarWidth);
570 fVarWidthInner.split(tmin, tmax, &partVarWidthInner);
571 partVarWidthInner = ScalarBezCurve::Mul(partVarWidthInner, -1);
574 switch (segment.fVerb) {
578 offsetSegs = strokeSegment(segment, partVarWidth, partVarWidthInner, firstSegment);
584 SkDebugf(
"Unhandled path verb %d\n", segment.fVerb);
592 appendStrokes(prevOffsetSegs, prevWasFirst);
595 const float innerRadius = varWidthInner.eval(tmin);
596 const float outerRadius = varWidth.eval(tmin);
597 join(segment.fPoints[0], innerRadius, outerRadius, prevOffsetSegs, offsetSegs);
602 prevWasFirst = firstSegment;
603 firstSegment =
false;
604 lenTraveled += verbLength;
609 appendStrokes(prevOffsetSegs, prevWasFirst);
612 const bool isClosed =
path.isLastContourClosed();
614 SkDebugf(
"Unhandled closed contour\n");
617 endcap(CapLocation::End);
621 appendPathReversed(fInner, &fOuter);
622 endcap(CapLocation::Start);
627SkVarWidthStroker::OffsetSegments SkVarWidthStroker::strokeSegment(
629 const ScalarBezCurve& varWidth,
630 const ScalarBezCurve& varWidthInner,
632 viz::outerErr.reset(
nullptr);
634 std::vector<PathSegment> outer = strokeSegment(segment, varWidth);
635 std::vector<PathSegment> inner = strokeSegment(segment, varWidthInner);
636 return {inner, outer};
639std::vector<SkVarWidthStroker::PathSegment> SkVarWidthStroker::strokeSegment(
640 const PathSegment& seg,
const ScalarBezCurve& distanceFunc)
const {
644 ScalarBezCurve fDistFnc, fDistFncSqd;
645 ScalarBezCurve fSegX, fSegY;
648 const ScalarBezCurve& distFnc,
649 const ScalarBezCurve& distFncSqd)
650 : fSeg(seg), fDistFnc(distFnc), fDistFncSqd(distFncSqd) {
651 const int segDegree = segmentDegree(seg);
652 fSegX = ScalarBezCurve(segDegree);
653 fSegY = ScalarBezCurve(segDegree);
654 for (
int i = 0;
i <= segDegree;
i++) {
655 fSegX[
i] = seg.fPoints[
i].fX;
656 fSegY[
i] = seg.fPoints[
i].fY;
662 std::stack<Item> stack;
663 stack.push(Item(seg, distanceFunc, ScalarBezCurve::Mul(distanceFunc, distanceFunc)));
665 std::vector<PathSegment>
result;
666 constexpr int kMaxIters = 5000;
668 while (!stack.empty()) {
669 if (iter++ >= kMaxIters)
break;
670 const Item item = stack.top();
673 const ScalarBezCurve& distFnc = item.fDistFnc;
674 ScalarBezCurve distFncSqd = item.fDistFncSqd;
675 const float kTol =
std::abs(0.5f * distFnc.extremumWeight());
679 approximateSegment(item.fSeg, distFnc, &quadApprox);
680 ScalarBezCurve quadApproxX(2), quadApproxY(2);
681 for (
int i = 0;
i < 3;
i++) {
682 quadApproxX[
i] = quadApprox.fPoints[
i].fX;
683 quadApproxY[
i] = quadApprox.fPoints[
i].fY;
687 const int deltaDegree =
std::max(quadApproxX.degree(), item.fSegX.degree());
688 ScalarBezCurve segX = item.fSegX, segY = item.fSegY;
689 segX.elevateDegree(deltaDegree);
690 segY.elevateDegree(deltaDegree);
691 quadApproxX.elevateDegree(deltaDegree);
692 quadApproxY.elevateDegree(deltaDegree);
694 ScalarBezCurve deltaX = ScalarBezCurve::Sub(quadApproxX, segX);
695 ScalarBezCurve deltaY = ScalarBezCurve::Sub(quadApproxY, segY);
698 ScalarBezCurve
E = ScalarBezCurve::AddSquares(deltaX, deltaY);
701 const int commonDeg =
std::max(distFncSqd.degree(),
E.degree());
702 distFncSqd.elevateDegree(commonDeg);
703 E.elevateDegree(commonDeg);
710 if (viz::outerErr ==
nullptr) {
712 outerErr = std::make_unique<ScalarBezCurve>(
E);
713 outerFirstApprox.rewind();
714 outerFirstApprox.moveTo(quadApprox.fPoints[0]);
715 outerFirstApprox.quadTo(quadApprox.fPoints[1], quadApprox.fPoints[2]);
720 float maxAbsErr =
std::abs(
E.extremumWeight());
722 if (maxAbsErr > kTol) {
724 splitSegment(item.fSeg, 0.5f, &
left, &
right);
726 ScalarBezCurve distFncL, distFncR;
727 distFnc.split(0.5f, &distFncL, &distFncR);
729 ScalarBezCurve distFncSqdL, distFncSqdR;
730 distFncSqd.split(0.5f, &distFncSqdL, &distFncSqdR);
732 stack.push(Item(
right, distFncR, distFncSqdR));
733 stack.push(Item(
left, distFncL, distFncSqdL));
737 result.push_back(quadApprox);
744void SkVarWidthStroker::endcap(CapLocation loc) {
745 const auto buttCap = [
this](CapLocation loc) {
746 if (loc == CapLocation::Start) {
752 fInner.getLastPt(&innerLastPt);
753 fOuter.lineTo(innerLastPt);
762 SkDebugf(
"Unhandled endcap %d\n", fCap);
771 const OffsetSegments&
prev,
772 const OffsetSegments& curr) {
776 const OffsetSegments&
prev,
777 const OffsetSegments& curr) {
781 const auto makeJoin = [
this, &
common, &
prev, &curr](
bool left,
float radius) {
783 const auto& prevSegs =
left ?
prev.fOuter :
prev.fInner;
784 const auto& currSegs =
left ? curr.fOuter : curr.fInner;
787 const SkPoint afterEndpt = currSegs.front().fPoints[0];
788 SkPoint before = unitNormal(prevSegs.back(), 1,
nullptr);
789 SkPoint after = unitNormal(currSegs.front(), 0,
nullptr);
792 const float cosTheta = before.
dot(after);
796 outerJoin = isClockwise(before, after);
798 before = rotate180(before);
799 after = rotate180(after);
800 outerJoin = !isClockwise(before, after);
807 SkPoint miterVec = before + after;
816 const float sinHalfTheta = sqrtf(0.5 * (1 + cosTheta));
817 const float halfMiterLength = radius / sinHalfTheta;
819 miterVec =
setLength(miterVec, halfMiterLength);
823 path->lineTo(afterEndpt);
830 path->lineTo(afterEndpt);
835 makeJoin(
true, leftRadius);
836 makeJoin(
false, rightRadius);
841 miterJoin(
common, innerRadius, outerRadius,
prev, curr);
845 miterJoin(
common, innerRadius, outerRadius,
prev, curr);
851 const int numVerbs =
path.countVerbs();
852 const int numPoints =
path.countPoints();
853 std::vector<uint8_t> verbs;
854 std::vector<SkPoint>
points;
855 verbs.resize(numVerbs);
857 path.getVerbs(verbs.data(), numVerbs);
860 for (
int i = numVerbs - 1, j = numPoints;
i >= 0;
i--) {
886int SkVarWidthStroker::segmentDegree(
const PathSegment& seg) {
887 static constexpr int lut[] = {
895 const int deg = lut[
static_cast<uint8_t
>(seg.fVerb)];
900void SkVarWidthStroker::splitSegment(
const PathSegment& seg,
905 const int degree = segmentDegree(seg);
906 ScalarBezCurve
x(degree),
y(degree);
907 for (
int i = 0;
i <= degree;
i++) {
908 x[
i] = seg.fPoints[
i].fX;
909 y[
i] = seg.fPoints[
i].fY;
912 ScalarBezCurve leftX(degree), rightX(degree), leftY(degree), rightY(degree);
913 x.split(t, &leftX, &rightX);
914 y.split(t, &leftY, &rightY);
916 segA->fVerb = segB->fVerb = seg.fVerb;
917 for (
int i = 0;
i <= degree;
i++) {
918 segA->fPoints[
i] = {leftX[
i], leftY[
i]};
919 segB->fPoints[
i] = {rightX[
i], rightY[
i]};
923void SkVarWidthStroker::approximateSegment(
const PathSegment& seg,
924 const ScalarBezCurve& distFnc,
930 SkPoint tangentStart, tangentEnd;
931 SkPoint offsetStart = unitNormal(seg, 0, &tangentStart);
932 SkPoint offsetEnd = unitNormal(seg, 1, &tangentEnd);
933 SkPoint offsetMid = offsetStart + offsetEnd;
935 const float radiusStart = distFnc.eval(0);
936 const float radiusMid = distFnc.eval(0.5f);
937 const float radiusEnd = distFnc.eval(1);
944 switch (segmentDegree(seg)) {
946 start = seg.fPoints[0];
947 end = seg.fPoints[1];
951 start = seg.fPoints[0];
952 mid = seg.fPoints[1];
953 end = seg.fPoints[2];
956 start = seg.fPoints[0];
957 mid = (seg.fPoints[1] + seg.fPoints[2]) * 0.5f;
958 end = seg.fPoints[3];
961 SkDebugf(
"Unhandled degree for segment approximation");
966 approxQuad->fPoints[0] =
start + offsetStart;
967 approxQuad->fPoints[1] = mid + offsetMid;
968 approxQuad->fPoints[2] =
end + offsetEnd;
977 *tangentOut = tangent;
984 tangent = seg.fPoints[1] - seg.fPoints[0];
986 tangent = seg.fPoints[2] - seg.fPoints[1];
988 tangent = ((seg.fPoints[1] - seg.fPoints[0]) * (1 - t) +
989 (seg.fPoints[2] - seg.fPoints[1]) * t) *
993 SkDebugf(
"Failed to normalize quad tangent\n");
997 *tangentOut = tangent;
999 return rotate90(tangent);
1003 SkEvalCubicAt(seg.fPoints.data(), t,
nullptr, &tangent,
nullptr);
1005 SkDebugf(
"Failed to normalize cubic tangent\n");
1009 *tangentOut = tangent;
1011 return rotate90(tangent);
1014 SkDebugf(
"Unhandled verb for unit normal %d\n", seg.fVerb);
1028 , fShowSkeleton(
true)
1029 , fShowStrokePoints(
false)
1031 , fDifferentInnerFunc(
false)
1032 , fShowErrorCurve(
false) {
1048 fNewFillPaint.
setColor(0x8000FF00);
1058 fName =
"VariableWidthStroker";
1068 this->toggle(fShowUI);
1071 this->toggle(fShowSkeleton);
1074 this->toggle(fShowHidden);
1077 this->toggle(fShowStrokePoints);
1080 this->toggle(fShowErrorCurve);
1083 this->toggle(fLengthMetric);
1104 this->makePath(&
path);
1109 ScalarBezCurve distFnc = makeDistFnc(fDistFncs, fWidth);
1110 ScalarBezCurve distFncInner =
1111 fDifferentInnerFunc ? makeDistFnc(fDistFncsInner, fWidth) : distFnc;
1112 SkVarWidthStroker stroker;
1114 stroker.getFillPath(
path, fStrokePaint, distFnc, distFncInner, fLengthMetric);
1116 canvas->
drawPath(fillPath, fNewFillPaint);
1119 canvas->
drawPath(fillPath, fHiddenPaint);
1122 if (fShowSkeleton) {
1128 if (fShowStrokePoints) {
1129 drawStrokePoints(canvas, fillPath);
1136 if (fShowErrorCurve && viz::outerErr !=
nullptr) {
1141 canvas->
drawPath(viz::outerFirstApprox, firstApproxPaint);
1142 drawErrorCurve(canvas, *viz::outerErr);
1150 for (
size_t i = 0;
i < fPathPts.size(); ++
i) {
1165 struct DistFncMenuItem {
1169 std::vector<float> fWeights;
1171 DistFncMenuItem(
const std::string&
name,
int degree,
bool selected) {
1174 fSelected = selected;
1175 fWeights.resize(degree + 1, 1.0f);
1180 void toggle(SkVarWidthStroker::LengthMetric&
value) {
1181 value =
value == SkVarWidthStroker::LengthMetric::kPathLength
1182 ? SkVarWidthStroker::LengthMetric::kNumSegments
1183 : SkVarWidthStroker::LengthMetric::kPathLength;
1186 void resetToDefaults() {
1187 fPathPts[0] = {300, 400};
1188 fPathPts[1] = {500, 400};
1189 fPathPts[2] = {700, 400};
1190 fPathPts[3] = {900, 400};
1191 fPathPts[4] = {1100, 400};
1195 fLengthMetric = SkVarWidthStroker::LengthMetric::kPathLength;
1196 fDistFncs = fDefaultsDistFncs;
1197 fDistFncsInner = fDefaultsDistFncs;
1201 path->moveTo(fPathPts[0]);
1202 path->quadTo(fPathPts[1], fPathPts[2]);
1203 path->quadTo(fPathPts[3], fPathPts[4]);
1206 static ScalarBezCurve makeDistFnc(
const std::vector<DistFncMenuItem>& fncs,
float strokeWidth) {
1208 for (
const auto& df : fncs) {
1210 return ScalarBezCurve::Mul(ScalarBezCurve(df.fDegree, df.fWeights), radius);
1214 return ScalarBezCurve(0, {radius});
1217 void drawStrokePoints(
SkCanvas* canvas,
const SkPath& fillPath) {
1221 std::vector<SkPoint> pointsVec, ctrlPts;
1225 pointsVec.push_back(
points[1]);
1228 ctrlPts.push_back(
points[1]);
1229 pointsVec.push_back(
points[2]);
1232 pointsVec.push_back(
points[0]);
1237 SkDebugf(
"Unhandled path verb %d for stroke points\n", verb);
1244 fStrokePointsPaint);
1248 fStrokePointsPaint);
1253 void drawErrorCurve(
SkCanvas* canvas,
const ScalarBezCurve&
E) {
1254 const float winW = fWinSize.
width() * 0.75f, winH = fWinSize.
height() * 0.25f;
1255 const float padding = 25;
1257 winW - 2 * padding, winH);
1258 constexpr int nsegs = 100;
1259 constexpr float dt = 1.0f / nsegs;
1260 constexpr float dx = 10.0f;
1261 const int deg =
E.degree();
1263 for (
int i = 0;
i < nsegs;
i++) {
1264 const float tmin =
i * dt, tmax = (
i + 1) * dt;
1267 const float tRel = tmin / tmax;
1268 ScalarBezCurve rl(deg), rr(deg);
1269 left.split(tRel, &rl, &rr);
1271 const float x =
i *
dx;
1273 path.moveTo(
x, -rr[0]);
1275 path.lineTo(
x +
dx, -rr[deg]);
1280 paint.setAntiAlias(
true);
1281 paint.setStrokeWidth(0);
1283 const SkRect pathBounds =
path.computeTightBounds();
1284 constexpr float yAxisMax = 8000;
1285 const float sx = box.
width() / pathBounds.
width();
1286 const float sy = box.
height() / (2 * yAxisMax);
1289 canvas->
scale(sx, sy);
1295 axes.
moveTo(0, -yAxisMax);
1296 axes.
lineTo(0, yAxisMax);
1298 paint.setAntiAlias(
false);
1305 static constexpr auto kUIOpacity = 0.35f;
1306 static constexpr float kUIWidth = 200.0f, kUIHeight = 400.0f;
1307 ImGui::SetNextWindowBgAlpha(kUIOpacity);
1308 if (ImGui::Begin(
"E-C Controls",
nullptr,
1309 ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize |
1310 ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
1311 ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) {
1313 ImGui::SetWindowPos(ImVec2(uiArea.
x(), uiArea.
y()));
1314 ImGui::SetWindowSize(ImVec2(uiArea.
width(), uiArea.
height()));
1316 const auto drawControls = [](std::vector<DistFncMenuItem>& distFncs,
1317 const std::string& menuPfx,
1318 const std::string& ptPfx) {
1319 std::string degreeMenuLabel = menuPfx +
": ";
1320 for (
const auto& df : distFncs) {
1322 degreeMenuLabel += df.fName;
1326 if (ImGui::BeginMenu(degreeMenuLabel.c_str())) {
1327 for (
size_t i = 0;
i < distFncs.size();
i++) {
1328 if (ImGui::MenuItem(distFncs[
i].
fName.
c_str(),
nullptr,
1329 distFncs[
i].fSelected)) {
1330 for (
size_t j = 0; j < distFncs.size(); j++) {
1331 distFncs[j].fSelected = j ==
i;
1338 for (
auto& df : distFncs) {
1340 for (
int i = 0;
i <= df.fDegree;
i++) {
1342 ImGui::SliderFloat(label.c_str(), &(df.fWeights[
i]), 0, 1);
1348 const std::array<std::pair<std::string, SkVarWidthStroker::LengthMetric>, 2> metrics = {
1349 std::make_pair(
"% path length", SkVarWidthStroker::LengthMetric::kPathLength),
1350 std::make_pair(
"% segment count",
1351 SkVarWidthStroker::LengthMetric::kNumSegments),
1353 if (ImGui::BeginMenu(
"Interpolation metric:")) {
1354 for (
const auto& metric : metrics) {
1355 if (ImGui::MenuItem(metric.first.c_str(),
nullptr,
1356 fLengthMetric == metric.second)) {
1357 fLengthMetric = metric.second;
1363 drawControls(fDistFncs,
"Degree",
"P");
1365 if (ImGui::CollapsingHeader(
"Inner stroke",
true)) {
1366 fDifferentInnerFunc =
true;
1367 drawControls(fDistFncsInner,
"Degree (inner)",
"Q");
1369 fDifferentInnerFunc =
false;
1375 bool fShowHidden, fShowSkeleton, fShowStrokePoints, fShowUI, fDifferentInnerFunc,
1378 SkPaint fPtsPaint, fStrokePaint, fNewFillPaint, fHiddenPaint, fSkeletonPaint,
1380 inline static constexpr int kNPts = 5;
1381 std::array<SkPoint, kNPts> fPathPts;
1383 SkVarWidthStroker::LengthMetric fLengthMetric;
1384 const std::vector<DistFncMenuItem> fDefaultsDistFncs = {
1385 DistFncMenuItem(
"Linear", 1,
true), DistFncMenuItem(
"Quadratic", 2,
false),
1386 DistFncMenuItem(
"Cubic", 3,
false), DistFncMenuItem(
"One Louder (11)", 11,
false),
1387 DistFncMenuItem(
"30?!", 30,
false)};
1388 std::vector<DistFncMenuItem> fDistFncs = fDefaultsDistFncs;
1389 std::vector<DistFncMenuItem> fDistFncsInner = fDefaultsDistFncs;
static const int strokeWidth
static const int points[]
std::unique_ptr< SkLatticeIter > fIter
static float prev(float f)
constexpr SkColor SK_ColorBLUE
constexpr SkColor SK_ColorRED
constexpr SkColor SK_ColorBLACK
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint *loc, SkVector *tangent, SkVector *curvature)
static int sign(SkScalar x)
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
void swap(sk_sp< T > &a, sk_sp< T > &b)
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
#define setLength(p, len)
void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint &paint)
void translate(SkScalar dx, SkScalar dy)
void drawColor(SkColor color, SkBlendMode mode=SkBlendMode::kSrcOver)
void drawPath(const SkPath &path, const SkPaint &paint)
void scale(SkScalar sx, SkScalar sy)
@ kPoints_PointMode
draw each point separately
@ kButt_Cap
no stroke extension
void setStyle(Style style)
void setColor(SkColor color)
void setAntiAlias(bool aa)
void setStrokeCap(Cap cap)
@ kStroke_Style
set to stroke geometry
@ kMiter_Join
extends to miter limit
void setStrokeWidth(SkScalar width)
SkPath & moveTo(SkScalar x, SkScalar y)
void setFillType(SkPathFillType ft)
SkPath & lineTo(SkScalar x, SkScalar y)
SkPath & quadTo(SkScalar x1, SkScalar y1, SkScalar x2, SkScalar y2)
const char * c_str() const
void resize(SkScalar w, SkScalar h) override
Click * onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override
VariableWidthStrokerSlide()
bool onChar(SkUnichar uni) override
bool onClick(ClickHandlerSlide::Click *) override
void draw(SkCanvas *canvas) override
void load(SkScalar w, SkScalar h) override
static float max(float r, float g, float b)
static float min(float r, float g, float b)
skia_private::AutoTArray< sk_sp< SkImageFilter > > filters TypedMatrix matrix TypedMatrix matrix SkScalar dx
static ArrayPtr Eval(Dart_Handle lib, const char *expr)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
DEF_SWITCHES_START aot vmservice shared library name
SIN Vec< N, float > abs(const Vec< N, float > &x)
static SkString to_string(int n)
static SkString join(const CommandLineFlags::StringArray &)
float dot(const SkVector &vec) const
static constexpr SkPoint Make(float x, float y)
constexpr float left() const
constexpr float top() const
constexpr float x() const
constexpr float y() const
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
bool intersects(const SkRect &r) const
constexpr float height() const
constexpr float width() const