cmp = a->y.ordinate - b->y.ordinate; if (cmp) return cmp;
cmp = a->y.approx - b->y.approx; if (cmp) return cmp;
return a->x.ordinate - b->x.ordinate;
}
/* Compare the slope of a to the slope of b, returning 1, 0, -1 if the * slope a is respectively greater than, equal to, or less than the * slope of b. * * For each edge, consider the direction vector formed from: * * top -> bottom * * which is: * * (dx, dy) = (line.p2.x - line.p1.x, line.p2.y - line.p1.y) * * We then define the slope of each edge as dx/dy, (which is the * inverse of the slope typically used in math instruction). We never * compute a slope directly as the value approaches infinity, but we * can derive a slope comparison without division as follows, (where * the ? represents our compare operator). * * 1. slope(a) ? slope(b) * 2. adx/ady ? bdx/bdy * 3. (adx * bdy) ? (bdx * ady) * * Note that from step 2 to step 3 there is no change needed in the * sign of the result since both ady and bdy are guaranteed to be * greater than or equal to 0. * * When using this slope comparison to sort edges, some care is needed * when interpreting the results. Since the slope compare operates on * distance vectors from top to bottom it gives a correct left to * right sort for edges that have a common top point, (such as two * edges with start events at the same location). On the other hand, * the sense of the result will be exactly reversed for two edges that * have a common stop point.
*/ staticinlineint
_slope_compare (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b)
{ /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins.
*/
int32_t adx = a->edge.line.p2.x - a->edge.line.p1.x;
int32_t bdx = b->edge.line.p2.x - b->edge.line.p1.x;
/* Since the dy's are all positive by construction we can fast * path several common cases.
*/
/* First check for vertical lines. */ if (adx == 0) return -bdx; if (bdx == 0) return adx;
/* Then where the two edges point in different directions wrt x. */ if ((adx ^ bdx) < 0) return adx;
/* Finally we actually need to do the general comparison. */
{
int32_t ady = a->edge.line.p2.y - a->edge.line.p1.y;
int32_t bdy = b->edge.line.p2.y - b->edge.line.p1.y;
cairo_int64_t adx_bdy = _cairo_int32x32_64_mul (adx, bdy);
cairo_int64_t bdx_ady = _cairo_int32x32_64_mul (bdx, ady);
return _cairo_int64_cmp (adx_bdy, bdx_ady);
}
}
/* * We need to compare the x-coordinates of a pair of lines for a particular y, * without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ B_x + (Y - B_y) * B_dx / B_dy, * where ∘ is our inequality operator. * * By construction, we know that A_dy and B_dy (and (Y - A_y), (Y - B_y)) are * all positive, so we can rearrange it thus without causing a sign change: * A_dy * B_dy * (A_x - B_x) ∘ (Y - B_y) * B_dx * A_dy * - (Y - A_y) * A_dx * B_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 128 bit arithmetic. For certain, but common, * input we can reduce this down to a single 32 bit compare by inspecting the * deltas. * * (And put the burden of the work on developing fast 128 bit ops, which are * required throughout the tessellator.) * * See the similar discussion for _slope_compare().
*/ staticint
edges_compare_x_for_y_general (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b,
int32_t y)
{ /* XXX: We're assuming here that dx and dy will still fit in 32 * bits. That's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm * begins.
*/
int32_t dx;
int32_t adx, ady;
int32_t bdx, bdy; enum {
HAVE_NONE = 0x0,
HAVE_DX = 0x1,
HAVE_ADX = 0x2,
HAVE_DX_ADX = HAVE_DX | HAVE_ADX,
HAVE_BDX = 0x4,
HAVE_DX_BDX = HAVE_DX | HAVE_BDX,
HAVE_ADX_BDX = HAVE_ADX | HAVE_BDX,
HAVE_ALL = HAVE_DX | HAVE_ADX | HAVE_BDX
} have_dx_adx_bdx = HAVE_ALL;
/* don't bother solving for abscissa if the edges' bounding boxes
* can be used to order them. */
{
int32_t amin, amax;
int32_t bmin, bmax; if (a->edge.line.p1.x < a->edge.line.p2.x) {
amin = a->edge.line.p1.x;
amax = a->edge.line.p2.x;
} else {
amin = a->edge.line.p2.x;
amax = a->edge.line.p1.x;
} if (b->edge.line.p1.x < b->edge.line.p2.x) {
bmin = b->edge.line.p1.x;
bmax = b->edge.line.p2.x;
} else {
bmin = b->edge.line.p2.x;
bmax = b->edge.line.p1.x;
} if (amax < bmin) return -1; if (amin > bmax) return +1;
}
ady = a->edge.line.p2.y - a->edge.line.p1.y;
adx = a->edge.line.p2.x - a->edge.line.p1.x; if (adx == 0)
have_dx_adx_bdx &= ~HAVE_ADX;
return _cairo_int64_cmp (bdy_dx, dy_bdx);
} case HAVE_ALL: /* XXX try comparing (a->edge.line.p2.x - b->edge.line.p2.x) et al */ return _cairo_int128_cmp (L, _cairo_int128_sub (B, A));
} #undef B #undef A #undef L
}
/* * We need to compare the x-coordinate of a line for a particular y wrt to a * given x, without loss of precision. * * The x-coordinate along an edge for a given y is: * X = A_x + (Y - A_y) * A_dx / A_dy * * So the inequality we wish to test is: * A_x + (Y - A_y) * A_dx / A_dy ∘ X * where ∘ is our inequality operator. * * By construction, we know that A_dy (and (Y - A_y)) are * all positive, so we can rearrange it thus without causing a sign change: * (Y - A_y) * A_dx ∘ (X - A_x) * A_dy * * Given the assumption that all the deltas fit within 32 bits, we can compute * this comparison directly using 64 bit arithmetic. * * See the similar discussion for _slope_compare() and * edges_compare_x_for_y_general().
*/ staticint
edge_compare_for_y_against_x (const cairo_bo_edge_t *a,
int32_t y,
int32_t x)
{
int32_t adx, ady;
int32_t dx, dy;
cairo_int64_t L, R;
if (x < a->edge.line.p1.x && x < a->edge.line.p2.x) return 1; if (x > a->edge.line.p1.x && x > a->edge.line.p2.x) return -1;
adx = a->edge.line.p2.x - a->edge.line.p1.x;
dx = x - a->edge.line.p1.x;
if (adx == 0) return -dx; if (dx == 0 || (adx ^ dx) < 0) return adx;
dy = y - a->edge.line.p1.y;
ady = a->edge.line.p2.y - a->edge.line.p1.y;
L = _cairo_int32x32_64_mul (dy, adx);
R = _cairo_int32x32_64_mul (dx, ady);
return _cairo_int64_cmp (L, R);
}
staticint
edges_compare_x_for_y (const cairo_bo_edge_t *a, const cairo_bo_edge_t *b,
int32_t y)
{ /* If the sweep-line is currently on an end-point of a line, * then we know its precise x value (and considering that we often need to * compare events at end-points, this happens frequently enough to warrant * special casing).
*/ enum {
HAVE_NEITHER = 0x0,
HAVE_AX = 0x1,
HAVE_BX = 0x2,
HAVE_BOTH = HAVE_AX | HAVE_BX
} have_ax_bx = HAVE_BOTH;
int32_t ax = 0, bx = 0;
/* compare the edges if not identical */ if (! _line_equal (&a->edge.line, &b->edge.line)) {
cmp = edges_compare_x_for_y (a, b, sweep_line->current_y); if (cmp) return cmp;
/* The two edges intersect exactly at y, so fall back on slope * comparison. We know that this compare_edges function will be * called only when starting a new edge, (not when stopping an * edge), so we don't have to worry about conditionally inverting
* the sense of _slope_compare. */
cmp = _slope_compare (a, b); if (cmp) return cmp;
}
/* Compute the intersection of two lines as defined by two edges. The * result is provided as a coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection or * %CAIRO_BO_STATUS_PARALLEL if the two lines are exactly parallel.
*/ 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's not true in general as there could be overflow. We * should prevent that before the tessellation algorithm begins. * What we're doing to mitigate this is to perform clamping in * 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;
/* Q: Can we determine that the lines do not intersect (within range) * much more cheaply than computing the intersection point i.e. by * avoiding the division? * * X = ax + t * adx = bx + s * bdx; * Y = ay + t * ady = by + s * bdy; * ∴ t * (ady*bdx - bdy*adx) = bdx * (by - ay) + bdy * (ax - bx) * => t * L = R * * Therefore we can reject any intersection (under the criteria for * valid intersection events) if: * L^R < 0 => t < 0, or * L<R => t > 1 * * (where top/bottom must at least extend to the line endpoints). * * A similar substitution can be performed for s, yielding: * s * (ady*bdx - bdy*adx) = ady * (ax - bx) - adx * (ay - by)
*/
R = det32_64 (dx2, dy2,
b->edge.line.p1.x - a->edge.line.p1.x,
b->edge.line.p1.y - a->edge.line.p1.y); if (_cairo_int64_le (den_det, R)) returnFALSE;
R = det32_64 (dy1, dx1,
a->edge.line.p1.y - b->edge.line.p1.y,
a->edge.line.p1.x - b->edge.line.p1.x); if (_cairo_int64_le (den_det, R)) returnFALSE;
/* We now know that the two lines should intersect within range. */
/* x = det (a_det, dx1, b_det, dx2) / den_det */
qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dx1,
b_det, dx2),
den_det); if (_cairo_int64_eq (qr.rem, den_det)) returnFALSE;
intersection->x = round_to_nearest (qr, den_det);
/* y = det (a_det, dy1, b_det, dy2) / den_det */
qr = _cairo_int_96by64_32x64_divrem (det64x32_128 (a_det, dy1,
b_det, dy2),
den_det); if (_cairo_int64_eq (qr.rem, den_det)) returnFALSE;
intersection->y = round_to_nearest (qr, den_det);
returnTRUE;
}
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;
/* Does the given edge contain the given point. The point must already * be known to be contained within the line determined by the edge, * (most likely the point results from an intersection of this edge * with another). * * If we had exact arithmetic, then this function would simply be a * matter of examining whether the y value of the point lies within * the range of y values of the edge. But since intersection points * are not exact due to being rounded to the nearest integer within * the available precision, we must also examine the x value of the * point. * * The definition of "contains" here is that the given intersection * point will be seen by the sweep line after the start event for the * given edge and before the stop event for the edge. See the comments * in the implementation for more details.
*/ static cairo_bool_t
_cairo_bo_edge_contains_intersect_point (cairo_bo_edge_t *edge,
cairo_bo_intersect_point_t *point)
{ return _cairo_bo_intersect_ordinate_32_compare (point->y,
edge->edge.bottom) < 0;
}
/* Compute the intersection of two edges. The result is provided as a * coordinate pair of 128-bit integers. * * Returns %CAIRO_BO_STATUS_INTERSECTION if there is an intersection * that is within both edges, %CAIRO_BO_STATUS_NO_INTERSECTION if the * intersection of the lines defined by the edges occurs outside of * one or both edges, and %CAIRO_BO_STATUS_PARALLEL if the two edges * are exactly parallel. * * Note that when determining if a candidate intersection is "inside" * an edge, we consider both the infinitesimal shortening and the * infinitesimal tilt rules described by John Hobby. Specifically, if * the intersection is exactly the same as an edge point, it is * effectively outside (no intersection is returned). Also, if the * intersection point has the same
*/ static cairo_bool_t
_cairo_bo_edge_intersect (cairo_bo_edge_t *a,
cairo_bo_edge_t *b,
cairo_bo_intersect_point_t *intersection)
{ if (! intersect_lines (a, b, intersection)) returnFALSE;
if (! _cairo_bo_edge_contains_intersect_point (a, intersection)) returnFALSE;
if (! _cairo_bo_edge_contains_intersect_point (b, intersection)) returnFALSE;
if (_line_equal (&left->edge.line, &right->edge.line)) return CAIRO_STATUS_SUCCESS;
/* The names "left" and "right" here are correct descriptions of * the order of the two edges within the active edge list. So if a * slope comparison also puts left less than right, then we know * that the intersection of these two segments has already
* 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 * is greater than the start of either line.
*/ if (a->edge.line.p1.y == b->edge.line.p1.y) { return a->edge.line.p1.x == b->edge.line.p1.x;
} elseif (a->edge.line.p1.y < b->edge.line.p1.y) { return edge_compare_for_y_against_x (b,
a->edge.line.p1.y,
a->edge.line.p1.x) == 0;
} else { return edge_compare_for_y_against_x (a,
b->edge.line.p1.y,
b->edge.line.p1.x) == 0;
}
}
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.