staticinline cairo_int64_t
det32_64 (int32_t a, int32_t b,
int32_t c, int32_t d)
{ /* det = a * d - b * c */ return _cairo_int64_sub (_cairo_int32x32_64_mul (a, d),
_cairo_int32x32_64_mul (b, c));
}
staticinline cairo_int128_t
det64x32_128 (cairo_int64_t a, int32_t b,
cairo_int64_t c, int32_t d)
{ /* det = a * d - b * c */ return _cairo_int128_sub (_cairo_int64x32_128_mul (a, d),
_cairo_int64x32_128_mul (c, b));
}
/* Compute the intersection of two lines as defined by two edges. The *resultisprovidedasacoordinatepairof128-bitintegers. * *Returns%CAIRO_BO_STATUS_INTERSECTIONifthereisanintersectionor *%CAIRO_BO_STATUS_PARALLELifthetwolinesareexactlyparallel.
*/ static cairo_bool_t
intersect_lines (cairo_bo_edge_t *a,
cairo_bo_edge_t *b,
cairo_bo_intersect_point_t *intersection)
{
cairo_int64_t a_det, b_det;
/* XXX: We're assuming here that dx and dy will still fit in 32 *bits.That'snottrueingeneralastherecouldbeoverflow.We *shouldpreventthatbeforethetessellationalgorithmbegins. *Whatwe'redoingtomitigatethisistoperformclampingin *cairo_bo_tessellate_polygon().
*/
int32_t dx1 = a->edge.line.p1.x - a->edge.line.p2.x;
int32_t dy1 = a->edge.line.p1.y - a->edge.line.p2.y;
staticint
_cairo_bo_intersect_ordinate_32_compare (cairo_bo_intersect_ordinate_t a,
int32_t b)
{ /* First compare the quotient */ if (a.ordinate > b) return +1; if (a.ordinate < b) return -1; /* With quotient identical, if remainder is 0 then compare equal */ /* Otherwise, the non-zero remainder makes a > b */ return INEXACT == a.exactness;
}
/* Does the given edge contain the given point. The point must already *beknowntobecontainedwithinthelinedeterminedbytheedge, *(mostlikelythepointresultsfromanintersectionofthisedge *withanother). * *Ifwehadexactarithmetic,thenthisfunctionwouldsimplybea *matterofexaminingwhethertheyvalueofthepointlieswithin *therangeofyvaluesoftheedge.Butsinceintersectionpoints *arenotexactduetobeingroundedtothenearestintegerwithin *theavailableprecision,wemustalsoexaminethexvalueofthe *point. * *Thedefinitionof"contains"hereisthatthegivenintersection *pointwillbeseenbythesweeplineafterthestarteventforthe *givenedgeandbeforethestopeventfortheedge.Seethecomments *intheimplementationformoredetails.
*/ static cairo_bool_t
_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge,
cairo_bo_intersect_point_t *point)
{ int cmp_top, cmp_bottom;
/* XXX: When running the actual algorithm, we don't actually need to *compareagainstedge->topatallhere,sinceanyintersectionabove *topiseliminatedearlyviaaslopecomparison.We'releavingthese *herefornowonlyforthesakeofthequadratic-timeintersection *finderwhichneedsthem.
*/
/* At this stage, the point lies on the same y value as either *edge->toporedge->bottom,sowehavetoexaminethexvaluein
* order to properly determine containment. */
/* If the y value of the point is the same as the y value of the *topoftheedge,thenthexvalueofthepointmustbegreater *tobeconsideredasinsidetheedge.Similarly,iftheyvalue *ofthepointisthesameastheyvalueofthebottomofthe *edge,thenthexvalueofthepointmustbelesstobe
* considered as inside. */
/* Compute the intersection of two edges. The result is provided as a *coordinatepairof128-bitintegers. * *Returns%CAIRO_BO_STATUS_INTERSECTIONifthereisanintersection *thatiswithinbothedges,%CAIRO_BO_STATUS_NO_INTERSECTIONifthe *intersectionofthelinesdefinedbytheedgesoccursoutsideof *oneorbothedges,and%CAIRO_BO_STATUS_PARALLELifthetwoedges *areexactlyparallel. * *Notethatwhendeterminingifacandidateintersectionis"inside" *anedge,weconsiderboththeinfinitesimalshorteningandthe *infinitesimaltiltrulesdescribedbyJohnHobby.Specifically,if *theintersectionisexactlythesameasanedgepoint,itis *effectivelyoutside(nointersectionisreturned).Also,ifthe *intersectionpointhasthesame
*/ static cairo_bool_t
_cairo_bo_edge_intersect (cairo_bo_edge_t *a,
cairo_bo_edge_t *b,
cairo_bo_point32_t *intersection)
{
cairo_bo_intersect_point_t quorem;
if (! intersect_lines (a, b, &quorem)) returnFALSE;
if (! _cairo_bo_edge_contains_intersect_point (a, &quorem)) returnFALSE;
if (! _cairo_bo_edge_contains_intersect_point (b, &quorem)) returnFALSE;
/* Now that we've correctly compared the intersection point and *determinedthatitlieswithintheedge,thenweknowthatwe *nolongerneedanymorebitsofstoragefortheintersection *thanwedoforouredgecoordinates.Wealsonolongerneedthe
* remainder from the division. */
intersection->x = quorem.x.ordinate;
intersection->y = quorem.y.ordinate;
if (MAX (left->edge.line.p1.x, left->edge.line.p2.x) <=
MIN (right->edge.line.p1.x, right->edge.line.p2.x)) return CAIRO_STATUS_SUCCESS;
if (cairo_lines_equal (&left->edge.line, &right->edge.line)) return CAIRO_STATUS_SUCCESS;
/* The names "left" and "right" here are correct descriptions of *theorderofthetwoedgeswithintheactiveedgelist.Soifa *slopecomparisonalsoputsleftlessthanright,thenweknow *thattheintersectionofthesetwosegmentshasalready
* occurred before the current sweep line position. */ if (_slope_compare (left, right) <= 0) return CAIRO_STATUS_SUCCESS;
if (! _cairo_bo_edge_intersect (left, right, &intersection)) return CAIRO_STATUS_SUCCESS;
/* The choice of y is not truly arbitrary since we must guarantee that it *isgreaterthanthestartofeitherline.
*/ if (p != 0) { /* colinear if either end-point are coincident */
p = (((p >> 1) & p) & 5) != 0;
} elseif (a->edge.line.p1.y < b->edge.line.p1.y) {
p = edge_compare_for_y_against_x (b,
a->edge.line.p1.y,
a->edge.line.p1.x) == 0;
} else {
p = edge_compare_for_y_against_x (a,
b->edge.line.p1.y,
b->edge.line.p1.x) == 0;
}
a->colinear = MARK_COLINEAR(b, p); return p;
}
/* Adds the trapezoid, if any, of the left edge to the #cairo_traps_t */ staticvoid
_cairo_bo_edge_end_trap (cairo_bo_edge_t *left,
int32_t bot,
cairo_traps_t *traps)
{
cairo_bo_trap_t *trap = &left->deferred_trap;
/* Only emit (trivial) non-degenerate trapezoids with positive height. */ if (likely (trap->top < bot)) {
_cairo_traps_add_trap (traps,
trap->top, bot,
&left->edge.line, &trap->right->edge.line);
/* Start a new trapezoid at the given top y coordinate, whose edges *are`edge'and`edge->next'.If`edge'alreadyhasatrapezoid, *theneitheraddittothetrapsin`traps',ifthetrapezoid's *rightedgediffersfrom`edge->next',ordonothingifthenew
* trapezoid would be a continuation of the existing one. */ staticinlinevoid
_cairo_bo_edge_start_or_continue_trap (cairo_bo_edge_t *left,
cairo_bo_edge_t *right, int top,
cairo_traps_t *traps)
{ if (left->deferred_trap.right == right) return;
assert (right); if (left->deferred_trap.right != NULL) { if (edges_colinear (left->deferred_trap.right, right))
{ /* continuation on right, so just swap edges */
left->deferred_trap.right = right; return;
}
#if DEBUG_PRINT_STATE
printf ("Processing active edges for %x\n", top); #endif
in_out = 0;
left = pos; while (pos != NULL) { if (pos != left && pos->deferred_trap.right) { /* XXX It shouldn't be possible to here with 2 deferred traps *oncolinearedges...Seebug-bo-rictoz.
*/ if (left->deferred_trap.right == NULL &&
edges_colinear (left, pos))
{ /* continuation on left */
left->deferred_trap = pos->deferred_trap;
pos->deferred_trap.right = NULL;
} else
{
_cairo_bo_edge_end_trap (pos, top, traps);
}
}
switch (event->type) { case CAIRO_BO_EVENT_TYPE_START:
e1 = &((cairo_bo_start_event_t *) event)->edge;
_cairo_bo_sweep_line_insert (&sweep_line, e1);
status = _cairo_bo_event_queue_insert_stop (&event_queue, e1); if (unlikely (status)) goto unwind;
/* check to see if this is a continuation of a stopped edge */ /* XXX change to an infinitesimal lengthening rule */ for (left = sweep_line.stopped; left; left = left->next) { if (e1->edge.top <= left->edge.bottom &&
edges_colinear (e1, left))
{
e1->deferred_trap = left->deferred_trap; if (left->prev != NULL)
left->prev = left->next; else
sweep_line.stopped = left->next; if (left->next != NULL)
left->next->prev = left->prev; break;
}
}
left = e1->prev;
right = e1->next;
if (left != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, e1); if (unlikely (status)) goto unwind;
}
if (right != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, e1, right); if (unlikely (status)) goto unwind;
}
break;
case CAIRO_BO_EVENT_TYPE_STOP:
e1 = ((cairo_bo_queue_event_t *) event)->e1;
_cairo_bo_event_queue_delete (&event_queue, event);
left = e1->prev;
right = e1->next;
_cairo_bo_sweep_line_delete (&sweep_line, e1);
/* first, check to see if we have a continuation via a fresh edge */ if (e1->deferred_trap.right != NULL) {
e1->next = sweep_line.stopped; if (sweep_line.stopped != NULL)
sweep_line.stopped->prev = e1;
sweep_line.stopped = e1;
e1->prev = NULL;
}
if (left != NULL && right != NULL) {
status = _cairo_bo_event_queue_insert_if_intersect_below_current_y (&event_queue, left, right); if (unlikely (status)) goto unwind;
}
/* XXX: This would be the convenient place to throw in multiple *passesoftheBentley-Ottmannalgorithm.Itwouldmerely *requirestoringtheresultsofeachpassintoatemporary
* cairo_traps_t. */
status = _cairo_bentley_ottmann_tessellate_bo_edges (event_ptrs, num_events,
fill_rule, traps,
&intersections); #if DEBUG_TRAPS
dump_traps (traps, "bo-polygon-out.txt"); #endif
for (i = 0; i < traps->num_traps; i++) {
status = _cairo_polygon_add_line (&polygon,
&traps->traps[i].left,
traps->traps[i].top,
traps->traps[i].bottom, 1); if (unlikely (status)) goto CLEANUP;
status = _cairo_polygon_add_line (&polygon,
&traps->traps[i].right,
traps->traps[i].top,
traps->traps[i].bottom,
-1); if (unlikely (status)) goto CLEANUP;
}
_cairo_traps_clear (traps);
status = _cairo_bentley_ottmann_tessellate_polygon (traps,
&polygon,
fill_rule);
#if0 static cairo_bool_t
edges_have_an_intersection_quadratic (cairo_bo_edge_t *edges, int num_edges)
{ int i, j;
cairo_bo_edge_t *a, *b;
cairo_bo_point32_t intersection;
/* We must not be given any upside-down edges. */ for (i = 0; i < num_edges; i++) {
assert (_cairo_bo_point32_compare (&edges[i].top, &edges[i].bottom) < 0);
edges[i].line.p1.x <<= CAIRO_BO_GUARD_BITS;
edges[i].line.p1.y <<= CAIRO_BO_GUARD_BITS;
edges[i].line.p2.x <<= CAIRO_BO_GUARD_BITS;
edges[i].line.p2.y <<= CAIRO_BO_GUARD_BITS;
}
for (i = 0; i < num_edges; i++) { for (j = 0; j < num_edges; j++) { if (i == j) continue;
a = &edges[i];
b = &edges[j];
if (! _cairo_bo_edge_intersect (a, b, &intersection)) continue;
printf ("Found intersection (%d,%d) between (%d,%d)-(%d,%d) and (%d,%d)-(%d,%d)\n",
intersection.x,
intersection.y,
a->line.p1.x, a->line.p1.y,
a->line.p2.x, a->line.p2.y,
b->line.p1.x, b->line.p1.y,
b->line.p2.x, b->line.p2.y);
return TRUE;
}
} returnFALSE;
}
#define TEST_MAX_EDGES 10
typedefstruct test { constchar *name; constchar *description; int num_edges;
cairo_bo_edge_t edges[TEST_MAX_EDGES];
} test_t;
static test_t
tests[] = {
{ "3 near misses", "3 edges all intersecting very close to each other", 3,
{
{ { 4, 2}, {0, 0}, { 9, 9}, NULL, NULL },
{ { 7, 2}, {0, 0}, { 2, 3}, NULL, NULL },
{ { 5, 2}, {0, 0}, { 1, 7}, NULL, NULL }
}
},
{ "inconsistent data", "Derived from random testing---was leading to skip list and edge list disagreeing.", 2,
{
{ { 2, 3}, {0, 0}, { 8, 9}, NULL, NULL },
{ { 2, 3}, {0, 0}, { 6, 7}, NULL, NULL }
}
},
{ "failed sort", "A test derived from random testing that leads to an inconsistent sort --- looks like we just can't attempt to validate the sweep line with edge_compare?", 3,
{
{ { 6, 2}, {0, 0}, { 6, 5}, NULL, NULL },
{ { 3, 5}, {0, 0}, { 5, 6}, NULL, NULL },
{ { 9, 2}, {0, 0}, { 5, 6}, NULL, NULL },
}
},
{ "minimal-intersection", "Intersection of a two from among the smallest possible edges.", 2,
{
{ { 0, 0}, {0, 0}, { 1, 1}, NULL, NULL },
{ { 1, 0}, {0, 0}, { 0, 1}, NULL, NULL }
}
},
{ "simple", "A simple intersection of two edges at an integer (2,2).", 2,
{
{ { 1, 1}, {0, 0}, { 3, 3}, NULL, NULL },
{ { 2, 1}, {0, 0}, { 2, 3}, NULL, NULL }
}
},
{ "bend-to-horizontal", "With intersection truncation one edge bends to horizontal", 2,
{
{ { 9, 1}, {0, 0}, {3, 7}, NULL, NULL },
{ { 3, 5}, {0, 0}, {9, 9}, NULL, NULL }
}
}
};
intersections = _cairo_bentley_ottmann_intersect_edges (test_edges, num_edges, &intersected_edges); if (intersections)
printf ("Pass 1 found %d intersections:\n", intersections);
/* XXX: Multi-pass Bentley-Ottmmann. Preferable would be to add a
* pass of Hobby's tolerance-square algorithm instead. */
passes = 1; while (intersections) { int num_edges = _cairo_array_num_elements (&intersected_edges);
passes++;
edges = _cairo_malloc_ab (num_edges, sizeof (cairo_bo_edge_t));
assert (edges != NULL);
memcpy (edges, _cairo_array_index (&intersected_edges, 0), num_edges * sizeof (cairo_bo_edge_t));
_cairo_array_fini (&intersected_edges);
_cairo_array_init (&intersected_edges, sizeof (cairo_bo_edge_t));
intersections = _cairo_bentley_ottmann_intersect_edges (edges, num_edges, &intersected_edges);
free (edges);
if (intersections){
printf ("Pass %d found %d remaining intersections:\n", passes, intersections);
} else { if (passes > 3) for (i = 0; i < passes; i++)
printf ("*");
printf ("No remainining intersections found after pass %d\n", passes);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.