void bicubicbezier2D_bbox(const Point *p0,const Point *p1, const Point *p2,const Point *p3, const PolyBBExtras *extra, Rectangle *rect) { double x[4],y[4]; Point vl,vt,p,tt; double *xy; int i,extr; double u[2]; rect->setLeft(p0->x()); rect->setRight(p0->x()); rect->setTop(p0->y()); rect->setBottom(p0->y()); rectangle_add_point(rect,p3); point_copy_add_scaled(&vl,p0,p1,-1); point_normalize(&vl); add_arrow_rectangle(rect,p0,&vl,extra->startLong, qMax(extra->startTrans, extra->middleTrans)); point_copy_add_scaled(&vl,p3,p2,-1); point_normalize(&vl); add_arrow_rectangle(rect,p3,&vl,extra->endLong, qMax(extra->endTrans, extra->middleTrans)); x[0] = p0->x(); x[1] = p1->x(); x[2] = p2->x(); x[3] = p3->x(); y[0] = p0->y(); y[1] = p1->y(); y[2] = p2->y(); y[3] = p3->y(); for (xy = x; xy ; xy=(xy==x?y:NULL) ) { extr = bicubicbezier_extrema(xy,u); for (i=0;i<extr;i++) { if ((u[i]<0) || (u[i]>1)) continue; p.setX(bezier_eval(x,u[i])); vl.setX(bezier_eval_tangent(x,u[i])); p.setY(bezier_eval(y,u[i])); vl.setY(bezier_eval_tangent(y,u[i])); point_normalize(&vl); point_get_perp(&vt,&vl); point_copy_add_scaled(&tt,&p,&vt,extra->middleTrans); rectangle_add_point(rect,&tt); point_copy_add_scaled(&tt,&p,&vt,-extra->middleTrans); rectangle_add_point(rect,&tt); } } }
/** Calculate the boundingbox for a 2D bezier curve segment. * @param p0 start point * @param p1 1st control point * @param p2 2nd control point * @param p3 end point * @param extra information about extra space from linewidth and arrow to add to the bounding box * @param rect The rectangle that the segment fits inside. */ void bicubicbezier2D_bbox(const Point *p0,const Point *p1, const Point *p2,const Point *p3, const PolyBBExtras *extra, Rectangle *rect) { real x[4],y[4]; Point vl,vt,p,tt; real *xy; int i,extr; real u[2]; rect->left = rect->right = p0->x; rect->top = rect->bottom = p0->y; rectangle_add_point(rect,p3); /* start point */ point_copy_add_scaled(&vl,p0,p1,-1); point_normalize(&vl); add_arrow_rectangle(rect,p0,&vl,extra->start_long,MAX(extra->start_trans, extra->middle_trans)); /* end point */ point_copy_add_scaled(&vl,p3,p2,-1); point_normalize(&vl); add_arrow_rectangle(rect,p3,&vl,extra->end_long,MAX(extra->end_trans, extra->middle_trans)); /* middle part */ x[0] = p0->x; x[1] = p1->x; x[2] = p2->x; x[3] = p3->x; y[0] = p0->y; y[1] = p1->y; y[2] = p2->y; y[3] = p3->y; for (xy = x; xy ; xy=(xy==x?y:NULL) ) { /* sorry */ extr = bicubicbezier_extrema(xy,u); for (i=0;i<extr;i++) { if ((u[i]<0) || (u[i]>1)) continue; p.x = bezier_eval(x,u[i]); vl.x = bezier_eval_tangent(x,u[i]); p.y = bezier_eval(y,u[i]); vl.y = bezier_eval_tangent(y,u[i]); point_normalize(&vl); point_get_perp(&vt,&vl); point_copy_add_scaled(&tt,&p,&vt,extra->middle_trans); rectangle_add_point(rect,&tt); point_copy_add_scaled(&tt,&p,&vt,-extra->middle_trans); rectangle_add_point(rect,&tt); } } }
/** Calculate the bounding box for a simple line. * @param p1 One end of the line. * @param p2 The other end of the line. * @param extra Extra information * @param rect The box that the line and extra stuff fits inside. */ void line_bbox(const Point *p1, const Point *p2, const LineBBExtras *extra, Rectangle *rect) { Point vl; rect->left = rect->right = p1->x; rect->top = rect->bottom = p1->y; rectangle_add_point(rect,p2); /* as a safety, so we don't need to care if it above or below p1 */ point_copy_add_scaled(&vl,p1,p2,-1); point_normalize(&vl); add_arrow_rectangle(rect,p1,&vl,extra->start_long,extra->start_trans); point_scale(&vl,-1); add_arrow_rectangle(rect,p2,&vl,extra->end_long,extra->end_trans); }
void line_bbox(const Point *p1, const Point *p2, const LineBBExtras *extra, Rectangle *rect) { Point vl; rect->setLeft(p1->x()); rect->setRight(p1->x()); rect->setTop(p1->y()); rect->setBottom(p1->y()); rectangle_add_point(rect,p2); point_copy_add_scaled(&vl,p1,p2,-1); point_normalize(&vl); add_arrow_rectangle(rect,p1,&vl,extra->startLong,extra->startTrans); point_scale(&vl,-1); add_arrow_rectangle(rect,p2,&vl,extra->endLong,extra->endTrans); }
void add_arrow_rectangle(Rectangle *rect, const Point *vertex, const Point *normed_dir, double extra_long,double extra_trans) { Point vl,vt,pt; vl = *normed_dir; point_get_perp(&vt,&vl); point_copy_add_scaled(&pt,vertex,&vl,extra_long); point_add_scaled(&pt,&vt,extra_trans); rectangle_add_point(rect,&pt); point_add_scaled(&pt,&vt,-2.0 * extra_trans); rectangle_add_point(rect,&pt); point_add_scaled(&pt,&vl,-2.0 * extra_long); rectangle_add_point(rect,&pt); point_add_scaled(&pt,&vt,2.0 * extra_trans); rectangle_add_point(rect,&pt); }
void polybezier_bbox(const BezPoint *pts, int numpoints, const PolyBBExtras *extra, bool closed, Rectangle *rect) { Point vx,vp,vn,vsc; int i,prev,next; Rectangle rt; PolyBBExtras bextra,start_bextra,end_bextra; LineBBExtras lextra,start_lextra,end_lextra,full_lextra; bool start,end; rect->setLeft(pts[0].p1.x()); rect->setRight(pts[0].p1.x()); rect->setTop(pts[0].p1.y()); rect->setBottom(pts[0].p1.y()); if (!closed) { start_lextra.startLong = extra->startLong; start_lextra.startTrans = qMax(extra->startTrans,extra->middleTrans); start_lextra.endLong = 0; start_lextra.endTrans = extra->middleTrans; end_lextra.startLong = 0; end_lextra.startTrans = extra->middleTrans; end_lextra.endLong = extra->endLong; end_lextra.endTrans = qMax(extra->endTrans,extra->middleTrans); } full_lextra.startLong = extra->startLong; full_lextra.startTrans = qMax(extra->startTrans,extra->middleTrans); full_lextra.endLong = extra->endLong; full_lextra.endTrans = qMax(extra->endTrans,extra->middleTrans); if (!closed) { lextra.startLong = 0; lextra.startTrans = extra->middleTrans; lextra.endLong = 0; lextra.endTrans = extra->middleTrans; start_bextra.startLong = extra->startLong; start_bextra.startTrans = extra->startTrans; start_bextra.middleTrans = extra->middleTrans; start_bextra.endLong = 0; start_bextra.endTrans = extra->middleTrans; end_bextra.startLong = 0; end_bextra.startTrans = extra->middleTrans; end_bextra.middleTrans = extra->middleTrans; end_bextra.endLong = extra->endLong; end_bextra.endTrans = extra->endTrans; } bextra.startLong = 0; bextra.startTrans = extra->middleTrans; bextra.middleTrans = extra->middleTrans; bextra.endLong = 0; bextra.endTrans = extra->middleTrans; for (i=1;i<numpoints;i++) { next = (i+1) % numpoints; prev = (i-1) % numpoints; if (closed && (next == 0)) next=1; if (closed && (prev == 0)) prev=numpoints-1; if (pts[i].type == BezPoint::BEZ_MOVE_TO) { continue; } switch(pts[i].type) { case BezPoint::BEZ_MOVE_TO: break; case BezPoint::BEZ_LINE_TO: vx = pts[i].p1; switch(pts[prev].type) { case BezPoint::BEZ_MOVE_TO: case BezPoint::BEZ_LINE_TO: vsc = pts[prev].p1; vp = pts[prev].p1; break; case BezPoint::BEZ_CURVE_TO: vsc = pts[prev].p3; vp = pts[prev].p3; break; } break; case BezPoint::BEZ_CURVE_TO: vx = pts[i].p3; vp = pts[i].p2; switch(pts[prev].type) { case BezPoint::BEZ_MOVE_TO: case BezPoint::BEZ_LINE_TO: vsc = pts[prev].p1; break; case BezPoint::BEZ_CURVE_TO: vsc = pts[prev].p3; break; } break; } start = (pts[prev].type == BezPoint::BEZ_MOVE_TO); end = (pts[next].type == BezPoint::BEZ_MOVE_TO); vn = pts[next].p1; if (closed) { if (pts[i].type == BezPoint::BEZ_LINE_TO) { line_bbox(&vsc,&vx,&full_lextra,&rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &bextra, &rt); } } else if (start) { if (pts[i].type == BezPoint::BEZ_LINE_TO) { if (end) { line_bbox(&vsc,&vx,&full_lextra,&rt); } else { line_bbox(&vsc,&vx,&start_lextra,&rt); } } else { if (end) { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, extra, &rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &start_bextra, &rt); } } } else if (end) { if (pts[i].type == BezPoint::BEZ_LINE_TO) { line_bbox(&vsc,&vx,&end_lextra,&rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &end_bextra, &rt); } } else { if (pts[i].type == BezPoint::BEZ_LINE_TO) { line_bbox(&vsc,&vx,&lextra,&rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &bextra, &rt); } } *rect = rect->united(rt); if ((!start) && (!end)) { Point vpx,vxn; double co,alpha; point_copy_add_scaled(&vpx,&vx,&vp,-1); point_normalize(&vpx); point_copy_add_scaled(&vxn,&vn,&vx,-1); point_normalize(&vxn); co = point_dot(&vpx,&vxn); alpha = acos(-co); if ((co > -0.9816) && (finite(alpha))) { double overshoot = extra->middleTrans / sin(alpha/2.0); Point vovs,pto; point_copy_add_scaled(&vovs,&vpx,&vxn,-1); point_normalize(&vovs); point_copy_add_scaled(&pto,&vx,&vovs,overshoot); rectangle_add_point(rect,&pto); } else { Point vpxt,vxnt,tmp; point_get_perp(&vpxt,&vpx); point_get_perp(&vxnt,&vxn); point_copy_add_scaled(&tmp,&vx,&vpxt,1); rectangle_add_point(rect,&tmp); point_copy_add_scaled(&tmp,&vx,&vpxt,-1); rectangle_add_point(rect,&tmp); point_copy_add_scaled(&tmp,&vx,&vxnt,1); rectangle_add_point(rect,&tmp); point_copy_add_scaled(&tmp,&vx,&vxnt,-1); rectangle_add_point(rect,&tmp); } } } }
/** Calculate a bounding box for a set of bezier points. * @param pts The bezier points * @param numpoints The number of elements in `pts' * @param extra Extra spacing information. * @param closed True if the bezier points form a closed line. * @param rect Return value: The enclosing rectangle will be stored here. * @bug This function is way too long (214 lines) and should be split. */ void polybezier_bbox(const BezPoint *pts, int numpoints, const PolyBBExtras *extra, gboolean closed, Rectangle *rect) { Point vx,vn,vsc,vp; int i,prev,next; Rectangle rt; PolyBBExtras bextra,start_bextra,end_bextra; LineBBExtras lextra,start_lextra,end_lextra,full_lextra; gboolean start,end; vp.x=0; vp.y=0; g_assert(pts[0].type == BEZ_MOVE_TO); rect->left = rect->right = pts[0].p1.x; rect->top = rect->bottom = pts[0].p1.y; /* First, we build derived BBExtras structures, so we have something to feed the primitives. */ if (!closed) { start_lextra.start_long = extra->start_long; start_lextra.start_trans = MAX(extra->start_trans,extra->middle_trans); start_lextra.end_long = 0; start_lextra.end_trans = extra->middle_trans; end_lextra.start_long = 0; end_lextra.start_trans = extra->middle_trans; end_lextra.end_long = extra->end_long; end_lextra.end_trans = MAX(extra->end_trans,extra->middle_trans); } full_lextra.start_long = extra->start_long; full_lextra.start_trans = MAX(extra->start_trans,extra->middle_trans); full_lextra.end_long = extra->end_long; full_lextra.end_trans = MAX(extra->end_trans,extra->middle_trans); if (!closed) { lextra.start_long = 0; lextra.start_trans = extra->middle_trans; lextra.end_long = 0; lextra.end_trans = extra->middle_trans; start_bextra.start_long = extra->start_long; start_bextra.start_trans = extra->start_trans; start_bextra.middle_trans = extra->middle_trans; start_bextra.end_long = 0; start_bextra.end_trans = extra->middle_trans; end_bextra.start_long = 0; end_bextra.start_trans = extra->middle_trans; end_bextra.middle_trans = extra->middle_trans; end_bextra.end_long = extra->end_long; end_bextra.end_trans = extra->end_trans; } bextra.start_long = 0; bextra.start_trans = extra->middle_trans; bextra.middle_trans = extra->middle_trans; bextra.end_long = 0; bextra.end_trans = extra->middle_trans; for (i=1;i<numpoints;i++) { next = (i+1) % numpoints; prev = (i-1) % numpoints; if (closed && (next == 0)) next=1; if (closed && (prev == 0)) prev=numpoints-1; /* We have now: i = index of current vertex. prev,next: index of previous/next vertices (of the control polygon) We want: vp, vx, vn: the previous, current and next vertices; start, end: TRUE if we're at an end of poly (then, vp and/or vn are not valid, respectively). Some values *will* be recomputed a few times across iterations (but stored in different boxes). Either gprof says it's a real problem, or gcc finally gets a clue. */ if (pts[i].type == BEZ_MOVE_TO) { continue; } switch(pts[i].type) { case BEZ_MOVE_TO: g_assert_not_reached(); break; case BEZ_LINE_TO: point_copy(&vx,&pts[i].p1); switch(pts[prev].type) { case BEZ_MOVE_TO: case BEZ_LINE_TO: point_copy(&vsc,&pts[prev].p1); point_copy(&vp,&pts[prev].p1); break; case BEZ_CURVE_TO: point_copy(&vsc,&pts[prev].p3); point_copy(&vp,&pts[prev].p3); break; } break; case BEZ_CURVE_TO: point_copy(&vx,&pts[i].p3); point_copy(&vp,&pts[i].p2); switch(pts[prev].type) { case BEZ_MOVE_TO: case BEZ_LINE_TO: point_copy(&vsc,&pts[prev].p1); break; case BEZ_CURVE_TO: point_copy(&vsc,&pts[prev].p3); break; } /* vsc is the start of the curve. */ break; } start = (pts[prev].type == BEZ_MOVE_TO); end = (pts[next].type == BEZ_MOVE_TO); point_copy(&vn,&pts[next].p1); /* whichever type pts[next] is. */ /* Now, we know about a few vertices around the one we're dealing with. Depending on the shape of the (previous,current) segment, and whether it's a middle or end segment, we'll be doing different stuff. */ if (closed) { if (pts[i].type == BEZ_LINE_TO) { line_bbox(&vsc,&vx,&full_lextra,&rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &bextra, &rt); } } else if (start) { if (pts[i].type == BEZ_LINE_TO) { if (end) { line_bbox(&vsc,&vx,&full_lextra,&rt); } else { line_bbox(&vsc,&vx,&start_lextra,&rt); } } else { /* BEZ_MOVE_TO */ if (end) { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, extra, &rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &start_bextra, &rt); } } } else if (end) { /* end but not start. Not closed anyway. */ if (pts[i].type == BEZ_LINE_TO) { line_bbox(&vsc,&vx,&end_lextra,&rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &end_bextra, &rt); } } else { /* normal case : middle segment (not closed shape). */ if (pts[i].type == BEZ_LINE_TO) { line_bbox(&vsc,&vx,&lextra,&rt); } else { bicubicbezier2D_bbox(&vsc, &pts[i].p1,&pts[i].p2,&pts[i].p3, &bextra, &rt); } } rectangle_union(rect,&rt); /* The following code enlarges a little the bounding box (if necessary) to account with the "pointy corners" X (and PS) add when LINEJOIN_MITER mode is in force. */ if ((!start) && (!end)) { /* We have a non-extremity vertex. */ Point vpx,vxn; real co,alpha; point_copy_add_scaled(&vpx,&vx,&vp,-1); point_normalize(&vpx); point_copy_add_scaled(&vxn,&vn,&vx,-1); point_normalize(&vxn); co = point_dot(&vpx,&vxn); alpha = acos(-co); if (co > -0.9816) { /* 0.9816 = cos(11deg) */ /* we have a pointy join. */ real overshoot; Point vovs,pto; if (finite(alpha)) overshoot = extra->middle_trans / sin(alpha/2.0); else /* prependicular? */ overshoot = extra->middle_trans; point_copy_add_scaled(&vovs,&vpx,&vxn,-1); point_normalize(&vovs); point_copy_add_scaled(&pto,&vx,&vovs,overshoot); rectangle_add_point(rect,&pto); } else { /* we don't have a pointy join. */ Point vpxt,vxnt,tmp; point_get_perp(&vpxt,&vpx); point_get_perp(&vxnt,&vxn); point_copy_add_scaled(&tmp,&vx,&vpxt,1); rectangle_add_point(rect,&tmp); point_copy_add_scaled(&tmp,&vx,&vpxt,-1); rectangle_add_point(rect,&tmp); point_copy_add_scaled(&tmp,&vx,&vxnt,1); rectangle_add_point(rect,&tmp); point_copy_add_scaled(&tmp,&vx,&vxnt,-1); rectangle_add_point(rect,&tmp); } } } }