@@ -141,9 +141,10 @@ DEF_SAMPLE( return new ClipView(); )
141141struct SkHalfPlane {
142142 SkScalar fA , fB , fC ;
143143
144- SkScalar operator () (SkScalar x, SkScalar y) const {
144+ SkScalar eval (SkScalar x, SkScalar y) const {
145145 return fA * x + fB * y + fC ;
146146 }
147+ SkScalar operator ()(SkScalar x, SkScalar y) const { return this ->eval (x, y); }
147148
148149 bool twoPts (SkPoint pts[2 ]) const {
149150 // normalize plane to help with the perpendicular step, below
@@ -172,11 +173,45 @@ struct SkHalfPlane {
172173 SkASSERT (SkScalarNearlyZero (this ->operator ()(pts[1 ].fX , pts[1 ].fY )));
173174 return true ;
174175 }
176+
177+ enum Result {
178+ kAllNegative ,
179+ kAllPositive ,
180+ kMixed
181+ };
182+ Result test (const SkRect& bounds) const {
183+ SkPoint diagMin, diagMax;
184+ if (fA >= 0 ) {
185+ diagMin.fX = bounds.fLeft ;
186+ diagMax.fX = bounds.fRight ;
187+ } else {
188+ diagMin.fX = bounds.fRight ;
189+ diagMax.fX = bounds.fLeft ;
190+ }
191+ if (fB >= 0 ) {
192+ diagMin.fY = bounds.fTop ;
193+ diagMax.fY = bounds.fBottom ;
194+ } else {
195+ diagMin.fY = bounds.fBottom ;
196+ diagMax.fY = bounds.fTop ;
197+ }
198+ SkScalar test = this ->eval (diagMin.fX , diagMin.fY );
199+ SkScalar sign = test*this ->eval (diagMax.fX , diagMin.fY );
200+ if (sign > 0 ) {
201+ // the path is either all on one side of the half-plane or the other
202+ if (test < 0 ) {
203+ return kAllNegative ;
204+ } else {
205+ return kAllPositive ;
206+ }
207+ }
208+ return kMixed ;
209+ }
175210};
176211
177212#include " src/core/SkEdgeClipper.h"
178213
179- static SkPath clip (const SkPath& path, SkPoint p0, SkPoint p1) {
214+ static void clip (const SkPath& path, SkPoint p0, SkPoint p1, SkPath* clippedPath ) {
180215 SkMatrix mx, inv;
181216 SkVector v = p1 - p0;
182217 mx.setAll (v.fX , -v.fY , p0.fX ,
@@ -191,9 +226,9 @@ static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
191226 SkRect clip = {-big, 0 , big, big };
192227
193228 struct Rec {
194- SkPath fResult ;
229+ SkPath* fResult ;
195230 SkPoint fPrev ;
196- } rec;
231+ } rec = { clippedPath, { 0 , 0 } } ;
197232
198233 SkEdgeClipper::ClipPath (rotated, clip, false ,
199234 [](SkEdgeClipper* clipper, bool newCtr, void * ctx) {
@@ -204,26 +239,26 @@ static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
204239 SkPath::Verb verb;
205240 while ((verb = clipper->next (pts)) != SkPath::kDone_Verb ) {
206241 if (newCtr) {
207- rec->fResult . moveTo (pts[0 ]);
242+ rec->fResult -> moveTo (pts[0 ]);
208243 rec->fPrev = pts[0 ];
209244 newCtr = false ;
210245 }
211246
212247 if (addLineTo || pts[0 ] != rec->fPrev ) {
213- rec->fResult . lineTo (pts[0 ]);
248+ rec->fResult -> lineTo (pts[0 ]);
214249 }
215250
216251 switch (verb) {
217252 case SkPath::kLine_Verb :
218- rec->fResult . lineTo (pts[1 ]);
253+ rec->fResult -> lineTo (pts[1 ]);
219254 rec->fPrev = pts[1 ];
220255 break ;
221256 case SkPath::kQuad_Verb :
222- rec->fResult . quadTo (pts[1 ], pts[2 ]);
257+ rec->fResult -> quadTo (pts[1 ], pts[2 ]);
223258 rec->fPrev = pts[2 ];
224259 break ;
225260 case SkPath::kCubic_Verb :
226- rec->fResult . cubicTo (pts[1 ], pts[2 ], pts[3 ]);
261+ rec->fResult -> cubicTo (pts[1 ], pts[2 ], pts[3 ]);
227262 rec->fPrev = pts[3 ];
228263 break ;
229264 default : break ;
@@ -232,50 +267,27 @@ static SkPath clip(const SkPath& path, SkPoint p0, SkPoint p1) {
232267 }
233268 }, &rec);
234269
235- rec.fResult .transform (mx);
236- return rec.fResult ;
270+ rec.fResult ->transform (mx);
237271}
238272
239- static SkPath clip (const SkPath& path, const SkHalfPlane& plane) {
240- // do a quick bounds check to see if we need to clip at all
241- const SkRect& bounds = path.getBounds ();
242-
243- // check whether the diagonal aligned with the normal crosses the plane
244- SkPoint diagMin, diagMax;
245- if (plane.fA >= 0 ) {
246- diagMin.fX = bounds.fLeft ;
247- diagMax.fX = bounds.fRight ;
248- } else {
249- diagMin.fX = bounds.fRight ;
250- diagMax.fX = bounds.fLeft ;
251- }
252- if (plane.fB >= 0 ) {
253- diagMin.fY = bounds.fTop ;
254- diagMax.fY = bounds.fBottom ;
255- } else {
256- diagMin.fY = bounds.fBottom ;
257- diagMax.fY = bounds.fTop ;
258- }
259- SkScalar test = plane (diagMin.fX , diagMin.fY );
260- SkScalar sign = test*plane (diagMax.fX , diagMin.fY );
261- if (sign > 0 ) {
262- // the path is either all on one side of the half-plane or the other
263- if (test < 0 ) {
264- // completely culled
265- return SkPath ();
266- } else {
267- // no clipping necessary
268- return path;
269- }
270- }
271-
272- // quick check failed, we have to clip
273- SkPoint pts[2 ];
274- if (plane.twoPts (pts)) {
275- return clip (path, pts[0 ], pts[1 ]);
276- } else {
277- return SkPath ();
273+ // true means use clippedPath.
274+ // false means there was no clipping -- use the original path
275+ static bool clip (const SkPath& path, const SkHalfPlane& plane, SkPath* clippedPath) {
276+ switch (plane.test (path.getBounds ())) {
277+ case SkHalfPlane::kAllPositive :
278+ return false ;
279+ case SkHalfPlane::kMixed : {
280+ SkPoint pts[2 ];
281+ if (plane.twoPts (pts)) {
282+ clip (path, pts[0 ], pts[1 ], clippedPath);
283+ return true ;
284+ }
285+ } break ;
286+ default : break ; // handled outside of the switch
278287 }
288+ // clipped out (or failed)
289+ clippedPath->reset ();
290+ return true ;
279291}
280292
281293static void draw_halfplane (SkCanvas* canvas, SkPoint p0, SkPoint p1, SkColor c) {
@@ -319,7 +331,10 @@ class HalfPlaneView : public Sample {
319331 canvas->drawPath (fPath , paint);
320332
321333 paint.setColor ({0 , 0 , 0 , 1 }, nullptr );
322- canvas->drawPath (clip (fPath , fPts [0 ], fPts [1 ]), paint);
334+
335+ SkPath clippedPath;
336+ clip (fPath , fPts [0 ], fPts [1 ], &clippedPath);
337+ canvas->drawPath (clippedPath, paint);
323338
324339 draw_halfplane (canvas, fPts [0 ], fPts [1 ], SK_ColorRED);
325340 }
@@ -430,6 +445,7 @@ static SkMatrix44 inv(const SkMatrix44& m) {
430445 return inverse;
431446}
432447
448+ #if 0 // Jim's general half-planes math
433449static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPlane planes[6]) {
434450 float mx[16];
435451 m44.asColMajorf(mx);
@@ -450,18 +466,10 @@ static void half_planes(const SkMatrix44& m44, SkScalar W, SkScalar H, SkHalfPla
450466 planes[4] = { m - i, n - j, p - l }; // w - z
451467 planes[5] = { m + i, n + j, p + l }; // w + z
452468}
469+ #endif
453470
454- static SkHalfPlane half_plane_w0 (const SkMatrix44& m44, SkScalar W, SkScalar H) {
455- float mx[16 ];
456- m44.asColMajorf (mx);
457-
458- SkScalar
459- // a = mx[0], b = mx[4], /* c = mx[ 8], */ d = mx[12],
460- // e = mx[1], f = mx[5], /* g = mx[ 9], */ h = mx[13],
461- // i = mx[2], j = mx[6], /* k = mx[10], */ l = mx[14],
462- m = mx[3 ], n = mx[7 ], /* o = mx[11], */ p = mx[15 ];
463-
464- return { m, n, p - 0 .05f }; // w = 0.05f
471+ static SkHalfPlane half_plane_w0 (const SkMatrix& m) {
472+ return { m[SkMatrix::kMPersp0 ], m[SkMatrix::kMPersp1 ], m[SkMatrix::kMPersp2 ] - 0 .05f };
465473}
466474
467475class HalfPlaneView3 : public Sample {
@@ -478,6 +486,7 @@ class HalfPlaneView3 : public Sample {
478486
479487 SkPath fPath ;
480488 sk_sp<SkShader> fShader ;
489+ bool fShowUnclipped = false ;
481490
482491 SkString name () override { return SkString (" halfplane3" ); }
483492
@@ -514,38 +523,37 @@ class HalfPlaneView3 : public Sample {
514523 }
515524
516525 void onDrawContent (SkCanvas* canvas) override {
517- SkMatrix44 mx44 = this ->get44 ();
518- SkMatrix mx = mx44;
526+ SkMatrix mx = this ->get44 ();
519527
520528 SkPaint paint;
521529 paint.setColor ({0.75 , 0.75 , 0.75 , 1 });
522530 canvas->drawPath (fPath , paint);
523531
524532 paint.setShader (fShader );
525533
526- canvas->save ();
527- canvas->concat (mx);
528- paint.setAlphaf (0 .33f );
529- canvas->drawPath (fPath , paint);
530- paint.setAlphaf (1 .f );
531- canvas->restore ();
534+ if (fShowUnclipped ) {
535+ canvas->save ();
536+ canvas->concat (mx);
537+ paint.setAlphaf (0 .33f );
538+ canvas->drawPath (fPath , paint);
539+ paint.setAlphaf (1 .f );
540+ canvas->restore ();
541+ }
532542
533- SkHalfPlane planes[6 ];
534- half_planes (mx44, 400 , 400 , planes);
535- SkHalfPlane hpw = half_plane_w0 (mx44, 400 , 400 );
543+ SkHalfPlane hpw = half_plane_w0 (mx);
536544
537- SkPath path = clip (fPath , hpw);// planes[4]);
545+ SkColor planeColor = SK_ColorBLUE;
546+ SkPath clippedPath, *path = &fPath ;
547+ if (clip (fPath , hpw, &clippedPath)) {
548+ path = &clippedPath;
549+ planeColor = SK_ColorRED;
550+ }
538551 canvas->save ();
539552 canvas->concat (mx);
540- canvas->drawPath (path, paint);
553+ canvas->drawPath (* path, paint);
541554 canvas->restore ();
542555
543- // for (auto& p : planes) {
544- // draw_halfplane(canvas, p, SK_ColorRED);
545- // }
546- draw_halfplane (canvas, planes[4 ], SK_ColorBLUE);
547- // draw_halfplane(canvas, planes[5], SK_ColorGREEN);
548- draw_halfplane (canvas, hpw, SK_ColorRED);
556+ draw_halfplane (canvas, hpw, planeColor);
549557 }
550558
551559 bool onChar (SkUnichar uni) override {
@@ -558,13 +566,15 @@ class HalfPlaneView3 : public Sample {
558566 case ' -' : this ->rotate (0 , 0 , delta); return true ;
559567 case ' +' : this ->rotate (0 , 0 , -delta); return true ;
560568
561- case ' i' : fTrans .fZ += 0 .1f ; SkDebugf (" ez %g\n " , fTrans .fZ ); return true ;
562- case ' k' : fTrans .fZ -= 0 .1f ; SkDebugf (" ez %g\n " , fTrans .fZ ); return true ;
569+ case ' i' : fTrans .fZ += 0 .1f ; SkDebugf (" z %g\n " , fTrans .fZ ); return true ;
570+ case ' k' : fTrans .fZ -= 0 .1f ; SkDebugf (" z %g\n " , fTrans .fZ ); return true ;
563571
564572 case ' n' : fNear += 0 .1f ; SkDebugf (" near %g\n " , fNear ); return true ;
565573 case ' N' : fNear -= 0 .1f ; SkDebugf (" near %g\n " , fNear ); return true ;
566574 case ' f' : fFar += 0 .1f ; SkDebugf (" far %g\n " , fFar ); return true ;
567575 case ' F' : fFar -= 0 .1f ; SkDebugf (" far %g\n " , fFar ); return true ;
576+
577+ case ' u' : fShowUnclipped = !fShowUnclipped ; return true ;
568578 default : break ;
569579 }
570580 return false ;
0 commit comments