bool intersect(const Quadratic& q1, const Quadratic& q2, Intersections& i) { if (implicit_matches(q1, q2)) { // FIXME: compute T values // compute the intersections of the ends to find the coincident span bool useVertical = fabs(q1[0].x - q1[2].x) < fabs(q1[0].y - q1[2].y); double t; if ((t = axialIntersect(q1, q2[0], useVertical)) >= 0) { i.addCoincident(t, 0); } if ((t = axialIntersect(q1, q2[2], useVertical)) >= 0) { i.addCoincident(t, 1); } useVertical = fabs(q2[0].x - q2[2].x) < fabs(q2[0].y - q2[2].y); if ((t = axialIntersect(q2, q1[0], useVertical)) >= 0) { i.addCoincident(0, t); } if ((t = axialIntersect(q2, q1[2], useVertical)) >= 0) { i.addCoincident(1, t); } assert(i.fCoincidentUsed <= 2); return i.fCoincidentUsed > 0; } QuadraticIntersections q(q1, q2, i); bool result = q.intersect(); // FIXME: partial coincidence detection is currently poor. For now, try // to fix up the data after the fact. In the future, revisit the error // term to try to avoid this kind of result in the first place. if (i.fUsed && i.fCoincidentUsed) { hackToFixPartialCoincidence(q1, q2, i); } return result; }
bool QTessellatorPrivate::edgeInChain(Intersection i, int edge) { int end = i.edge; while (1) { if (i.edge == edge) return true; IntersectionLink l = intersections.value(i); if (l.next == end) break; Q_ASSERT(l.next != -1); Q_ASSERT(l.prev != -1); Intersection i2 = i; i2.edge = l.next; #ifndef QT_NO_DEBUG IntersectionLink l2 = intersections.value(i2); Q_ASSERT(l2.next != -1); Q_ASSERT(l2.prev != -1); Q_ASSERT(l.next == i2.edge); Q_ASSERT(l2.prev == i.edge); #endif i = i2; } return false; }
void Scene::cut_segment_plane() { // Build tree (if build fail, exit) build_facet_tree(); if ( m_facet_tree.empty() ) { return; } Plane plane = frame_plane(); // Compute intersections typedef std::vector<Facet_tree::Object_and_primitive_id> Intersections; Intersections intersections; m_facet_tree.all_intersections(plane, std::back_inserter(intersections)); // Fill data structure m_cut_segments.clear(); for ( Intersections::iterator it = intersections.begin(), end = intersections.end() ; it != end ; ++it ) { const Segment* inter_seg = CGAL::object_cast<Segment>(&(it->first)); if ( NULL != inter_seg ) { m_cut_segments.push_back(*inter_seg); } } m_cut_plane = CUT_SEGMENTS; }
bool intersect(double minT1, double maxT1, double minT2, double maxT2) { Cubic sub1, sub2; // FIXME: carry last subdivide and reduceOrder result with cubic sub_divide(cubic1, minT1, maxT1, sub1); sub_divide(cubic2, minT2, maxT2, sub2); Intersections i; intersect2(sub1, sub2, i); if (i.used() == 0) { return false; } double x1, y1, x2, y2; t1 = minT1 + i.fT[0][0] * (maxT1 - minT1); t2 = minT2 + i.fT[1][0] * (maxT2 - minT2); xy_at_t(cubic1, t1, x1, y1); xy_at_t(cubic2, t2, x2, y2); if (AlmostEqualUlps(x1, x2) && AlmostEqualUlps(y1, y2)) { return true; } double half1 = (minT1 + maxT1) / 2; double half2 = (minT2 + maxT2) / 2; ++depth; bool result; if (depth & 1) { result = intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2) || intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2); } else { result = intersect(minT1, maxT1, minT2, half2) || intersect(minT1, maxT1, half2, maxT2) || intersect(minT1, half1, minT2, maxT2) || intersect(half1, maxT1, minT2, maxT2); } --depth; return result; }
// This method performs intersection testing at the given XY coords, and returns true if // any intersections were found. It will break after processing the first pickable Window // it finds. bool WindowManager::pickAtXY(float x, float y, WidgetList& wl) { Intersections intr; osg::Camera* camera = _view->getCamera(); osgViewer::GraphicsWindow* gw = dynamic_cast<osgViewer::GraphicsWindow*>(camera->getGraphicsContext()); if (gw) { _view->computeIntersections(camera, osgUtil::Intersector::WINDOW, x, y, intr, _nodeMask); } if (!intr.empty()) { // Get the first Window at the XY coordinates; if you want a Window to be // non-pickable, set the NodeMask to something else. Window* activeWin = 0; // Iterate over every picked result and create a list of Widgets that belong // to that Window. for(Intersections::iterator i = intr.begin(); i != intr.end(); i++) { Window* win = dynamic_cast<Window*>(i->nodePath.back()->getParent(0)); // Make sure that our window is valid, and that our pick is within the // "visible area" of the Window. if( !win || (win->getVisibilityMode() == Window::VM_PARTIAL && !win->isPointerXYWithinVisible(x, y)) ) { continue; } // Set our activeWin, so that we know when we've got all the Widgets // that belong to it. if(!activeWin) activeWin = win; // If we've found a new Widnow, break out! else if(activeWin != win) break; Widget* widget = dynamic_cast<Widget*>(i->drawable.get()); if(!widget) continue; // We need to return a list of every Widget that was picked, so // that the handler can operate on it accordingly. else wl.push_back(widget); } if(wl.size()) { // Potentially VERY expensive; only to be used for debugging. :) if(_flags & WM_PICK_DEBUG) _updatePickWindow(&wl, x, y); return true; } } if(_flags & WM_PICK_DEBUG) _updatePickWindow(0, x, y); return false; }
int intersect(const Cubic& c, Intersections& i) { // check to see if x or y end points are the extrema. Are other quick rejects possible? if (ends_are_extrema_in_x_or_y(c)) { return false; } (void) intersect3(c, c, i); if (i.used() > 0) { SkASSERT(i.used() == 1); if (i.fT[0][0] > i.fT[1][0]) { SkTSwap(i.fT[0][0], i.fT[1][0]); } } return i.used(); }
bool Widget::computeIntersections(osgGA::EventVisitor* ev, osgGA::GUIEventAdapter* event, Intersections& intersections, osg::Node::NodeMask traversalMask) const { osgGA::GUIActionAdapter* aa = ev ? ev->getActionAdapter() : 0; osgUtil::LineSegmentIntersector::Intersections source_intersections; if (aa && aa->computeIntersections(*event, ev->getNodePath(), source_intersections, traversalMask)) { typedef std::vector<const osgUtil::LineSegmentIntersector::Intersection*> IntersectionPointerList; IntersectionPointerList intersectionsToSort; // populate the temporay vector of poiners to the original intersection pointers. for(osgUtil::LineSegmentIntersector::Intersections::iterator itr = source_intersections.begin(); itr != source_intersections.end(); ++itr) { if (itr->drawable->getName()!="DepthSetPanel") { intersectionsToSort.push_back(&(*itr)); } } // sort the pointer list into order based on child traversal order, to be consistent with osgUI rendering order. std::sort(intersectionsToSort.begin(), intersectionsToSort.end(), SortTraversalOrder()); // copy the pointers to final Intersection container for(IntersectionPointerList::iterator itr = intersectionsToSort.begin(); itr != intersectionsToSort.end(); ++itr) { intersections.push_back(*(*itr)); } return true; } return false; }
void QTessellatorPrivate::addIntersections() { if (scanline.size) { QDEBUG() << "INTERSECTIONS"; // check marked edges for intersections #ifdef DEBUG for (int i = 0; i < scanline.size; ++i) { Edge *e = scanline.edges[i]; QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right << ')'; } #endif for (int i = 0; i < scanline.size - 1; ++i) { Edge *e1 = scanline.edges[i]; Edge *e2 = scanline.edges[i + 1]; // check for intersection if (e1->intersect_right || e2->intersect_left) addIntersection(e1, e2); } } #if 0 if (intersections.constBegin().key().y == y) { QDEBUG() << "----------------> intersection on same line"; scanline.clearMarks(); scanline.processIntersections(y, &intersections); goto redo; } #endif }
static bool closeEnd(const Cubic& cubic, int cubicIndex, Intersections& i, _Point& pt) { int last = i.used() - 1; if (i.fT[cubicIndex][last] != 1 || i.fT[cubicIndex][last - 1] < 1 - CLOSE_ENOUGH) { return false; } pt = xy_at_t(cubic, (i.fT[cubicIndex][last] + i.fT[cubicIndex][last - 1]) / 2); return true; }
// FIXME: add intersection of convex null on cubics' ends with the opposite cubic. The hull line // segments can be constructed to be only as long as the calculated precision suggests. If the hull // line segments intersect the cubic, then use the intersections to construct a subdivision for // quadratic curve fitting. bool intersect2(const Cubic& c1, const Cubic& c2, Intersections& i) { #if SK_DEBUG debugDepth = 0; #endif bool result = intersect2(c1, 0, 1, c2, 0, 1, 1, i); // FIXME: pass in cached bounds from caller _Rect c1Bounds, c2Bounds; c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ? c2Bounds.setBounds(c2); result |= intersectEnd(c1, false, c2, c2Bounds, i); result |= intersectEnd(c1, true, c2, c2Bounds, i); i.swap(); result |= intersectEnd(c2, false, c1, c1Bounds, i); result |= intersectEnd(c2, true, c1, c1Bounds, i); i.swap(); return result; }
bool intersect3(const Cubic& c1, const Cubic& c2, Intersections& i) { bool result = intersect3(c1, 0, 1, c2, 0, 1, 1, i); // FIXME: pass in cached bounds from caller _Rect c1Bounds, c2Bounds; c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ? c2Bounds.setBounds(c2); result |= intersectEnd(c1, false, c2, c2Bounds, i); result |= intersectEnd(c1, true, c2, c2Bounds, i); bool selfIntersect = c1 == c2; if (!selfIntersect) { i.swap(); result |= intersectEnd(c2, false, c1, c1Bounds, i); result |= intersectEnd(c2, true, c1, c1Bounds, i); i.swap(); } // If an end point and a second point very close to the end is returned, the second // point may have been detected because the approximate quads // intersected at the end and close to it. Verify that the second point is valid. if (i.used() <= 1 || i.coincidentUsed()) { return result; } _Point pt[2]; if (closeStart(c1, 0, i, pt[0]) && closeStart(c2, 1, i, pt[1]) && pt[0].approximatelyEqual(pt[1])) { i.removeOne(1); } if (closeEnd(c1, 0, i, pt[0]) && closeEnd(c2, 1, i, pt[1]) && pt[0].approximatelyEqual(pt[1])) { i.removeOne(i.used() - 2); } return result; }
void CubicIntersection_Test() { for (size_t index = firstCubicIntersectionTest; index < tests_count; ++index) { const Cubic& cubic1 = tests[index][0]; const Cubic& cubic2 = tests[index][1]; Cubic reduce1, reduce2; int order1 = reduceOrder(cubic1, reduce1, kReduceOrder_NoQuadraticsAllowed); int order2 = reduceOrder(cubic2, reduce2, kReduceOrder_NoQuadraticsAllowed); if (order1 < 4) { printf("%s [%d] cubic1 order=%d\n", __FUNCTION__, (int) index, order1); continue; } if (order2 < 4) { printf("%s [%d] cubic2 order=%d\n", __FUNCTION__, (int) index, order2); continue; } if (implicit_matches(reduce1, reduce2)) { printf("%s [%d] coincident\n", __FUNCTION__, (int) index); continue; } Intersections tIntersections; intersect(reduce1, reduce2, tIntersections); if (!tIntersections.intersected()) { printf("%s [%d] no intersection\n", __FUNCTION__, (int) index); continue; } for (int pt = 0; pt < tIntersections.used(); ++pt) { double tt1 = tIntersections.fT[0][pt]; double tx1, ty1; xy_at_t(cubic1, tt1, tx1, ty1); double tt2 = tIntersections.fT[1][pt]; double tx2, ty2; xy_at_t(cubic2, tt2, tx2, ty2); if (!AlmostEqualUlps(tx1, tx2)) { printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); } if (!AlmostEqualUlps(ty1, ty2)) { printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); } } } }
bool intersect(const Cubic& cubic, Intersections& i) { SkTDArray<double> ts; double precision = calcPrecision(cubic); cubic_to_quadratics(cubic, precision, ts); int tsCount = ts.count(); if (tsCount == 1) { return false; } double t1Start = 0; Cubic part; for (int idx = 0; idx < tsCount; ++idx) { double t1 = ts[idx]; Quadratic q1; sub_divide(cubic, t1Start, t1, part); demote_cubic_to_quad(part, q1); double t2Start = t1; for (int i2 = idx + 1; i2 <= tsCount; ++i2) { const double t2 = i2 < tsCount ? ts[i2] : 1; Quadratic q2; sub_divide(cubic, t2Start, t2, part); demote_cubic_to_quad(part, q2); Intersections locals; intersect2(q1, q2, locals); for (int tIdx = 0; tIdx < locals.used(); ++tIdx) { // discard intersections at cusp? (maximum curvature) double t1sect = locals.fT[0][tIdx]; double t2sect = locals.fT[1][tIdx]; if (idx + 1 == i2 && t1sect == 1 && t2sect == 0) { continue; } double to1 = t1Start + (t1 - t1Start) * t1sect; double to2 = t2Start + (t2 - t2Start) * t2sect; i.insert(to1, to2); } t2Start = t2; } t1Start = t1; } return i.intersected(); }
static void standardTestCases() { for (size_t index = firstQuadIntersectionTest; index < quadraticTests_count; ++index) { const Quadratic& quad1 = quadraticTests[index][0]; const Quadratic& quad2 = quadraticTests[index][1]; Quadratic reduce1, reduce2; int order1 = reduceOrder(quad1, reduce1, kReduceOrder_TreatAsFill); int order2 = reduceOrder(quad2, reduce2, kReduceOrder_TreatAsFill); if (order1 < 3) { printf("[%d] quad1 order=%d\n", (int) index, order1); } if (order2 < 3) { printf("[%d] quad2 order=%d\n", (int) index, order2); } if (order1 == 3 && order2 == 3) { Intersections intersections; intersect2(reduce1, reduce2, intersections); if (intersections.intersected()) { for (int pt = 0; pt < intersections.used(); ++pt) { double tt1 = intersections.fT[0][pt]; double tx1, ty1; xy_at_t(quad1, tt1, tx1, ty1); double tt2 = intersections.fT[1][pt]; double tx2, ty2; xy_at_t(quad2, tt2, tx2, ty2); if (!approximately_equal(tx1, tx2)) { printf("%s [%d,%d] x!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); } if (!approximately_equal(ty1, ty2)) { printf("%s [%d,%d] y!= t1=%g (%g,%g) t2=%g (%g,%g)\n", __FUNCTION__, (int)index, pt, tt1, tx1, ty1, tt2, tx2, ty2); } } } } } }
int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& i) { SkTDArray<double> ts; double precision = calcPrecision(cubic); cubic_to_quadratics(cubic, precision, ts); double tStart = 0; Cubic part; int tsCount = ts.count(); for (int idx = 0; idx <= tsCount; ++idx) { double t = idx < tsCount ? ts[idx] : 1; Quadratic q1; sub_divide(cubic, tStart, t, part); demote_cubic_to_quad(part, q1); Intersections locals; intersect2(q1, quad, locals); for (int tIdx = 0; tIdx < locals.used(); ++tIdx) { double globalT = tStart + (t - tStart) * locals.fT[0][tIdx]; i.insertOne(globalT, 0); globalT = locals.fT[1][tIdx]; i.insertOne(globalT, 1); } tStart = t; } return i.used(); }
static void addValidRoots(const double roots[4], const int count, const int side, Intersections& i) { int index; for (index = 0; index < count; ++index) { if (!approximately_zero_or_more(roots[index]) || !approximately_one_or_less(roots[index])) { continue; } double t = 1 - roots[index]; if (approximately_less_than_zero(t)) { t = 0; } else if (approximately_greater_than_one(t)) { t = 1; } i.insertOne(t, side); } }
// Up promote the quad to a cubic. // OPTIMIZATION If this is a common use case, optimize by duplicating // the intersect 3 loop to avoid the promotion / demotion code int intersect(const Cubic& cubic, const Quadratic& quad, Intersections& i) { Cubic up; toCubic(quad, up); (void) intersect3(cubic, up, i); return i.used(); }
static void hackToFixPartialCoincidence(const Quadratic& q1, const Quadratic& q2, Intersections& i) { // look to see if non-coincident data basically has unsortable tangents // look to see if a point between non-coincident data is on the curve int cIndex; for (int uIndex = 0; uIndex < i.fUsed; ) { double bestDist1 = 1; double bestDist2 = 1; int closest1 = -1; int closest2 = -1; for (cIndex = 0; cIndex < i.fCoincidentUsed; ++cIndex) { double dist = fabs(i.fT[0][uIndex] - i.fCoincidentT[0][cIndex]); if (bestDist1 > dist) { bestDist1 = dist; closest1 = cIndex; } dist = fabs(i.fT[1][uIndex] - i.fCoincidentT[1][cIndex]); if (bestDist2 > dist) { bestDist2 = dist; closest2 = cIndex; } } _Line ends; _Point mid; double t1 = i.fT[0][uIndex]; xy_at_t(q1, t1, ends[0].x, ends[0].y); xy_at_t(q1, i.fCoincidentT[0][closest1], ends[1].x, ends[1].y); double midT = (t1 + i.fCoincidentT[0][closest1]) / 2; xy_at_t(q1, midT, mid.x, mid.y); LineParameters params; params.lineEndPoints(ends); double midDist = params.pointDistance(mid); // Note that we prefer to always measure t error, which does not scale, // instead of point error, which is scale dependent. FIXME if (!approximately_zero(midDist)) { ++uIndex; continue; } double t2 = i.fT[1][uIndex]; xy_at_t(q2, t2, ends[0].x, ends[0].y); xy_at_t(q2, i.fCoincidentT[1][closest2], ends[1].x, ends[1].y); midT = (t2 + i.fCoincidentT[1][closest2]) / 2; xy_at_t(q2, midT, mid.x, mid.y); params.lineEndPoints(ends); midDist = params.pointDistance(mid); if (!approximately_zero(midDist)) { ++uIndex; continue; } // if both midpoints are close to the line, lengthen coincident span int cEnd = closest1 ^ 1; // assume coincidence always travels in pairs if (!between(i.fCoincidentT[0][cEnd], t1, i.fCoincidentT[0][closest1])) { i.fCoincidentT[0][closest1] = t1; } cEnd = closest2 ^ 1; if (!between(i.fCoincidentT[0][cEnd], t2, i.fCoincidentT[0][closest2])) { i.fCoincidentT[0][closest2] = t2; } int remaining = --i.fUsed - uIndex; if (remaining > 0) { memmove(&i.fT[0][uIndex], &i.fT[0][uIndex + 1], sizeof(i.fT[0][0]) * remaining); memmove(&i.fT[1][uIndex], &i.fT[1][uIndex + 1], sizeof(i.fT[1][0]) * remaining); } } // if coincident data is subjectively a tiny span, replace it with a single point for (cIndex = 0; cIndex < i.fCoincidentUsed; ) { double start1 = i.fCoincidentT[0][cIndex]; double end1 = i.fCoincidentT[0][cIndex + 1]; _Line ends1; xy_at_t(q1, start1, ends1[0].x, ends1[0].y); xy_at_t(q1, end1, ends1[1].x, ends1[1].y); if (!AlmostEqualUlps(ends1[0].x, ends1[1].x) || AlmostEqualUlps(ends1[0].y, ends1[1].y)) { cIndex += 2; continue; } double start2 = i.fCoincidentT[1][cIndex]; double end2 = i.fCoincidentT[1][cIndex + 1]; _Line ends2; xy_at_t(q2, start2, ends2[0].x, ends2[0].y); xy_at_t(q2, end2, ends2[1].x, ends2[1].y); // again, approximately should be used with T values, not points FIXME if (!AlmostEqualUlps(ends2[0].x, ends2[1].x) || AlmostEqualUlps(ends2[0].y, ends2[1].y)) { cIndex += 2; continue; } if (approximately_less_than_zero(start1) || approximately_less_than_zero(end1)) { start1 = 0; } else if (approximately_greater_than_one(start1) || approximately_greater_than_one(end1)) { start1 = 1; } else { start1 = (start1 + end1) / 2; } if (approximately_less_than_zero(start2) || approximately_less_than_zero(end2)) { start2 = 0; } else if (approximately_greater_than_one(start2) || approximately_greater_than_one(end2)) { start2 = 1; } else { start2 = (start2 + end2) / 2; } i.insert(start1, start2); i.fCoincidentUsed -= 2; int remaining = i.fCoincidentUsed - cIndex; if (remaining > 0) { memmove(&i.fCoincidentT[0][cIndex], &i.fCoincidentT[0][cIndex + 2], sizeof(i.fCoincidentT[0][0]) * remaining); memmove(&i.fCoincidentT[1][cIndex], &i.fCoincidentT[1][cIndex + 2], sizeof(i.fCoincidentT[1][0]) * remaining); } } }
// this flavor centers potential intersections recursively. In contrast, '2' may inadvertently // chase intersections near quadratic ends, requiring odd hacks to find them. static bool intersect3(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2, double t2s, double t2e, double precisionScale, Intersections& i) { i.upDepth(); bool result = false; Cubic c1, c2; sub_divide(cubic1, t1s, t1e, c1); sub_divide(cubic2, t2s, t2e, c2); SkTDArray<double> ts1; // OPTIMIZE: if c1 == c2, call once (happens when detecting self-intersection) cubic_to_quadratics(c1, calcPrecision(c1) * precisionScale, ts1); SkTDArray<double> ts2; cubic_to_quadratics(c2, calcPrecision(c2) * precisionScale, ts2); double t1Start = t1s; int ts1Count = ts1.count(); for (int i1 = 0; i1 <= ts1Count; ++i1) { const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1; const double t1 = t1s + (t1e - t1s) * tEnd1; Quadratic s1; int o1 = quadPart(cubic1, t1Start, t1, s1); double t2Start = t2s; int ts2Count = ts2.count(); for (int i2 = 0; i2 <= ts2Count; ++i2) { const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1; const double t2 = t2s + (t2e - t2s) * tEnd2; if (cubic1 == cubic2 && t1Start >= t2Start) { t2Start = t2; continue; } Quadratic s2; int o2 = quadPart(cubic2, t2Start, t2, s2); #if ONE_OFF_DEBUG char tab[] = " "; if (tLimits1[0][0] >= t1Start && tLimits1[0][1] <= t1 && tLimits1[1][0] >= t2Start && tLimits1[1][1] <= t2) { Cubic cSub1, cSub2; sub_divide(cubic1, t1Start, t1, cSub1); sub_divide(cubic2, t2Start, t2, cSub2); SkDebugf("%.*s %s t1=(%1.9g,%1.9g) t2=(%1.9g,%1.9g)", i.depth()*2, tab, __FUNCTION__, t1Start, t1, t2Start, t2); Intersections xlocals; intersectWithOrder(s1, o1, s2, o2, xlocals); SkDebugf(" xlocals.fUsed=%d\n", xlocals.used()); } #endif Intersections locals; intersectWithOrder(s1, o1, s2, o2, locals); double coStart[2] = { -1 }; _Point coPoint; int tCount = locals.used(); for (int tIdx = 0; tIdx < tCount; ++tIdx) { double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx]; double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx]; // if the computed t is not sufficiently precise, iterate _Point p1 = xy_at_t(cubic1, to1); _Point p2 = xy_at_t(cubic2, to2); if (p1.approximatelyEqual(p2)) { if (locals.fIsCoincident[0] & 1 << tIdx) { if (coStart[0] < 0) { coStart[0] = to1; coStart[1] = to2; coPoint = p1; } else { i.insertCoincidentPair(coStart[0], to1, coStart[1], to2, coPoint, p1); coStart[0] = -1; } result = true; } else if (cubic1 != cubic2 || !approximately_equal(to1, to2)) { if (i.swapped()) { // FIXME: insert should respect swap i.insert(to2, to1, p1); } else { i.insert(to1, to2, p1); } result = true; } } else { double offset = precisionScale / 16; // FIME: const is arbitrary -- test & refine #if 1 double c1Bottom = tIdx == 0 ? 0 : (t1Start + (t1 - t1Start) * locals.fT[0][tIdx - 1] + to1) / 2; double c1Min = SkTMax(c1Bottom, to1 - offset); double c1Top = tIdx == tCount - 1 ? 1 : (t1Start + (t1 - t1Start) * locals.fT[0][tIdx + 1] + to1) / 2; double c1Max = SkTMin(c1Top, to1 + offset); double c2Min = SkTMax(0., to2 - offset); double c2Max = SkTMin(1., to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 1 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 1 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, c1Bottom, c1Top, 0., 1., to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 1 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 1 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i.fT[0][i.used() - 1] : -1); #endif if (tCount > 1) { c1Min = SkTMax(0., to1 - offset); c1Max = SkTMin(1., to1 + offset); double c2Bottom = tIdx == 0 ? to2 : (t2Start + (t2 - t2Start) * locals.fT[1][tIdx - 1] + to2) / 2; double c2Top = tIdx == tCount - 1 ? to2 : (t2Start + (t2 - t2Start) * locals.fT[1][tIdx + 1] + to2) / 2; if (c2Bottom > c2Top) { SkTSwap(c2Bottom, c2Top); } if (c2Bottom == to2) { c2Bottom = 0; } if (c2Top == to2) { c2Top = 1; } c2Min = SkTMax(c2Bottom, to2 - offset); c2Max = SkTMin(c2Top, to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 2 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 2 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 2 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 2 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i.fT[0][i.used() - 1] : -1); #endif c1Min = SkTMax(c1Bottom, to1 - offset); c1Max = SkTMin(c1Top, to1 + offset); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 3 contains1=%d/%d contains2=%d/%d\n", i.depth()*2, tab, __FUNCTION__, c1Min <= tLimits1[0][1] && tLimits1[0][0] <= c1Max && c2Min <= tLimits1[1][1] && tLimits1[1][0] <= c2Max, to1 - offset <= tLimits1[0][1] && tLimits1[0][0] <= to1 + offset && to2 - offset <= tLimits1[1][1] && tLimits1[1][0] <= to2 + offset, c1Min <= tLimits2[0][1] && tLimits2[0][0] <= c1Max && c2Min <= tLimits2[1][1] && tLimits2[1][0] <= c2Max, to1 - offset <= tLimits2[0][1] && tLimits2[0][0] <= to1 + offset && to2 - offset <= tLimits2[1][1] && tLimits2[1][0] <= to2 + offset); SkDebugf("%.*s %s 3 c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", i.depth()*2, tab, __FUNCTION__, 0., 1., c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%.*s %s 3 to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", i.depth()*2, tab, __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); #if ONE_OFF_DEBUG SkDebugf("%.*s %s 3 i.used=%d t=%1.9g\n", i.depth()*2, tab, __FUNCTION__, i.used(), i.used() > 0 ? i.fT[0][i.used() - 1] : -1); #endif } #else double c1Bottom = tIdx == 0 ? 0 : (t1Start + (t1 - t1Start) * locals.fT[0][tIdx - 1] + to1) / 2; double c1Min = SkTMax(c1Bottom, to1 - offset); double c1Top = tIdx == tCount - 1 ? 1 : (t1Start + (t1 - t1Start) * locals.fT[0][tIdx + 1] + to1) / 2; double c1Max = SkTMin(c1Top, to1 + offset); double c2Bottom = tIdx == 0 ? to2 : (t2Start + (t2 - t2Start) * locals.fT[1][tIdx - 1] + to2) / 2; double c2Top = tIdx == tCount - 1 ? to2 : (t2Start + (t2 - t2Start) * locals.fT[1][tIdx + 1] + to2) / 2; if (c2Bottom > c2Top) { SkTSwap(c2Bottom, c2Top); } if (c2Bottom == to2) { c2Bottom = 0; } if (c2Top == to2) { c2Top = 1; } double c2Min = SkTMax(c2Bottom, to2 - offset); double c2Max = SkTMin(c2Top, to2 + offset); #if ONE_OFF_DEBUG SkDebugf("%s contains1=%d/%d contains2=%d/%d\n", __FUNCTION__, c1Min <= 0.210357794 && 0.210357794 <= c1Max && c2Min <= 0.223476406 && 0.223476406 <= c2Max, to1 - offset <= 0.210357794 && 0.210357794 <= to1 + offset && to2 - offset <= 0.223476406 && 0.223476406 <= to2 + offset, c1Min <= 0.211324707 && 0.211324707 <= c1Max && c2Min <= 0.211327209 && 0.211327209 <= c2Max, to1 - offset <= 0.211324707 && 0.211324707 <= to1 + offset && to2 - offset <= 0.211327209 && 0.211327209 <= to2 + offset); SkDebugf("%s c1Bottom=%1.9g c1Top=%1.9g c2Bottom=%1.9g c2Top=%1.9g" " 1-o=%1.9g 1+o=%1.9g 2-o=%1.9g 2+o=%1.9g offset=%1.9g\n", __FUNCTION__, c1Bottom, c1Top, c2Bottom, c2Top, to1 - offset, to1 + offset, to2 - offset, to2 + offset, offset); SkDebugf("%s to1=%1.9g to2=%1.9g c1Min=%1.9g c1Max=%1.9g c2Min=%1.9g" " c2Max=%1.9g\n", __FUNCTION__, to1, to2, c1Min, c1Max, c2Min, c2Max); #endif #endif intersect3(cubic1, c1Min, c1Max, cubic2, c2Min, c2Max, offset, i); // TODO: if no intersection is found, either quadratics intersected where // cubics did not, or the intersection was missed. In the former case, expect // the quadratics to be nearly parallel at the point of intersection, and check // for that. } } SkASSERT(coStart[0] == -1); t2Start = t2; } t1Start = t1; } i.downDepth(); return result; }
void QTessellatorPrivate::processIntersections() { QDEBUG() << "PROCESS INTERSECTIONS"; // process intersections while (!intersections.isEmpty()) { Intersections::iterator it = intersections.begin(); if (it.key().y != y) break; // swap edges QDEBUG() << " swapping intersecting edges "; int min = scanline.size; int max = 0; Q27Dot5 xmin = INT_MAX; Q27Dot5 xmax = INT_MIN; int num = 0; while (1) { const Intersection &i = it.key(); int next = it->next; int edgePos = scanline.findEdge(i.edge); if (edgePos >= 0) { ++num; min = qMin(edgePos, min); max = qMax(edgePos, max); Edge *edge = scanline.edges[edgePos]; xmin = qMin(xmin, edge->positionAt(y)); xmax = qMax(xmax, edge->positionAt(y)); } Intersection key; key.y = y; key.edge = next; it = intersections.find(key); intersections.remove(i); if (it == intersections.end()) break; } if (num < 2) continue; Q_ASSERT(min != max); QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax; while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) { QDEBUG() << " adding edge on left"; --min; } while (max + 1 < scanline.size && scanline.edges[max + 1]->positionAt(y) <= xmax) { QDEBUG() << " adding edge on right"; ++max; } qSort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y)); #ifdef DEBUG for (int i = min; i <= max; ++i) QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i; #endif for (int i = min; i <= max; ++i) { Edge *edge = scanline.edges[i]; edge->intersect_left = true; edge->intersect_right = true; edge->mark = true; } } }
// intersect the end of the cubic with the other. Try lines from the end to control and opposite // end to determine range of t on opposite cubic. static bool intersectEnd(const Cubic& cubic1, bool start, const Cubic& cubic2, const _Rect& bounds2, Intersections& i) { // bool selfIntersect = cubic1 == cubic2; _Line line; int t1Index = start ? 0 : 3; line[0] = cubic1[t1Index]; // don't bother if the two cubics are connnected #if 0 if (!selfIntersect && (line[0].approximatelyEqual(cubic2[0]) || line[0].approximatelyEqual(cubic2[3]))) { return false; } #endif bool result = false; SkTDArray<double> tVals; // OPTIMIZE: replace with hard-sized array for (int index = 0; index < 4; ++index) { if (index == t1Index) { continue; } _Vector dxy1 = cubic1[index] - line[0]; dxy1 /= gPrecisionUnit; line[1] = line[0] + dxy1; _Rect lineBounds; lineBounds.setBounds(line); if (!bounds2.intersects(lineBounds)) { continue; } Intersections local; if (!intersect(cubic2, line, local)) { continue; } for (int idx2 = 0; idx2 < local.used(); ++idx2) { double foundT = local.fT[0][idx2]; if (approximately_less_than_zero(foundT) || approximately_greater_than_one(foundT)) { continue; } if (local.fPt[idx2].approximatelyEqual(line[0])) { if (i.swapped()) { // FIXME: insert should respect swap i.insert(foundT, start ? 0 : 1, line[0]); } else { i.insert(start ? 0 : 1, foundT, line[0]); } result = true; } else { *tVals.append() = local.fT[0][idx2]; } } } if (tVals.count() == 0) { return result; } QSort<double>(tVals.begin(), tVals.end() - 1); double tMin1 = start ? 0 : 1 - LINE_FRACTION; double tMax1 = start ? LINE_FRACTION : 1; int tIdx = 0; do { int tLast = tIdx; while (tLast + 1 < tVals.count() && roughly_equal(tVals[tLast + 1], tVals[tIdx])) { ++tLast; } double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0); double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0); int lastUsed = i.used(); result |= intersect3(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i); if (lastUsed == i.used()) { tMin2 = SkTMax(tVals[tIdx] - (1.0 / gPrecisionUnit), 0.0); tMax2 = SkTMin(tVals[tLast] + (1.0 / gPrecisionUnit), 1.0); result |= intersect3(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i); } tIdx = tLast + 1; } while (tIdx < tVals.count()); return result; }
void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2) { const IntersectionLink emptyLink = {-1, -1}; int next = vertices.nextPos(vertices[e1->edge]); if (e2->edge == next) return; int prev = vertices.prevPos(vertices[e1->edge]); if (e2->edge == prev) return; Q27Dot5 yi; bool det_positive; bool isect = e1->intersect(*e2, &yi, &det_positive); QDEBUG("checking edges %d and %d", e1->edge, e2->edge); if (!isect) { QDEBUG() << " no intersection"; return; } // don't emit an intersection if it's at the start of a line segment or above us if (yi <= y) { if (!det_positive) return; QDEBUG() << " ----->>>>>> WRONG ORDER!"; yi = y; } QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point (" << Q27Dot5ToDouble(yi) << ')'; Intersection i1; i1.y = yi; i1.edge = e1->edge; IntersectionLink link1 = intersections.value(i1, emptyLink); Intersection i2; i2.y = yi; i2.edge = e2->edge; IntersectionLink link2 = intersections.value(i2, emptyLink); // new pair of edges if (link1.next == -1 && link2.next == -1) { link1.next = link1.prev = i2.edge; link2.next = link2.prev = i1.edge; } else if (link1.next == i2.edge || link1.prev == i2.edge || link2.next == i1.edge || link2.prev == i1.edge) { #ifdef DEBUG checkLinkChain(intersections, i1); checkLinkChain(intersections, i2); Q_ASSERT(edgeInChain(i1, i2.edge)); #endif return; } else if (link1.next == -1 || link2.next == -1) { if (link2.next == -1) { qSwap(i1, i2); qSwap(link1, link2); } Q_ASSERT(link1.next == -1); #ifdef DEBUG checkLinkChain(intersections, i2); #endif // only i2 in list link1.next = i2.edge; link1.prev = link2.prev; link2.prev = i1.edge; Intersection other; other.y = yi; other.edge = link1.prev; IntersectionLink link = intersections.value(other, emptyLink); Q_ASSERT(link.next == i2.edge); Q_ASSERT(link.prev != -1); link.next = i1.edge; intersections.insert(other, link); } else { bool connected = edgeInChain(i1, i2.edge); if (connected) return; #ifdef DEBUG checkLinkChain(intersections, i1); checkLinkChain(intersections, i2); #endif // both already in some list. Have to make sure they are connected // this can be done by cutting open the ring(s) after the two eges and // connecting them again Intersection other1; other1.y = yi; other1.edge = link1.next; IntersectionLink linko1 = intersections.value(other1, emptyLink); Intersection other2; other2.y = yi; other2.edge = link2.next; IntersectionLink linko2 = intersections.value(other2, emptyLink); linko1.prev = i2.edge; link2.next = other1.edge; linko2.prev = i1.edge; link1.next = other2.edge; intersections.insert(other1, linko1); intersections.insert(other2, linko2); } intersections.insert(i1, link1); intersections.insert(i2, link2); #ifdef DEBUG checkLinkChain(intersections, i1); checkLinkChain(intersections, i2); Q_ASSERT(edgeInChain(i1, i2.edge)); #endif return; }
// this flavor approximates the cubics with quads to find the intersecting ts // OPTIMIZE: if this strategy proves successful, the quad approximations, or the ts used // to create the approximations, could be stored in the cubic segment // FIXME: this strategy needs to intersect the convex hull on either end with the opposite to // account for inset quadratics that cause the endpoint intersection to avoid detection // the segments can be very short -- the length of the maximum quadratic error (precision) // FIXME: this needs to recurse on itself, taking a range of T values and computing the new // t range ala is linear inner. The range can be figured by taking the dx/dy and determining // the fraction that matches the precision. That fraction is the change in t for the smaller cubic. static bool intersect2(const Cubic& cubic1, double t1s, double t1e, const Cubic& cubic2, double t2s, double t2e, double precisionScale, Intersections& i) { Cubic c1, c2; sub_divide(cubic1, t1s, t1e, c1); sub_divide(cubic2, t2s, t2e, c2); SkTDArray<double> ts1; cubic_to_quadratics(c1, calcPrecision(c1) * precisionScale, ts1); SkTDArray<double> ts2; cubic_to_quadratics(c2, calcPrecision(c2) * precisionScale, ts2); double t1Start = t1s; int ts1Count = ts1.count(); for (int i1 = 0; i1 <= ts1Count; ++i1) { const double tEnd1 = i1 < ts1Count ? ts1[i1] : 1; const double t1 = t1s + (t1e - t1s) * tEnd1; Cubic part1; sub_divide(cubic1, t1Start, t1, part1); Quadratic q1; demote_cubic_to_quad(part1, q1); // start here; // should reduceOrder be looser in this use case if quartic is going to blow up on an // extremely shallow quadratic? Quadratic s1; int o1 = reduceOrder(q1, s1); double t2Start = t2s; int ts2Count = ts2.count(); for (int i2 = 0; i2 <= ts2Count; ++i2) { const double tEnd2 = i2 < ts2Count ? ts2[i2] : 1; const double t2 = t2s + (t2e - t2s) * tEnd2; Cubic part2; sub_divide(cubic2, t2Start, t2, part2); Quadratic q2; demote_cubic_to_quad(part2, q2); Quadratic s2; double o2 = reduceOrder(q2, s2); Intersections locals; if (o1 == 3 && o2 == 3) { intersect2(q1, q2, locals); } else if (o1 <= 2 && o2 <= 2) { locals.fUsed = intersect((const _Line&) s1, (const _Line&) s2, locals.fT[0], locals.fT[1]); } else if (o1 == 3 && o2 <= 2) { intersect(q1, (const _Line&) s2, locals); } else { SkASSERT(o1 <= 2 && o2 == 3); intersect(q2, (const _Line&) s1, locals); for (int s = 0; s < locals.fUsed; ++s) { SkTSwap(locals.fT[0][s], locals.fT[1][s]); } } for (int tIdx = 0; tIdx < locals.used(); ++tIdx) { double to1 = t1Start + (t1 - t1Start) * locals.fT[0][tIdx]; double to2 = t2Start + (t2 - t2Start) * locals.fT[1][tIdx]; // if the computed t is not sufficiently precise, iterate _Point p1, p2; xy_at_t(cubic1, to1, p1.x, p1.y); xy_at_t(cubic2, to2, p2.x, p2.y); if (p1.approximatelyEqual(p2)) { i.insert(i.swapped() ? to2 : to1, i.swapped() ? to1 : to2); } else { double dt1, dt2; computeDelta(cubic1, to1, (t1e - t1s), cubic2, to2, (t2e - t2s), dt1, dt2); double scale = precisionScale; if (dt1 > 0.125 || dt2 > 0.125) { scale /= 2; SkDebugf("%s scale=%1.9g\n", __FUNCTION__, scale); } #if SK_DEBUG ++debugDepth; assert(debugDepth < 10); #endif i.swap(); intersect2(cubic2, SkTMax(to2 - dt2, 0.), SkTMin(to2 + dt2, 1.), cubic1, SkTMax(to1 - dt1, 0.), SkTMin(to1 + dt1, 1.), scale, i); i.swap(); #if SK_DEBUG --debugDepth; #endif } } t2Start = t2; } t1Start = t1; } return i.intersected(); }