DEF_TEST(PathOpsAngleCircle, reporter) { SkChunkAlloc allocator(4096); SkOpContour contour; SkOpGlobalState state(NULL PATH_OPS_DEBUG_PARAMS(&contour)); contour.init(&state, false, false); for (int index = 0; index < circleDataSetSize; ++index) { CircleData& data = circleDataSet[index]; for (int idx2 = 0; idx2 < data.fPtCount; ++idx2) { data.fShortPts[idx2] = data.fPts.fPts[idx2].asSkPoint(); } switch (data.fPtCount) { case 2: contour.addLine(data.fShortPts, &allocator); break; case 3: contour.addQuad(data.fShortPts, &allocator); break; case 4: contour.addCubic(data.fShortPts, &allocator); break; } } SkOpSegment* first = contour.first(); first->debugAddAngle(0, 1, &allocator); SkOpSegment* next = first->next(); next->debugAddAngle(0, 1, &allocator); PathOpsAngleTester::Orderable(*first->debugLastAngle(), *next->debugLastAngle()); }
static void testQuadAngles(skiatest::Reporter* reporter, const SkDQuad& quad1, const SkDQuad& quad2, int testNo, SkChunkAlloc* allocator) { SkPoint shortQuads[2][3]; SkOpContour contour; SkOpGlobalState state(NULL PATH_OPS_DEBUG_PARAMS(&contour)); contour.init(&state, false, false); makeSegment(&contour, quad1, shortQuads[0], allocator); makeSegment(&contour, quad1, shortQuads[1], allocator); SkOpSegment* seg1 = contour.first(); seg1->debugAddAngle(0, 1, allocator); SkOpSegment* seg2 = seg1->next(); seg2->debugAddAngle(0, 1, allocator); int realOverlap = PathOpsAngleTester::ConvexHullOverlaps(*seg1->debugLastAngle(), *seg2->debugLastAngle()); const SkDPoint& origin = quad1[0]; REPORTER_ASSERT(reporter, origin == quad2[0]); double a1s = atan2(origin.fY - quad1[1].fY, quad1[1].fX - origin.fX); double a1e = atan2(origin.fY - quad1[2].fY, quad1[2].fX - origin.fX); double a2s = atan2(origin.fY - quad2[1].fY, quad2[1].fX - origin.fX); double a2e = atan2(origin.fY - quad2[2].fY, quad2[2].fX - origin.fX); bool oldSchoolOverlap = radianBetween(a1s, a2s, a1e) || radianBetween(a1s, a2e, a1e) || radianBetween(a2s, a1s, a2e) || radianBetween(a2s, a1e, a2e); int overlap = quadHullsOverlap(reporter, quad1, quad2); bool realMatchesOverlap = realOverlap == overlap || SK_ScalarPI - fabs(a2s - a1s) < 0.002; if (realOverlap != overlap) { SkDebugf("\nSK_ScalarPI - fabs(a2s - a1s) = %1.9g\n", SK_ScalarPI - fabs(a2s - a1s)); } if (!realMatchesOverlap) { DumpQ(quad1, quad2, testNo); } REPORTER_ASSERT(reporter, realMatchesOverlap); if (oldSchoolOverlap != (overlap < 0)) { overlap = quadHullsOverlap(reporter, quad1, quad2); // set a breakpoint and debug if assert fires REPORTER_ASSERT(reporter, oldSchoolOverlap == (overlap < 0)); } SkDVector v1s = quad1[1] - quad1[0]; SkDVector v1e = quad1[2] - quad1[0]; SkDVector v2s = quad2[1] - quad2[0]; SkDVector v2e = quad2[2] - quad2[0]; double vDir[2] = { v1s.cross(v1e), v2s.cross(v2e) }; bool ray1In2 = v1s.cross(v2s) * vDir[1] <= 0 && v1s.cross(v2e) * vDir[1] >= 0; bool ray2In1 = v2s.cross(v1s) * vDir[0] <= 0 && v2s.cross(v1e) * vDir[0] >= 0; if (overlap >= 0) { // verify that hulls really don't overlap REPORTER_ASSERT(reporter, !ray1In2); REPORTER_ASSERT(reporter, !ray2In1); bool ctrl1In2 = v1e.cross(v2s) * vDir[1] <= 0 && v1e.cross(v2e) * vDir[1] >= 0; REPORTER_ASSERT(reporter, !ctrl1In2); bool ctrl2In1 = v2e.cross(v1s) * vDir[0] <= 0 && v2e.cross(v1e) * vDir[0] >= 0; REPORTER_ASSERT(reporter, !ctrl2In1); // check answer against reference bruteForce(reporter, quad1, quad2, overlap > 0); } // continue end point rays and see if they intersect the opposite curve SkDLine rays[] = {{{origin, quad2[2]}}, {{origin, quad1[2]}}}; const SkDQuad* quads[] = {&quad1, &quad2}; SkDVector midSpokes[2]; SkIntersections intersect[2]; double minX, minY, maxX, maxY; minX = minY = SK_ScalarInfinity; maxX = maxY = -SK_ScalarInfinity; double maxWidth = 0; bool useIntersect = false; double smallestTs[] = {1, 1}; for (unsigned index = 0; index < SK_ARRAY_COUNT(quads); ++index) { const SkDQuad& q = *quads[index]; midSpokes[index] = q.ptAtT(0.5) - origin; minX = SkTMin(SkTMin(SkTMin(minX, origin.fX), q[1].fX), q[2].fX); minY = SkTMin(SkTMin(SkTMin(minY, origin.fY), q[1].fY), q[2].fY); maxX = SkTMax(SkTMax(SkTMax(maxX, origin.fX), q[1].fX), q[2].fX); maxY = SkTMax(SkTMax(SkTMax(maxY, origin.fY), q[1].fY), q[2].fY); maxWidth = SkTMax(maxWidth, SkTMax(maxX - minX, maxY - minY)); intersect[index].intersectRay(q, rays[index]); const SkIntersections& i = intersect[index]; REPORTER_ASSERT(reporter, i.used() >= 1); bool foundZero = false; double smallT = 1; for (int idx2 = 0; idx2 < i.used(); ++idx2) { double t = i[0][idx2]; if (t == 0) { foundZero = true; continue; } if (smallT > t) { smallT = t; } } REPORTER_ASSERT(reporter, foundZero == true); if (smallT == 1) { continue; } SkDVector ray = q.ptAtT(smallT) - origin; SkDVector end = rays[index][1] - origin; if (ray.fX * end.fX < 0 || ray.fY * end.fY < 0) { continue; } double rayDist = ray.length(); double endDist = end.length(); double delta = fabs(rayDist - endDist) / maxWidth; if (delta > 1e-4) { useIntersect ^= true; } smallestTs[index] = smallT; } bool firstInside; if (useIntersect) { int sIndex = (int) (smallestTs[1] < 1); REPORTER_ASSERT(reporter, smallestTs[sIndex ^ 1] == 1); double t = smallestTs[sIndex]; const SkDQuad& q = *quads[sIndex]; SkDVector ray = q.ptAtT(t) - origin; SkDVector end = rays[sIndex][1] - origin; double rayDist = ray.length(); double endDist = end.length(); SkDVector mid = q.ptAtT(t / 2) - origin; double midXray = mid.crossCheck(ray); if (gPathOpsAngleIdeasVerbose) { SkDebugf("rayDist>endDist:%d sIndex==0:%d vDir[sIndex]<0:%d midXray<0:%d\n", rayDist > endDist, sIndex == 0, vDir[sIndex] < 0, midXray < 0); } SkASSERT(SkScalarSignAsInt(SkDoubleToScalar(midXray)) == SkScalarSignAsInt(SkDoubleToScalar(vDir[sIndex]))); firstInside = (rayDist > endDist) ^ (sIndex == 0) ^ (vDir[sIndex] < 0); } else if (overlap >= 0) { return; // answer has already been determined } else { firstInside = checkParallel(reporter, quad1, quad2); } if (overlap < 0) { SkDEBUGCODE(int realEnds =) PathOpsAngleTester::EndsIntersect(*seg1->debugLastAngle(), *seg2->debugLastAngle()); SkASSERT(realEnds == (firstInside ? 1 : 0)); }