Пример #1
0
/** 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);
      }
    }
  }
}
Пример #2
0
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);
            }
        }
    }
}
Пример #3
0
/*!
 * \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;
  }
}