OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2012 Google Inc. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. |
| 6 */ |
| 7 #include "SkIntersections.h" |
| 8 #include "SkOpAngle.h" |
| 9 #include "SkPathOpsCurve.h" |
| 10 |
| 11 // FIXME: this is bogus for quads and cubics |
| 12 // if the quads and cubics' line from end pt to ctrl pt are coincident, |
| 13 // there's no obvious way to determine the curve ordering from the |
| 14 // derivatives alone. In particular, if one quadratic's coincident tangent |
| 15 // is longer than the other curve, the final control point can place the |
| 16 // longer curve on either side of the shorter one. |
| 17 // Using Bezier curve focus http://cagd.cs.byu.edu/~tom/papers/bezclip.pdf |
| 18 // may provide some help, but nothing has been figured out yet. |
| 19 |
| 20 /*( |
| 21 for quads and cubics, set up a parameterized line (e.g. LineParameters ) |
| 22 for points [0] to [1]. See if point [2] is on that line, or on one side |
| 23 or the other. If it both quads' end points are on the same side, choose |
| 24 the shorter tangent. If the tangents are equal, choose the better second |
| 25 tangent angle |
| 26 |
| 27 maybe I could set up LineParameters lazily |
| 28 */ |
| 29 bool SkOpAngle::operator<(const SkOpAngle& rh) const { |
| 30 double y = dy(); |
| 31 double ry = rh.dy(); |
| 32 if ((y < 0) ^ (ry < 0)) { // OPTIMIZATION: better to use y * ry < 0 ? |
| 33 return y < 0; |
| 34 } |
| 35 double x = dx(); |
| 36 double rx = rh.dx(); |
| 37 if (y == 0 && ry == 0 && x * rx < 0) { |
| 38 return x < rx; |
| 39 } |
| 40 double x_ry = x * ry; |
| 41 double rx_y = rx * y; |
| 42 double cmp = x_ry - rx_y; |
| 43 if (!approximately_zero(cmp)) { |
| 44 return cmp < 0; |
| 45 } |
| 46 if (approximately_zero(x_ry) && approximately_zero(rx_y) |
| 47 && !approximately_zero_squared(cmp)) { |
| 48 return cmp < 0; |
| 49 } |
| 50 // at this point, the initial tangent line is coincident |
| 51 // see if edges curl away from each other |
| 52 if (fSide * rh.fSide <= 0 && (!approximately_zero(fSide) |
| 53 || !approximately_zero(rh.fSide))) { |
| 54 // FIXME: running demo will trigger this assertion |
| 55 // (don't know if commenting out will trigger further assertion or not) |
| 56 // commenting it out allows demo to run in release, though |
| 57 // SkASSERT(fSide != rh.fSide); |
| 58 return fSide < rh.fSide; |
| 59 } |
| 60 // see if either curve can be lengthened and try the tangent compare again |
| 61 if (cmp && (*fSpans)[fEnd].fOther != rh.fSegment // tangents not absolutely
identical |
| 62 && (*rh.fSpans)[rh.fEnd].fOther != fSegment) { // and not intersecti
ng |
| 63 SkOpAngle longer = *this; |
| 64 SkOpAngle rhLonger = rh; |
| 65 if (longer.lengthen() | rhLonger.lengthen()) { |
| 66 return longer < rhLonger; |
| 67 } |
| 68 } |
| 69 if ((fVerb == SkPath::kLine_Verb && approximately_zero(x) && approximately_z
ero(y)) |
| 70 || (rh.fVerb == SkPath::kLine_Verb |
| 71 && approximately_zero(rx) && approximately_zero(ry))) { |
| 72 // See general unsortable comment below. This case can happen when |
| 73 // one line has a non-zero change in t but no change in x and y. |
| 74 fUnsortable = true; |
| 75 rh.fUnsortable = true; |
| 76 return this < &rh; // even with no solution, return a stable sort |
| 77 } |
| 78 if ((*rh.fSpans)[SkMin32(rh.fStart, rh.fEnd)].fTiny |
| 79 || (*fSpans)[SkMin32(fStart, fEnd)].fTiny) { |
| 80 fUnsortable = true; |
| 81 rh.fUnsortable = true; |
| 82 return this < &rh; // even with no solution, return a stable sort |
| 83 } |
| 84 SkASSERT(fVerb >= SkPath::kQuad_Verb); |
| 85 SkASSERT(rh.fVerb >= SkPath::kQuad_Verb); |
| 86 // FIXME: until I can think of something better, project a ray from the |
| 87 // end of the shorter tangent to midway between the end points |
| 88 // through both curves and use the resulting angle to sort |
| 89 // FIXME: some of this setup can be moved to set() if it works, or cached if
it's expensive |
| 90 double len = fTangent1.normalSquared(); |
| 91 double rlen = rh.fTangent1.normalSquared(); |
| 92 SkDLine ray; |
| 93 SkIntersections i, ri; |
| 94 int roots, rroots; |
| 95 bool flip = false; |
| 96 do { |
| 97 bool useThis = (len < rlen) ^ flip; |
| 98 const SkDCubic& part = useThis ? fCurvePart : rh.fCurvePart; |
| 99 SkPath::Verb partVerb = useThis ? fVerb : rh.fVerb; |
| 100 ray[0] = partVerb == SkPath::kCubic_Verb && part[0].approximatelyEqual(p
art[1]) ? |
| 101 part[2] : part[1]; |
| 102 ray[1].fX = (part[0].fX + part[partVerb].fX) / 2; |
| 103 ray[1].fY = (part[0].fY + part[partVerb].fY) / 2; |
| 104 SkASSERT(ray[0] != ray[1]); |
| 105 roots = (i.*CurveRay[fVerb])(fPts, ray); |
| 106 rroots = (ri.*CurveRay[rh.fVerb])(rh.fPts, ray); |
| 107 } while ((roots == 0 || rroots == 0) && (flip ^= true)); |
| 108 if (roots == 0 || rroots == 0) { |
| 109 // FIXME: we don't have a solution in this case. The interim solution |
| 110 // is to mark the edges as unsortable, exclude them from this and |
| 111 // future computations, and allow the returned path to be fragmented |
| 112 fUnsortable = true; |
| 113 rh.fUnsortable = true; |
| 114 return this < &rh; // even with no solution, return a stable sort |
| 115 } |
| 116 SkDPoint loc; |
| 117 double best = SK_ScalarInfinity; |
| 118 SkDVector dxy; |
| 119 double dist; |
| 120 int index; |
| 121 for (index = 0; index < roots; ++index) { |
| 122 loc = (*CurveDPointAtT[fVerb])(fPts, i[0][index]); |
| 123 dxy = loc - ray[0]; |
| 124 dist = dxy.lengthSquared(); |
| 125 if (best > dist) { |
| 126 best = dist; |
| 127 } |
| 128 } |
| 129 for (index = 0; index < rroots; ++index) { |
| 130 loc = (*CurveDPointAtT[rh.fVerb])(rh.fPts, ri[0][index]); |
| 131 dxy = loc - ray[0]; |
| 132 dist = dxy.lengthSquared(); |
| 133 if (best > dist) { |
| 134 return fSide < 0; |
| 135 } |
| 136 } |
| 137 return fSide > 0; |
| 138 } |
| 139 |
| 140 bool SkOpAngle::lengthen() { |
| 141 int newEnd = fEnd; |
| 142 if (fStart < fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { |
| 143 fEnd = newEnd; |
| 144 setSpans(); |
| 145 return true; |
| 146 } |
| 147 return false; |
| 148 } |
| 149 |
| 150 bool SkOpAngle::reverseLengthen() { |
| 151 if (fReversed) { |
| 152 return false; |
| 153 } |
| 154 int newEnd = fStart; |
| 155 if (fStart > fEnd ? ++newEnd < fSpans->count() : --newEnd >= 0) { |
| 156 fEnd = newEnd; |
| 157 fReversed = true; |
| 158 setSpans(); |
| 159 return true; |
| 160 } |
| 161 return false; |
| 162 } |
| 163 |
| 164 void SkOpAngle::set(const SkPoint* orig, SkPath::Verb verb, const SkOpSegment* s
egment, |
| 165 int start, int end, const SkTDArray<SkOpSpan>& spans) { |
| 166 fSegment = segment; |
| 167 fStart = start; |
| 168 fEnd = end; |
| 169 fPts = orig; |
| 170 fVerb = verb; |
| 171 fSpans = &spans; |
| 172 fReversed = false; |
| 173 fUnsortable = false; |
| 174 setSpans(); |
| 175 } |
| 176 |
| 177 |
| 178 void SkOpAngle::setSpans() { |
| 179 double startT = (*fSpans)[fStart].fT; |
| 180 double endT = (*fSpans)[fEnd].fT; |
| 181 switch (fVerb) { |
| 182 case SkPath::kLine_Verb: { |
| 183 SkDLine l = SkDLine::SubDivide(fPts, startT, endT); |
| 184 // OPTIMIZATION: for pure line compares, we never need fTangent1.c |
| 185 fTangent1.lineEndPoints(l); |
| 186 fSide = 0; |
| 187 } break; |
| 188 case SkPath::kQuad_Verb: { |
| 189 SkDQuad& quad = (SkDQuad&)fCurvePart; |
| 190 quad = SkDQuad::SubDivide(fPts, startT, endT); |
| 191 fTangent1.quadEndPoints(quad, 0, 1); |
| 192 if (dx() == 0 && dy() == 0) { |
| 193 fTangent1.quadEndPoints(quad); |
| 194 } |
| 195 fSide = -fTangent1.pointDistance(fCurvePart[2]); // not normalized -- co
mpare sign only |
| 196 } break; |
| 197 case SkPath::kCubic_Verb: { |
| 198 int nextC = 2; |
| 199 fCurvePart = SkDCubic::SubDivide(fPts, startT, endT); |
| 200 fTangent1.cubicEndPoints(fCurvePart, 0, 1); |
| 201 if (dx() == 0 && dy() == 0) { |
| 202 fTangent1.cubicEndPoints(fCurvePart, 0, 2); |
| 203 nextC = 3; |
| 204 if (dx() == 0 && dy() == 0) { |
| 205 fTangent1.cubicEndPoints(fCurvePart, 0, 3); |
| 206 } |
| 207 } |
| 208 fSide = -fTangent1.pointDistance(fCurvePart[nextC]); // compare sign onl
y |
| 209 if (nextC == 2 && approximately_zero(fSide)) { |
| 210 fSide = -fTangent1.pointDistance(fCurvePart[3]); |
| 211 } |
| 212 } break; |
| 213 default: |
| 214 SkASSERT(0); |
| 215 } |
| 216 fUnsortable = dx() == 0 && dy() == 0; |
| 217 if (fUnsortable) { |
| 218 return; |
| 219 } |
| 220 SkASSERT(fStart != fEnd); |
| 221 int step = fStart < fEnd ? 1 : -1; // OPTIMIZE: worth fStart - fEnd >> 31 ty
pe macro? |
| 222 for (int index = fStart; index != fEnd; index += step) { |
| 223 #if 1 |
| 224 const SkOpSpan& thisSpan = (*fSpans)[index]; |
| 225 const SkOpSpan& nextSpan = (*fSpans)[index + step]; |
| 226 if (thisSpan.fTiny || precisely_equal(thisSpan.fT, nextSpan.fT)) { |
| 227 continue; |
| 228 } |
| 229 fUnsortable = step > 0 ? thisSpan.fUnsortableStart : nextSpan.fUnsortabl
eEnd; |
| 230 #if DEBUG_UNSORTABLE |
| 231 if (fUnsortable) { |
| 232 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, thisSpan.fT); |
| 233 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, nextSpan.fT); |
| 234 SkDebugf("%s unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", __
FUNCTION__, |
| 235 index, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
| 236 } |
| 237 #endif |
| 238 return; |
| 239 #else |
| 240 if ((*fSpans)[index].fUnsortableStart) { |
| 241 fUnsortable = true; |
| 242 return; |
| 243 } |
| 244 #endif |
| 245 } |
| 246 #if 1 |
| 247 #if DEBUG_UNSORTABLE |
| 248 SkPoint iPt = (*CurvePointAtT[fVerb])(fPts, startT); |
| 249 SkPoint ePt = (*CurvePointAtT[fVerb])(fPts, endT); |
| 250 SkDebugf("%s all tiny unsortable [%d] (%1.9g,%1.9g) [%d] (%1.9g,%1.9g)\n", _
_FUNCTION__, |
| 251 fStart, iPt.fX, iPt.fY, fEnd, ePt.fX, ePt.fY); |
| 252 #endif |
| 253 fUnsortable = true; |
| 254 #endif |
| 255 } |
| 256 |
OLD | NEW |