Bezier* bezier_subrange(Bezier* b, float t1, float t2) { Bezier* left = 0; Bezier* middle_and_right = 0; Bezier* middle = 0; Bezier* right = 0; bezier_split(b, t1, &left, &middle_and_right); bezier_split(middle_and_right, t2, &middle, &right); bezier_destroy(left); bezier_destroy(middle_and_right); bezier_destroy(right); return middle; }
static bool test_bezier_split(void) { double coords[5] = { 0., 1., 0.5, 1., 0.5 }; double coordsA[5]; double coordsB[5]; bezier_split(0.5, 4, coordsA, coordsB, coords); double err = 0.; for (double x = 0.; x < 1.; x += 0.01) { double a = bezier_curve(x, 4, coords); double b = (x <= 0.5) ? bezier_curve(2. * x, 4, coordsA) : bezier_curve(2. * (x - 0.5), 4, coordsB); err += pow(a - b, 2); } return (err < 1.E-28); }
/*! * Given the original segments and splits apply * all segment splits and create unique segment index. * * Split.seg is the index to the segment to split before this function. * After the splits are applied every split.seq is unique. */ static void _split_segments (GArray *segs, GArray *splits, const GArray *other) { int i, sofs = 0; GArray *pending; /* splits must be sorted for the algorithm below */ g_array_sort (splits, _compare_split); for (i = 0; i < splits->len; ++i) { int j, to; int from = i; int from_seg = g_array_index (splits, Split, i).seg; BezierSegment bs; real t = 0; g_return_if_fail (from_seg + sofs < segs->len); bs = g_array_index (segs, BezierSegment, from_seg + sofs); while (i < splits->len - 1 && from_seg == g_array_index (splits, Split, i+1).seg) ++i; /* advance while segment reference is the same */ to = i; for (j = from; j <= to; j++) { BezierSegment left, right; /* scale t to split the right segment */ real tL = g_array_index (splits, Split, j).split; real tR = (tL - t) / (1.0 - t); bezier_split_at (&bs, &left, &right, tR); bs = right; t = tL; /* overwrite the exisiting */ g_return_if_fail (from_seg + sofs < segs->len); g_array_index (segs, BezierSegment, from_seg + sofs) = left; sofs += 1; /* increment segment offset for every segment added */ /* insert a new one behind that ... */ g_array_insert_val (segs, from_seg + sofs, right); /* ... potentially overwritten */ /* adjust the segment reference */ g_array_index (splits, Split, j).seg = from_seg + sofs; } } pending = g_array_new (FALSE, FALSE, sizeof(BezierSegment)); /* for every sub-path determine if it is inside the full other path */ for (i = 0; i < splits->len; ++i) { Split *sp = &g_array_index (splits, Split, i); BezierSegment *bs = &g_array_index (segs, BezierSegment, sp->seg); BezierSegment left, right; int to, j; if (i == 0 && sp->seg > 0) g_array_append_vals (pending, &g_array_index (segs, BezierSegment, 0), sp->seg); bezier_split (bs, &left, &right); sp->outside = distance_bez_shape_point (&g_array_index (other, BezPoint, 0), other->len, 0 /* line width */, &right.p0) > 0.0; /* also remember the sub-path */ to = g_array_index (splits, Split, (i+1)%splits->len).seg; sp->path = g_array_new (FALSE, FALSE, sizeof(BezierSegment)); if (to < sp->seg) { g_array_append_vals (sp->path, bs, segs->len - sp->seg); #if 0 /* XXX: this is only correct if there is no move-to within the segments */ g_array_append_vals (sp->path, &g_array_index (segs, BezierSegment, 0), to); #else g_array_append_vals (sp->path, &g_array_index (pending, BezierSegment, 0), pending->len); g_array_set_size (pending, 0); #endif } else { for (j = sp->seg; j < to; ++j) { if (_segment_is_moveto (bs)) { g_array_append_vals (sp->path, &g_array_index (pending, BezierSegment, 0), pending->len); g_array_set_size (pending, 0); break; } g_array_append_val (sp->path, *bs); bs++; } for (/* remains */; j < to; ++j) { g_array_append_val (pending, *bs); bs++; } } } g_array_free (pending, TRUE); }
/*! * \brief Calculate crossing points of two bezier segments * * Beware two bezier segments can intersect more than once, but this * function only returns the first or no intersection. It is the * responsibility of the caller to further split segments until there * is no intersection left. */ static gboolean bezier_bezier_intersection (GArray *crossing, const BezierSegment *a, const BezierSegment *b, int depth, real asplit, real bsplit) { Rectangle abox, bbox; PolyBBExtras extra = { 0, }; gboolean small_a, small_b; /* Avoid intersection overflow: if start and end are on the other segment * assume full overlap and no crossing. */ if ( (_segment_has_point (a, &b->p0) && _segment_has_point (a, &b->p3)) || (_segment_has_point (b, &a->p0) && _segment_has_point (b, &a->p3))) return FALSE; /* XXX: more variants pending, partial overlap */ /* With very similar segments we would create a lot of points with not * a very deep recursion (test with ying-yang symbol). * Just comparing the segments on depth=1 is not good enough, so for * now we are limiting the number of intersections */ if (crossing->len > 127) { /* XXX: arbitrary limit */ g_warning ("Crossing limit (%d) reached", crossing->len); return FALSE; } bicubicbezier2D_bbox (&a->p0, &a->p1, &a->p2, &a->p3, &extra, &abox); bicubicbezier2D_bbox (&b->p0, &b->p1, &b->p2, &b->p3, &extra, &bbox); if (!rectangle_intersects (&abox, &bbox)) return FALSE; small_a = (abox.right - abox.left) < EPSILON && (abox.bottom - abox.top) < EPSILON; small_b = (bbox.right - bbox.left) < EPSILON && (bbox.bottom - bbox.top) < EPSILON; /* if the boxes are small enough we can calculate the point */ if (small_a && small_b) { /* intersecting and both small, should not matter which one is used */ Point pt = { (abox.right + abox.left + bbox.right + bbox.left) / 4, (abox.bottom + abox.top + bbox.bottom + bbox.top) / 4 }; Intersection is; int i; for (i = 0; i < crossing->len; ++i) { /* if it's already included we are done */ if (distance_point_point (&g_array_index (crossing, Intersection, i).pt, &pt) < 1.4142*EPSILON) return TRUE; /* although we did not add it */ } is.split_one = asplit; is.split_two = bsplit; is.pt = pt; g_print ("d=%d; as=%g; bs=%g; ", depth, asplit, bsplit); g_array_append_val (crossing, is); return TRUE; } else { /* further splitting of a and b; it could be smart to only search in the * intersection of a-box and b-box ... */ BezierSegment a1, a2; BezierSegment b1, b2; real ofs = 1.0/(1<<(depth+1)); gboolean ret = FALSE; bezier_split (a, &a1, &a2); bezier_split (b, &b1, &b2); ret |= bezier_bezier_intersection (crossing, &a1, &b1, depth+1, asplit-ofs, bsplit-ofs); ret |= bezier_bezier_intersection (crossing, &a2, &b1, depth+1, asplit+ofs, bsplit-ofs); ret |= bezier_bezier_intersection (crossing, &a1, &b2, depth+1, asplit-ofs, bsplit+ofs); ret |= bezier_bezier_intersection (crossing, &a2, &b2, depth+1, asplit+ofs, bsplit+ofs); /* XXX: check !ret case, not sure if it should happen */ return ret; } }