aboutsummaryrefslogtreecommitdiff
path: root/generic/Geo.c
diff options
context:
space:
mode:
Diffstat (limited to 'generic/Geo.c')
-rw-r--r--generic/Geo.c3276
1 files changed, 0 insertions, 3276 deletions
diff --git a/generic/Geo.c b/generic/Geo.c
deleted file mode 100644
index 1fde494..0000000
--- a/generic/Geo.c
+++ /dev/null
@@ -1,3276 +0,0 @@
-/*
- * Geo.c -- Implementation of common geometric routines.
- *
- * Authors : Patrick Lecoanet.
- * Creation date :
- *
- * $Id$
- */
-
-/*
- * Copyright (c) 1993 - 2005 CENA, Patrick Lecoanet --
- *
- * See the file "Copyright" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- */
-
-/*
- * Much of the code here is inspired by (or copied from) the Tk code.
- */
-
-
-#include "Geo.h"
-#include "WidgetInfo.h"
-
-#include <memory.h>
-
-
-static const char rcsid[] = "$Id$";
-static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $";
-
-
-void
-ZnPolyInit(ZnPoly *poly)
-{
- poly->num_contours = 0;
- poly->contours = NULL;
-}
-
-void
-ZnPolyContour1(ZnPoly *poly,
- ZnPoint *pts,
- unsigned int num_pts,
- ZnBool cw)
-{
- poly->num_contours = 1;
- poly->contours = &poly->contour1;
- poly->contour1.num_points = num_pts;
- poly->contour1.points = pts;
- poly->contour1.cw = cw;
- poly->contour1.controls = NULL;
-}
-
-void
-ZnPolySet(ZnPoly *poly1,
- ZnPoly *poly2)
-{
- ZnPolyFree(poly1);
- if (poly2->num_contours == 1) {
- ZnPolyContour1(poly1, poly2->contours[0].points, poly2->contours[0].num_points,
- poly2->contours[0].cw);
- if (poly2->contours != &poly2->contour1) {
- ZnFree(poly2->contours);
- }
- }
- else {
- poly1->num_contours = poly2->num_contours;
- poly1->contours = poly2->contours;
- }
-}
-
-void
-ZnPolyFree(ZnPoly *poly)
-{
- if (poly->num_contours) {
- unsigned int i;
- for (i = 0; i < poly->num_contours; i++) {
- ZnFree(poly->contours[i].points);
-/* if (poly->contours[i].controls) {
- ZnFree(poly->contours[i].controls);
- }*/
- }
- if (poly->contours != &poly->contour1) {
- ZnFree(poly->contours);
- }
- poly->num_contours = 0;
- poly->contours = NULL;
- }
-}
-
-void
-ZnTriStrip1(ZnTriStrip *tristrip,
- ZnPoint *pts,
- unsigned int num_pts,
- ZnBool fan)
-{
- tristrip->num_strips = 1;
- tristrip->strips = &tristrip->strip1;
- tristrip->strip1.points = pts;
- tristrip->strip1.num_points = num_pts;
- tristrip->strip1.fan = fan;
-}
-
-void
-ZnTriFree(ZnTriStrip *tristrip)
-{
- if (tristrip->num_strips) {
- unsigned int i;
- for (i = 0; i < tristrip->num_strips; i++) {
- ZnFree(tristrip->strips[i].points);
- }
- if (tristrip->strips != &tristrip->strip1) {
- ZnFree(tristrip->strips);
- }
- tristrip->num_strips = 0;
- tristrip->strips = NULL;
- }
-}
-
-/*
- * Compute the origin of the rectangle given
- * by position, anchor, width and height.
- */
-void
-ZnAnchor2Origin(ZnPoint *position,
- ZnDim width,
- ZnDim height,
- Tk_Anchor anchor,
- ZnPoint *origin)
-{
- switch (anchor) {
- case TK_ANCHOR_CENTER:
- origin->x = position->x - width/2;
- origin->y = position->y - height/2;
- break;
- case TK_ANCHOR_NW:
- *origin = *position;
- break;
- case TK_ANCHOR_N:
- origin->x = position->x - width/2;
- origin->y = position->y;
- break;
- case TK_ANCHOR_NE:
- origin->x = position->x - width;
- origin->y = position->y;
- break;
- case TK_ANCHOR_E:
- origin->x = position->x - width;
- origin->y = position->y - height/2;
- break;
- case TK_ANCHOR_SE:
- origin->x = position->x - width;
- origin->y = position->y - height;
- break;
- case TK_ANCHOR_S:
- origin->x = position->x - width/2;
- origin->y = position->y - height;
- break;
- case TK_ANCHOR_SW:
- origin->x = position->x;
- origin->y = position->y - height;
- break;
- case TK_ANCHOR_W:
- origin->x = position->x;
- origin->y = position->y - height/2;
- break;
- }
-}
-
-
-/*
- * Compute the anchor position given the bbox origin, width,
- * height and the anchor.
- */
-void
-ZnOrigin2Anchor(ZnPoint *origin,
- ZnDim width,
- ZnDim height,
- Tk_Anchor anchor,
- ZnPoint *position)
-{
- switch (anchor) {
- case TK_ANCHOR_CENTER:
- position->x = origin->x + width/2;
- position->y = origin->y + height/2;
- break;
- case TK_ANCHOR_NW:
- *position = *origin;
- break;
- case TK_ANCHOR_N:
- position->x = origin->x + width/2;
- position->y = origin->y;
- break;
- case TK_ANCHOR_NE:
- position->x = origin->x + width;
- position->y = origin->y;
- break;
- case TK_ANCHOR_E:
- position->x = origin->x + width;
- position->y = origin->y + height/2;
- break;
- case TK_ANCHOR_SE:
- position->x = origin->x + width;
- position->y = origin->y + height;
- break;
- case TK_ANCHOR_S:
- position->x = origin->x + width/2;
- position->y = origin->y + height;
- break;
- case TK_ANCHOR_SW:
- position->x = origin->x;
- position->y = origin->y + height;
- break;
- case TK_ANCHOR_W:
- position->x = origin->x;
- position->y = origin->y + height/2;
- break;
- }
-}
-
-/*
- * Compute the anchor position given a rectangle and
- * the anchor. The rectangle vertices must be ordered
- * as for a triangle strip:
- *
- * v0 ------------ v2
- * | |
- * | |
- * v1 ------------ v3
- */
-void
-ZnRectOrigin2Anchor(ZnPoint *rect,
- Tk_Anchor anchor,
- ZnPoint *position)
-{
- switch (anchor) {
- case TK_ANCHOR_CENTER:
- position->x = (rect[0].x + rect[3].x) / 2.0;
- position->y = (rect[0].y + rect[3].y) / 2.0;
- break;
- case TK_ANCHOR_NW:
- *position = *rect;
- break;
- case TK_ANCHOR_N:
- position->x = (rect[0].x + rect[2].x) / 2.0;
- position->y = (rect[0].y + rect[2].y) / 2.0;
- break;
- case TK_ANCHOR_NE:
- *position = rect[2];
- break;
- case TK_ANCHOR_E:
- position->x = (rect[2].x + rect[3].x) / 2.0;
- position->y = (rect[2].y + rect[3].y) / 2.0;
- break;
- case TK_ANCHOR_SE:
- *position = rect[3];
- break;
- case TK_ANCHOR_S:
- position->x = (rect[1].x + rect[3].x) / 2.0;
- position->y = (rect[1].y + rect[3].y) / 2.0;
- break;
- case TK_ANCHOR_SW:
- *position = rect[1];
- break;
- case TK_ANCHOR_W:
- position->x = (rect[0].x + rect[1].x) / 2.0;
- position->y = (rect[0].y + rect[1].y) / 2.0;
- break;
- }
-}
-
-void
-ZnBBox2XRect(ZnBBox *bbox,
- XRectangle *r)
-{
- r->x = ZnNearestInt(bbox->orig.x);
- r->y = ZnNearestInt(bbox->orig.y);
- r->width = ZnNearestInt(bbox->corner.x) - r->x;
- r->height = ZnNearestInt(bbox->corner.y) - r->y;
-}
-
-
-void
-ZnGetStringBBox(char *str,
- Tk_Font font,
- ZnPos x,
- ZnPos y,
- ZnBBox *str_bbox)
-{
- Tk_FontMetrics fm;
-
- str_bbox->orig.x = x;
- str_bbox->corner.x = x + Tk_TextWidth(font, str, (int) strlen(str));
- Tk_GetFontMetrics(font, &fm);
- str_bbox->orig.y = y - fm.ascent;
- str_bbox->corner.y = str_bbox->orig.y + fm.ascent + fm.descent;
-}
-
-
-void
-ZnResetBBox(ZnBBox *bbox)
-{
- bbox->orig.x = bbox->orig.y = 0;
- bbox->corner = bbox->orig;
-}
-
-
-void
-ZnCopyBBox(ZnBBox *bbox_from,
- ZnBBox *bbox_to)
-{
- bbox_to->orig = bbox_from->orig;
- bbox_to->corner = bbox_from->corner;
-}
-
-
-void
-ZnIntersectBBox(ZnBBox *bbox1,
- ZnBBox *bbox2,
- ZnBBox *bbox_inter)
-{
- if ((bbox1->corner.x < bbox2->orig.x) ||
- (bbox1->corner.y < bbox2->orig.y) ||
- (bbox2->corner.x < bbox1->orig.x) ||
- (bbox2->corner.y < bbox1->orig.y)) {
- ZnResetBBox(bbox_inter);
- }
- else {
- bbox_inter->orig.x = MAX(bbox1->orig.x, bbox2->orig.x);
- bbox_inter->orig.y = MAX(bbox1->orig.y, bbox2->orig.y);
- bbox_inter->corner.x = MIN(bbox1->corner.x, bbox2->corner.x);
- bbox_inter->corner.y = MIN(bbox1->corner.y, bbox2->corner.y);
- }
-}
-
-
-ZnBool
-ZnIsEmptyBBox(ZnBBox *bbox)
-{
- return (bbox->orig.x >= bbox->corner.x) || (bbox->orig.y >= bbox->corner.y);
-}
-
-
-void
-ZnAddBBoxToBBox(ZnBBox *bbox,
- ZnBBox *bbox2)
-{
- if (ZnIsEmptyBBox(bbox2)) {
- return;
- }
- if (ZnIsEmptyBBox(bbox)) {
- ZnCopyBBox(bbox2, bbox);
- }
- else {
- bbox->orig.x = MIN(bbox->orig.x, bbox2->orig.x);
- bbox->orig.y = MIN(bbox->orig.y, bbox2->orig.y);
- bbox->corner.x = MAX(bbox->corner.x, bbox2->corner.x);
- bbox->corner.y = MAX(bbox->corner.y, bbox2->corner.y);
- }
-}
-
-
-void
-ZnAddPointToBBox(ZnBBox *bbox,
- ZnPos px,
- ZnPos py)
-{
- if (ZnIsEmptyBBox(bbox)) {
- bbox->orig.x = px;
- bbox->orig.y = py;
- bbox->corner.x = bbox->orig.x + 1;
- bbox->corner.y = bbox->orig.y + 1;
- }
- else {
- bbox->orig.x = MIN(bbox->orig.x, px);
- bbox->orig.y = MIN(bbox->orig.y, py);
- bbox->corner.x = MAX(bbox->corner.x, px + 1);
- bbox->corner.y = MAX(bbox->corner.y, py + 1);
- }
-}
-
-
-void
-ZnAddPointsToBBox(ZnBBox *bbox,
- ZnPoint *points,
- unsigned int num_points)
-{
- ZnReal x1, y1, x2, y2, cur;
-
- if (points == NULL) {
- return;
- }
-
- if (num_points == 0) {
- return;
- }
-
- if (ZnIsEmptyBBox(bbox)) {
- x1 = points->x;
- y1 = points->y;
- x2 = x1 + 1;
- y2 = y1 + 1;
- num_points--;
- points++;
- }
- else {
- x1 = bbox->orig.x;
- y1 = bbox->orig.y;
- x2 = bbox->corner.x;
- y2 = bbox->corner.y;
- }
-
- for ( ; num_points > 0; num_points--, points++) {
- cur = points->x;
- if (cur < x1) {
- x1 = cur;
- }
- if (cur > x2) {
- x2 = cur;
- }
- cur = points->y;
- if (cur < y1) {
- y1 = cur;
- }
- if (cur > y2) {
- y2 = cur;
- }
- }
- bbox->orig.x = x1;
- bbox->orig.y = y1;
- if (x1 == x2) {
- x2++;
- }
- if (y1 == y2) {
- y2++;
- }
- bbox->corner.x = x2;
- bbox->corner.y = y2;
-}
-
-
-void
-ZnAddStringToBBox(ZnBBox *bbox,
- char *str,
- Tk_Font font,
- ZnPos cx,
- ZnPos cy)
-{
- ZnBBox str_bbox;
-
- ZnGetStringBBox(str, font, cx, cy, &str_bbox);
- ZnAddBBoxToBBox(bbox, &str_bbox);
-}
-
-ZnBool
-ZnPointInBBox(ZnBBox *bbox,
- ZnPos x,
- ZnPos y)
-{
- return ((x >= bbox->orig.x) && (x < bbox->corner.x) &&
- (y >= bbox->orig.y) && (y < bbox->corner.y));
-}
-
-
-/*
- * Tell where aa area is with respect to another area.
- * Return -1 if the first is entirely outside the second,
- * 1 if it is entirely inside and 0 otherwise.
- */
-int
-ZnBBoxInBBox(ZnBBox *bbox1,
- ZnBBox *bbox2)
-{
- if ((bbox1->corner.x <= bbox2->orig.x) ||
- (bbox1->orig.x >= bbox2->corner.x) ||
- (bbox1->corner.y <= bbox2->orig.y) ||
- (bbox1->orig.y >= bbox2->corner.y)) {
- return -1;
- }
- if ((bbox2->orig.x <= bbox1->orig.x) &&
- (bbox1->corner.x <= bbox2->corner.x) &&
- (bbox2->orig.y <= bbox1->orig.y) &&
- (bbox1->corner.y <= bbox2->corner.y)) {
- return 1;
- }
- return 0;
-}
-
-/*
- * Tell where a line is with respect to an area.
- * Return -1 if the line is entirely outside the bbox, 1
- * if it is entirely inside and 0 otherwise.
- */
-int
-ZnLineInBBox(ZnPoint *p1,
- ZnPoint *p2,
- ZnBBox *bbox)
-{
- ZnBool p1_inside = ZnPointInBBox(bbox, p1->x, p1->y);
- ZnBool p2_inside = ZnPointInBBox(bbox, p2->x, p2->y);
-
- if (p1_inside != p2_inside) {
- return 0;
- }
-
- if (p1_inside && p2_inside) {
- return 1;
- }
-
- /*
- * Segment may intersect area, check it more thoroughly.
- */
- /* Vertical line */
- if (p1->x == p2->x) {
- if (((p1->y >= bbox->orig.y) ^ (p2->y >= bbox->orig.y)) &&
- (p1->x >= bbox->orig.x) &&
- (p1->x <= bbox->corner.x)) {
- return 0;
- }
- }
- /* Horizontal line */
- else if (p1->y == p2->y) {
- if (((p1->x >= bbox->orig.x) ^ (p2->x >= bbox->orig.x)) &&
- (p1->y >= bbox->orig.y) &&
- (p1->y <= bbox->corner.y)) {
- return 0;
- }
- }
- /* Diagonal, do it the hard way. */
- else {
- ZnReal slope = (p2->y - p1->y) / (p2->x - p1->x);
- ZnDim low, high, x, y;
- ZnDim bbox_width = bbox->corner.x - bbox->orig.x;
- ZnDim bbox_height = bbox->corner.y - bbox->orig.y;
-
- /* Check against left edge */
- if (p1->x < p2->x) {
- low = p1->x;
- high = p2->x;
- }
- else {
- low = p2->x;
- high = p1->x;
- }
-
- y = p1->y + (bbox->orig.x - p1->x) * slope;
- if ((bbox->orig.x >= low) && (bbox->orig.x <= high) &&
- (y >= bbox->orig.y) && (y <= bbox->corner.y))
- return 0;
-
- /* Check against right edge */
- y += bbox_width * slope;
- if ((y >= bbox->orig.y) && (y <= bbox->corner.y) &&
- (bbox->corner.x >= low) && (bbox->corner.x <= high))
- return 0;
-
- /* Check against bottom edge */
- if (p1->y < p2->y) {
- low = p1->y;
- high = p2->y;
- }
- else {
- low = p2->y;
- high = p1->y;
- }
-
- x = p1->x + (bbox->orig.y - p1->y) / slope;
- if ((x >= bbox->orig.x) && (x <= bbox->corner.x) &&
- (bbox->orig.y >= low) && (bbox->orig.y <= high))
- return 0;
-
- /* Check against top edge */
- x += bbox_height / slope;
- if ((x >= bbox->orig.x) && (x <= bbox->corner.x) &&
- (bbox->corner.y >= low) && (bbox->corner.y <= high))
- return 0;
- }
-
- return -1;
-}
-
-
-ZnBool
-ZnTestCCW(ZnPoint *points,
- unsigned int num_points)
-{
- ZnPoint *p, *p_p=NULL, *p_n=NULL, min;
- ZnReal xprod;
- unsigned int i, min_index;
-
- if (num_points < 3) {
- return True;
- }
-
- /*
- * First find the lowest rightmost vertex. In X11 this is the
- * topmost one.
- */
- p = points;
- min = *p;
- min_index = 0;
- for (i = 1, p++; i < num_points; i++, p++) {
- if ((p->y < min.y) ||
- ((p->y == min.y) && (p->x > min.x))) {
- min_index = i;
- min = *p;
- }
- }
- /*
- * Then find the indices of the previous and next
- * vertices.
- */
- p = &points[min_index];
- /*printf("min index %d, prev %d, next %d\n", min_index,
- (min_index+(num_points-1))%num_points, (min_index+1)%num_points);*/
- /*printf("lower point index %d %d %d\n",
- (min_index+(num_points-1))%num_points, min_index, (min_index+1)%num_points);*/
- /*
- * Try to find preceding and following points that are not
- * the same as the base point.
- */
- for (i = 1; i < num_points; i++) {
- p_p = &points[(min_index+(num_points-i))%num_points]; /* min_index-1 */
- if ((p_p->x != p->x) || (p_p->y != p->y)) {
- break;
- }
- }
- for (i = 1; i < num_points; i++) {
- p_n = &points[(min_index+i)%num_points];
- if ((p_p->x != p->x) || (p_p->y != p->y)) {
- break;
- }
- }
- xprod = ((p->x - p_p->x) * (p_n->y - p->y)) - ((p->y - p_p->y) * (p_n->x - p->x));
- return (xprod < 0.0); /* Should be > 0 but X11 has Y axis reverted. */
-}
-
-
-/*
- * ZnShiftLine --
- * Given two points describing a line and a distance, return
- * to points describing a line parallel to it at the given distance.
- * When looking the line from p1 to p2 the new line will be dist away
- * on its left. Negative values are allowed for dist, resulting in a line
- * on the right.
- */
-void
-ZnShiftLine(ZnPoint *p1,
- ZnPoint *p2,
- ZnReal dist,
- ZnPoint *p3,
- ZnPoint *p4)
-{
- static int shift_table[129];
- ZnBool dx_neg, dy_neg;
- int dx, dy;
-
- /*
- * Initialize the conversion table.
- */
- if (shift_table[0] == 0) {
- int i;
- ZnReal tangent, cosine;
-
- for (i = 0; i <= 128; i++) {
- tangent = i/128.0;
- cosine = 128/cos(atan(tangent)) + 0.5;
- shift_table[i] = (int) cosine;
- }
- }
-
- *p3 = *p1;
- dx = (int) (p2->x - p1->x);
- dy = (int) (p2->y - p1->y);
- if (dx < 0) {
- dx = -dx;
- dx_neg = True;
- }
- else {
- dx_neg = False;
- }
- if (dy < 0) {
- dy = -dy;
- dy_neg = True;
- }
- else {
- dy_neg = False;
- }
- if ((dy < PRECISION_LIMIT) && (dx < PRECISION_LIMIT)) {
- fprintf(stderr, "ShiftLine: segment is a point\n");
- return;
- }
-
- if (dy <= dx) {
- dy = (((int) dist * shift_table[(dy*128)/dx]) + 64) / 128;
- if (!dx_neg) {
- dy = -dy;
- }
- p3->y += dy;
- }
- else {
- dx = (((int) dist * shift_table[(dx*128)/dy]) + 64) / 128;
- if (dy_neg) {
- dx = -dx;
- }
- p3->x += dx;
- }
-
- p4->x = p3->x + (p2->x - p1->x);
- p4->y = p3->y + (p2->y - p1->y);
-}
-
-
-/*
- * IntersectLines --
- * Given two lines described by two points, compute their intersection.
- * The function returns True if the lines are not parallel and False
- * otherwise.
- */
-ZnBool
-ZnIntersectLines(ZnPoint *a1,
- ZnPoint *a2,
- ZnPoint *b1,
- ZnPoint *b2,
- ZnPoint *pi)
-{
- ZnReal dxadyb, dxbdya, dxadxb, dyadyb, p, q;
-
- dxadyb = (a2->x - a1->x)*(b2->y - b1->y);
- dxbdya = (b2->x - b1->x)*(a2->y - a1->y);
- dxadxb = (a2->x - a1->x)*(b2->x - b1->x);
- dyadyb = (a2->y - a1->y)*(b2->y - b1->y);
-
- if (dxadyb == dxbdya) {
- return False;
- }
-
- p = a1->x*dxbdya - b1->x*dxadyb + (b1->y - a1->y)*dxadxb;
- q = dxbdya - dxadyb;
- if (q < 0) {
- p = -p;
- q = -q;
- }
- if (p < 0) {
- pi->x = - ((-p + q/2)/q);
- }
- else {
- pi->x = (p + q/2)/q;
- }
-
- p = a1->y*dxadyb - b1->y*dxbdya + (b1->x - a1->x)*dyadyb;
- q = dxadyb - dxbdya;
- if (q < 0) {
- p = -p;
- q = -q;
- }
- if (p < 0) {
- pi->y = - ((-p + q/2)/q);
- }
- else {
- pi->y = (p + q/2)/q;
- }
-
- return True;
-}
-
-
-/*
- * InsetPolygon --
- * Inset the given polygon by the given amount. The
- * value can be negative, in this case the polygon will
- * be outset.
- */
-/**** A FINIR ****/
-void
-ZnInsetPolygon(ZnPoint *p,
- unsigned int num_points,
- ZnDim inset)
-{
- ZnPoint *p1, *p2;
- ZnPoint new_p1, new_p2;
- /* ZnPoint shift1, shift2;*/
- unsigned int i, processed_points;
-
- processed_points = 0;
-
- if ((p->x == p[num_points-1].x) && (p->y == p[num_points-1].y)) {
- num_points--;
- }
- for (p1 = p, p2 = p1+1, i = 0; i < num_points; i++, p1 = p2, p2++) {
- /*
- * Wrap to the first point.
- */
- if (i == num_points-1) {
- p2 = p;
- }
- /*
- * Skip duplicate vertices.
- */
- if ((p2->x == p1->x) && (p2->y == p1->y)) {
- continue;
- }
-
- ZnShiftLine(p1, p2, inset, &new_p1, &new_p2);
-
- if (processed_points >= 1) {
- }
- }
-}
-
-
-/*
- * Compute the two corner points of a thick line end.
- * Two points describing the line segment and the width
- * are given as input. If projecting is true this function
- * mimics the X11 line projecting behaviour. The computed
- * end is located around p2.
- */
-void
-ZnGetButtPoints(ZnPoint *p1,
- ZnPoint *p2,
- ZnDim width,
- ZnBool projecting,
- ZnPoint *c1,
- ZnPoint *c2)
-{
- ZnReal w_2 = width/2.0;
- ZnDim length = hypot(p2->x - p1->x, p2->y - p1->y);
- ZnReal delta_x, delta_y;
-
- if (length == 0.0) {
- c1->x = c2->x = p2->x;
- c1->y = c2->y = p2->y;
- }
- else {
- delta_x = -w_2 * (p2->y - p1->y) / length;
- delta_y = w_2 * (p2->x - p1->x) / length;
- c1->x = p2->x + delta_x;
- c2->x = p2->x - delta_x;
- c1->y = p2->y + delta_y;
- c2->y = p2->y - delta_y;
- if (projecting) {
- c1->x += delta_y;
- c2->x += delta_y;
- c1->y -= delta_x;
- c2->y -= delta_x;
- }
- }
-}
-
-/*
- * Compute the inside and outside points of the mitered
- * corner formed by a thick line going through 3 points.
- * If the angle formed by the three points is less than
- * 11 degrees, False is returned an no points are computed.
- * Else True is returned and the points are in c1, c2.
- *
- * If someday the code is switched to REAL coordinates, we
- * must round each coordinate to the nearer integer to mimic
- * the way pixels are drawn. Sample code: floor(p->x+0.5);
- *
- * Hmmm, the switch has been done but not the rounding ;-)
- */
-ZnBool
-ZnGetMiterPoints(ZnPoint *p1,
- ZnPoint *p2,
- ZnPoint *p3,
- ZnDim width,
- ZnPoint *c1,
- ZnPoint *c2)
-{
- static ZnReal deg11 = (11.0*2.0*M_PI)/360.0;
- ZnReal theta1; /* angle of p2-p1 segment. */
- ZnReal theta2; /* angle of p2-p3 segment. */
- ZnReal theta; /* angle of the joint */
- ZnReal theta3; /* angle of bisector of the joint toward
- * the external point of the joint. */
- ZnReal dist; /* distance of the external points
- * of the corner from the mid point
- * p2. */
- ZnReal delta_x, delta_y; /* projection of (dist,theta3) on x
- * and y. */
-
- if (p2->y == p1->y) {
- theta1 = (p2->x < p1->x) ? 0.0 : M_PI;
- }
- else if (p2->x == p1->x) {
- theta1 = (p2->y < p1->y) ? M_PI/2.0 : -M_PI/2.0;
- }
- else {
- theta1 = atan2(p1->y - p2->y, p1->x - p2->x);
- }
- if (p3->y == p2->y) {
- theta2 = (p3->x > p2->x) ? 0.0 : M_PI;
- }
- else if (p3->x == p2->x) {
- theta2 = (p3->y > p2->y) ? M_PI/2.0 : -M_PI/2.0;
- }
- else {
- theta2 = atan2(p3->y - p2->y, p3->x - p2->x);
- }
- theta = theta1 - theta2;
- if (theta > M_PI) {
- theta -= 2.0*M_PI;
- }
- else if (theta < -M_PI) {
- theta += 2*M_PI;
- }
- if ((theta < deg11) && (theta > -deg11)) {
- return False;
- }
- /*
- * Compute the distance of the internal and external
- * corner points from the intersection p2 (considered
- * at 0,0).
- */
- dist = 0.5*width / sin(0.5*theta);
- dist = ABS(dist);
-
- /*
- * Compute the angle of the bisector of the joint that
- * goes toward the outside of the joint (the left hand
- * when looking from p1-p2).
- */
- theta3 = (theta1 + theta2)/2.0;
- if (sin(theta3 - (theta1 + M_PI)) < 0.0) {
- theta3 += M_PI;
- }
-
- delta_x = dist * cos(theta3);
- c1->x = p2->x + delta_x;
- c2->x = p2->x - delta_x;
- delta_y = dist * sin(theta3);
- c1->y = p2->y + delta_y;
- c2->y = p2->y - delta_y;
-
- return True;
-}
-
-/*
- * Tell where a thick polyline is with respect to an area.
- * Return -1 if the polyline is entirely outside the bbox, 1
- * if it is entirely inside and 0 otherwise. The joints can
- * be specified as JoinMiter, JoinRound, JoinBevel. The ends
- * can be: CapRound, CapButt, CapProjecting.
- */
-int
-ZnPolylineInBBox(ZnPoint *points,
- unsigned int num_points,
- ZnDim width,
- int cap_style,
- int join_style,
- ZnBBox *bbox)
-{
- unsigned int count;
- int inside = -1;
- ZnBool do_miter_as_bevel;
- ZnPoint poly[4];
-
- /*
- * If the first point is inside the area, change inside
- * accordingly.
- */
- if ((points[0].x >= bbox->orig.x) && (points[0].x <= bbox->corner.x) &&
- (points[0].y >= bbox->orig.y) && (points[0].y <= bbox->corner.y)) {
- inside = 1;
- }
-
- /*
- * Now iterate through all the edges. Compute a polygon for
- * each and test it against the area. At each vertex an oval
- * of radius width/2 is also tested to account for round ends
- * and joints.
- */
- do_miter_as_bevel = False;
- for (count = num_points; count >= 2; count--, points++) {
-
- /*
- * Test a circle around the first point if CapRound or
- * around every joint if JoinRound.
- */
- if (((cap_style == CapRound) && (count == num_points)) ||
- ((join_style == JoinRound) && (count != num_points))) {
- if (ZnOvalInBBox(points, width, width, bbox) != inside) {
- return 0;
- }
- }
- /*
- * Build a polygon to represent an edge from the current
- * point to the next. Special cases for the first and
- * last edges to implement the line ends.
- */
- /*
- * First vertex of the edge
- */
- if (count == num_points) {
- ZnGetButtPoints(&points[1], points, width,
- cap_style == CapProjecting, poly, &poly[1]);
- }
- /*
- * Here we are at a joint starting a new edge. If the
- * joint is mitered, start by carrying over the points
- * from the previous edge. Otherwise compute new points
- * for a butt end.
- */
- else if ((join_style == JoinMiter) && !do_miter_as_bevel) {
- poly[0] = poly[3];
- poly[1] = poly[2];
- }
- else {
- ZnGetButtPoints(&points[1], points, width, 0, poly, &poly[1]);
- /*
- * If the previous joint was beveled (or considered so),
- * check the polygon that fill the bevel. It has more or
- * less an X shape, i.e, it's self intersecting. If this
- * is not ok, it may be necessary to permutte poly[1] &
- * poly[2].
- */
- if ((join_style == JoinBevel) || do_miter_as_bevel) {
- if (ZnPolygonInBBox(poly, 4, bbox, NULL) != inside) {
- return 0;
- }
- do_miter_as_bevel = False;
- }
- }
-
- /*
- * Opposite vertex of the edge.
- */
- if (count == 2) {
- ZnGetButtPoints(points, &points[1], width, cap_style == CapProjecting,
- &poly[2], &poly[3]);
- }
- else if (join_style == JoinMiter) {
- if (ZnGetMiterPoints(points, &points[1], &points[2], width,
- &poly[2], &poly[3]) == False) {
- do_miter_as_bevel = True;
- ZnGetButtPoints(points, &points[1], width, 0, &poly[2], &poly[3]);
- }
- }
- else {
- ZnGetButtPoints(points, &points[1], width, 0, &poly[2], &poly[3]);
- }
-
- if (ZnPolygonInBBox(poly, 4, bbox, NULL) != inside) {
- return 0;
- }
- }
-
- /*
- * Test a circle around the last point if CapRound.
- */
- if (cap_style == CapRound) {
- if (ZnOvalInBBox(points, width, width, bbox) != inside) {
- return 0;
- }
- }
-
- return inside;
-}
-
-
-/*
- * Tell where a polygon is with respect to an area.
- * Return -1 if the polygon is entirely outside the bbox, 1
- * if it is entirely inside and 0 otherwise. If area_enclosed
- * is not NULL it tells whether the area is enclosed by the
- * polygon or not.
- */
-int
-ZnPolygonInBBox(ZnPoint *points,
- unsigned int num_points,
- ZnBBox *bbox,
- ZnBool *area_enclosed)
-{
- int inside, count;
- ZnPoint *p, *head, *first, *second;
- ZnBool closed;
-
- if (area_enclosed) {
- *area_enclosed = False;
- }
- p = head = points;
- closed = True;
- count = num_points-2;
- /*
- * Check to see if closed. If not adjust num_points and
- * record this.
- */
- if ((points[0].x != points[num_points-1].x) ||
- (points[0].y != points[num_points-1].y)) {
- closed = False;
- count = num_points-1;
- }
-
- /*
- * Get the status of the first edge.
- */
- inside = ZnLineInBBox(&p[0], &p[1], bbox);
- p++;
- if (inside == 0) {
- return 0;
- }
- for (; count > 0; p++, count--) {
- first = &p[0];
- /*
- * Pretend the polygon is closed if this is not the case.
- */
- if (!closed && (count == 1)) {
- second = head;
- }
- else {
- second = &p[1];
- }
-
- if (ZnLineInBBox(first, second, bbox) != inside) {
- return 0;
- }
- }
-
- /*
- * If all the edges are inside the area, the polygon is
- * inside the area. If all the edges are outside, the polygon
- * may completely enclose the area. Test if the origin of
- * the area is inside the polygon to decide this.
- */
- if (inside == 1) {
- return 1;
- }
-
- /*printf("PolygonInBBox, np = %d, x = %g, y = %g, dist = %g\n",
- num_points, bbox->orig.x, bbox->orig.y,
- PolygonToPointDist(points, num_points, &bbox->orig));*/
- if (ZnPolygonToPointDist(points, num_points, &bbox->orig) <= 0.0) {
- if (area_enclosed) {
- *area_enclosed = True;
- }
- return 0;
- }
-
- return -1;
-}
-
-
-/*
- * Tell where an oval is with respect to an area.
- * Return -1 if the oval is entirely outside the bbox, 1
- * if it is entirely inside and 0 otherwise.
- */
-int
-ZnOvalInBBox(ZnPoint *center,
- ZnDim width,
- ZnDim height,
- ZnBBox *bbox)
-{
- ZnPoint origin, corner;
- ZnDim w_2, h_2;
- ZnReal delta_x, delta_y;
-
- w_2 = (width+1)/2;
- h_2 = (height+1)/2;
-
- origin.x = center->x - w_2;
- origin.y = center->y - h_2;
- corner.x = center->x + w_2;
- corner.y = center->y + h_2;
-
- /*
- * This if the oval bbox is completely inside or outside
- * of the area. Trivial case first.
- */
- if ((bbox->orig.x <= origin.x) && (bbox->corner.x >= corner.x) &&
- (bbox->orig.y <= origin.y) && (bbox->corner.y >= corner.y)) {
- return 1;
- }
- if ((bbox->corner.x < origin.x) || (bbox->orig.x > corner.x) ||
- (bbox->corner.y < origin.y) || (bbox->orig.y > corner.y)) {
- return -1;
- }
-
- /*
- * Then test all sides of the area against the oval center.
- * If the point of a side closest to the center is inside
- * the oval, then the oval intersects the area.
- */
-
- /*
- * Compute the square of the Y axis distance, reducing
- * the oval to a unit circle to take into account the
- * shape factor.
- */
- delta_y = bbox->orig.y - center->y;
- if (delta_y < 0.0) {
- delta_y = center->y - bbox->corner.y;
- if (delta_y < 0.0) {
- delta_y = 0.0;
- }
- }
- delta_y /= h_2;
- delta_y *= delta_y;
-
- /*
- * Test left and then right edges.
- */
- delta_x = (bbox->orig.x - center->x) / w_2;
- delta_x *= delta_x;
- if ((delta_x + delta_y) <= 1.0) {
- return 0;
- }
- delta_x = (bbox->corner.x - center->x) / w_2;
- delta_x *= delta_x;
- if ((delta_x + delta_y) <= 1.0) {
- return 0;
- }
-
- /*
- * Compute the square of the X axis distance, reducing
- * the oval to a unit circle to take into account the
- * shape factor.
- */
- delta_x = bbox->orig.x - center->x;
- if (delta_x < 0.0) {
- delta_x = center->x - bbox->corner.x;
- if (delta_x < 0.0) {
- delta_x = 0.0;
- }
- }
- delta_x /= w_2;
- delta_x *= delta_x;
-
- /*
- * Test top and then bottom edges.
- */
- delta_y = (bbox->orig.y - center->y) / h_2;
- delta_y *= delta_y;
- if ((delta_x + delta_y) <= 1.0) {
- return 0;
- }
- delta_y = (bbox->corner.y - center->y) / h_2;
- delta_y *= delta_y;
- if ((delta_x + delta_y) <= 1.0) {
- return 0;
- }
-
- return -1;
-}
-
-
-/*
- * Tell if a point is in an angular range whose center is 0,0.
- * The range is specified by a starting angle and an angle extent.
- * The use of a double here is important, don't change it. In some
- * case we need to normalize a figure to take care of its shape and
- * the result needs precision.
- */
-ZnBool
-ZnPointInAngle(int start_angle,
- int angle_extent,
- ZnPoint *p)
-{
- ZnReal point_angle;
- int angle_diff;
-
- if ((p->x == 0) && (p->y == 0)) {
- point_angle = 0.0;
- }
- else {
- point_angle = atan2(p->y, p->x) * 180.0 / M_PI;
- }
- angle_diff = (ZnNearestInt(point_angle) - start_angle) % 360;
- if (angle_diff < 0) {
- angle_diff += 360;
- }
- return ((angle_diff <= angle_extent) ||
- ((angle_extent < 0) && ((angle_diff - 360) >= angle_extent)));
-}
-
-/*
- * PointPolarToCartesian --
- * Convert a point in polar coordinates (rho, theta)
- * in a reference system described by angle heading
- * (angles running clockwise) to a point in cartesian
- * coordinates (delta_x, delta_y).
- *
- */
-void
-ZnPointPolarToCartesian(ZnReal heading,
- ZnReal rho,
- ZnReal theta,
- ZnReal *delta_x,
- ZnReal *delta_y)
-{
- ZnReal to_angle;
-
- /* Compute angle in trigonometric system */
- /* to_angle = ZnDegRad(theta) + heading - M_PI_2;*/
- to_angle = heading - ZnDegRad(theta) - M_PI_2;
- /* Compute cartesian coordinates */
- *delta_x = rho * cos(to_angle);
- *delta_y = rho * sin(to_angle);
-}
-
-/*
- * Return a vector angle given its projections
- */
-ZnReal
-ZnProjectionToAngle(ZnReal dx,
- ZnReal dy)
-{
- if (dx == 0) {
- if (dy < 0) {
- return -M_PI_2;
- }
- else if (dy > 0) {
- return M_PI_2;
- }
- else {
- return 0.0;
- }
- }
- else if (dx < 0) {
- return atan(dy / dx) - M_PI;
- }
- else {
- return atan(dy / dx);
- }
- return 0.0;
-}
-
-
-/*
- * Tell if an horizontal line intersect an axis aligned
- * elliptical arc.
- *
- * Returns True if the line described by (x1, x2, y) intersects
- * the arc described by (r1, r2, start_angle and angle_extent).
- * This arc is origin centered.
- */
-ZnBool
-ZnHorizLineToArc(ZnReal x1,
- ZnReal x2,
- ZnReal y,
- ZnReal rx,
- ZnReal ry,
- int start_angle,
- int angle_extent)
-{
- ZnReal tmp, x;
- ZnPoint t;
-
- /*
- * Compute the x-coordinate of one possible intersection point
- * between the arc and the line. Use a transformed coordinate
- * system where the oval is a unit circle centered at the origin.
- * Then scale back to get actual x-coordinate.
- */
- t.y = y/ry;
- tmp = 1 - t.y*t.y;
- if (tmp < 0.0) {
- return False;
- }
- t.x = sqrt(tmp);
- x = t.x*rx;
-
- /*
- * Test both intersection points.
- */
- if ((x >= x1) && (x <= x2) && ZnPointInAngle((int) start_angle, (int) angle_extent, &t)) {
- return True;
- }
- t.x = -t.x;
- if ((-x >= x1) && (-x <= x2) && ZnPointInAngle((int) start_angle, (int) angle_extent, &t)) {
- return True;
- }
- return False;
-}
-
-
-/*
- * Tell if an vertical line intersect an axis aligned
- * elliptical arc.
- *
- * Returns True if the line described by (x1, x2, y) intersects
- * the arc described by (r1, r2, start_angle and angle_extent).
- * This arc is origin centered.
- */
-ZnBool
-ZnVertLineToArc(ZnReal x,
- ZnReal y1,
- ZnReal y2,
- ZnReal rx,
- ZnReal ry,
- int start_angle,
- int angle_extent)
-{
- ZnReal tmp, y;
- ZnPoint t;
-
- /*
- * Compute the y-coordinate of one possible intersection point
- * between the arc and the line. Use a transformed coordinate
- * system where the oval is a unit circle centered at the origin.
- * Then scale back to get actual y-coordinate.
- */
- t.x = x/rx;
- tmp = 1 - t.x*t.x;
- if (tmp < 0.0) {
- return False;
- }
- t.y = sqrt(tmp);
- y = t.y*ry;
-
- /*
- * Test both intersection points.
- */
- if ((y > y1) && (y < y2) && ZnPointInAngle((int) start_angle, (int) angle_extent, &t)) {
- return True;
- }
- t.y = -t.y;
- if ((-y > y1) && (-y < y2) && ZnPointInAngle((int) start_angle, (int) angle_extent, &t)) {
- return True;
- }
- return False;
-}
-
-
-/*
- * Return the distance of the given point to the rectangle
- * described by rect. Return negative values for points in
- * the rectangle.
- */
-ZnDim
-ZnRectangleToPointDist(ZnBBox *bbox,
- ZnPoint *p)
-{
- ZnDim new_dist, dist;
- ZnPoint p1, p2;
-
- p1.x = bbox->orig.x;
- p1.y = p2.y = bbox->orig.y;
- p2.x = bbox->corner.x;
- dist = ZnLineToPointDist(&p1, &p2, p, NULL);
- if (dist == 0.0) {
- return 0.0;
- }
-
- p1 = p2;
- p2.y = bbox->corner.y;
- new_dist = ZnLineToPointDist(&p1, &p2, p, NULL);
- dist = MIN(dist, new_dist);
- if (dist == 0.0) {
- return 0.0;
- }
-
- p1 = p2;
- p2.x = bbox->orig.x;
- new_dist = ZnLineToPointDist(&p1, &p2, p, NULL);
- dist = MIN(dist, new_dist);
- if (dist == 0.0) {
- return 0.0;
- }
-
- p1 = p2;
- p2.y = bbox->orig.y;
- new_dist = ZnLineToPointDist(&p1, &p2, p, NULL);
- dist = MIN(dist, new_dist);
-
- if (ZnPointInBBox(bbox, p->x, p->y)) {
- return -dist;
- }
- else {
- return dist;
- }
-}
-
-
-/*
- * Return the distance of the given point to the line
- * described by <xl1,yl1>, <xl2,yl2>..
- */
-ZnDim
-ZnLineToPointDist(ZnPoint *p1,
- ZnPoint *p2,
- ZnPoint *p,
- ZnPoint *closest)
-{
- ZnReal x, y;
- ZnReal x_int, y_int;
-
- /*
- * First compute the closest point on the line. This is done
- * separately for vertical, horizontal, other lines.
- */
-
- /* Vertical */
- if (p1->x == p2->x) {
- x = p1->x;
- if (p1->y >= p2->y) {
- y_int = MIN(p1->y, p->y);
- y_int = MAX(y_int, p2->y);
- }
- else {
- y_int = MIN(p2->y, p->y);
- y_int = MAX(y_int, p1->y);
- }
- y = y_int;
- }
-
- /* Horizontal */
- else if (p1->y == p2->y) {
- y = p1->y;
- if (p1->x >= p2->x) {
- x_int = MIN(p1->x, p->x);
- x_int = MAX(x_int, p2->x);
- }
- else {
- x_int = MIN(p2->x, p->x);
- x_int = MAX(x_int, p1->x);
- }
- x = x_int;
- }
-
- /*
- * Other. Compute its parameters of form y = a1*x + b1 and
- * then compute the parameters of the perpendicular passing
- * through the point y = a2*x + b2, last find the closest point
- * on the segment.
- */
- else {
- ZnReal a1, a2, b1, b2;
-
- a1 = (p2->y - p1->y) / (p2->x - p1->x);
- b1 = p1->y - a1*p1->x;
-
- a2 = -1.0/a1;
- b2 = p->y - a2*p->x;
-
- x = (b2 - b1) / (a1 - a2);
- y = a1*x + b1;
-
- if (p1->x > p2->x) {
- if (x > p1->x) {
- x = p1->x;
- y = p1->y;
- }
- else if (x < p2->x) {
- x = p2->x;
- y = p2->y;
- }
- }
- else {
- if (x > p2->x) {
- x = p2->x;
- y = p2->y;
- }
- else if (x < p1->x) {
- x = p1->x;
- y = p1->y;
- }
- }
- }
-
- if (closest) {
- closest->x = x;
- closest->y = y;
- }
-
- /* Return the distance */
- return hypot(p->x - x, p->y - y);
-}
-
-
-/*
- * Return the distance of the polygon described by
- * points, to the given point. If the point is
- * inside return values are negative.
- */
-ZnDim
-ZnPolygonToPointDist(ZnPoint *points,
- unsigned int num_points,
- ZnPoint *p)
-{
- ZnDim best_distance, dist;
- int intersections;
- int x_int, y_int;
- ZnPoint *first_point;
- ZnReal x, y;
- ZnPoint p1, p2;
-
- /*
- * The algorithm iterates through all the edges of the polygon
- * computing for each the distance to the point and whether a vertical
- * ray starting at the point, intersects the edge. The smallest
- * distance of all edges is stored in best_distance while intersections
- * hold the count of edges to rays intersections. For more informations
- * on how the distance is computed see LineToPointDist.
- */
- best_distance = 1.0e40;
- intersections = 0;
-
- first_point = points;
-
- /*
- * Check to see if closed. Adjust num_points to open it (the
- * algorithm always consider a set of points as a closed polygon).
- */
- if ((points[0].x == points[num_points-1].x) &&
- (points[0].y == points[num_points-1].y)) {
- num_points--;
- }
-
- for ( ; num_points >= 1; num_points--, points++) {
- p1 = points[0];
- /*
- * Wrap over to the first point.
- */
- if (num_points == 1) {
- p2 = *first_point;
- }
- else {
- p2 = points[1];
- }
-
- /*
- * First try to find the closest point on this edge.
- */
-
- /* Vertical edge */
- if (p1.x == p2.x) {
- x = p1.x;
- if (p1.y >= p2.y) {
- y_int = (int) MIN(p1.y, p->y);
- y_int = (int) MAX(y_int, p2.y);
- }
- else {
- y_int = (int) MIN(p2.y, p->y);
- y_int = (int) MAX(y_int, p1.y);
- }
- y = y_int;
- }
-
- /* Horizontal edge */
- else if (p1.y == p2.y) {
- y = p1.y;
- if (p1.x >= p2.x) {
- x_int = (int) MIN(p1.x, p->x);
- x_int = (int) MAX(x_int, p2.x);
- if ((p->y < y) && (p->x < p1.x) && (p->x >= p2.x)) {
- intersections++;
- }
- }
- else {
- x_int = (int) MIN(p2.x, p->x);
- x_int = (int) MAX(x_int, p1.x);
- if ((p->y < y) && (p->x < p2.x) && (p->x >= p1.x)) {
- intersections++;
- }
- }
- x = x_int;
- }
-
- /* Other */
- else {
- ZnReal a1, b1, a2, b2;
-
- a1 = (p2.y - p1.y) / (p2.x - p1.x);
- b1 = p1.y - a1 * p1.x;
-
- a2 = -1.0/a1;
- b2 = p->y - a2 * p->x;
-
- x = (b2 - b1)/(a1 - a2);
- y = a1 * x + b1;
-
- if (p1.x > p2.x) {
- if (x > p1.x) {
- x = p1.x;
- y = p1.y;
- }
- else if (x < p2.x) {
- x = p2.x;
- y = p2.y;
- }
- }
- else {
- if (x > p2.x) {
- x = p2.x;
- y = p2.y;
- }
- else if (x < p1.x) {
- x = p1.x;
- y = p1.y;
- }
- }
-
- if (((a1 * p->x + b1) > p->y) && /* True if point is lower */
- (p->x >= MIN(p1.x, p2.x)) &&
- (p->x < MAX(p1.x, p2.x))) {
- intersections++;
- }
- }
-
- /*
- * Now compute the distance to the closest point and
- * keep it if it is the shortest.
- */
- dist = hypot(p->x - x, p->y - y);
- best_distance = MIN(best_distance, dist);
- /*
- * We can safely escape here if distance is zero.
- */
- if (best_distance == 0.0) {
- return 0.0;
- }
- }
-
- /*
- * Well, all the edges are processed, if the
- * intersection count is odd the point is inside.
- */
- if (intersections & 0x1) {
- return -best_distance;
- }
- else {
- return best_distance;
- }
-}
-
-
-/*
- * Return the distance of a thick polyline to the
- * given point. Cap and Join parameters are considered
- * in the process.
- */
-ZnDim
-ZnPolylineToPointDist(ZnPoint *points,
- unsigned int num_points,
- ZnDim width,
- int cap_style,
- int join_style,
- ZnPoint *p)
-{
- ZnBool miter2bevel = False;
- unsigned int count;
- ZnPoint *ptr;
- ZnPoint outline[5];
- ZnDim dist, best_dist, h_width;
-
- best_dist = 1.0e36;
- h_width = width/2.0;
-
- for (count = num_points, ptr = points; count >= 2; count--, ptr++) {
- if (((cap_style == CapRound) && (count == num_points)) ||
- ((join_style == JoinRound) && (count != num_points))) {
- dist = hypot(ptr->x - p->x, ptr->y - p->y) - h_width;
- if (dist <= 0.0) {
- best_dist = 0.0;
- goto done;
- }
- else if (dist < best_dist) {
- best_dist = dist;
- }
- }
- /*
- * Build the polygonal outline of the current edge.
- */
- if (count == num_points) {
- ZnGetButtPoints(&ptr[1], ptr, width, cap_style==CapProjecting, outline, &outline[1]);
- }
- else if ((join_style == JoinMiter) && !miter2bevel) {
- outline[0] = outline[3];
- outline[1] = outline[2];
- }
- else {
- ZnGetButtPoints(&ptr[1], ptr, width, 0, outline, &outline[1]);
- /*
- * If joints are beveled, check the distance to the polygon
- * that fills the joint.
- */
- if ((join_style == JoinBevel) || miter2bevel) {
- outline[4] = outline[0];
- dist = ZnPolygonToPointDist(outline, 5, p);
- if (dist <= 0.0) {
- best_dist = 0.0;
- goto done;
- }
- else if (dist < best_dist) {
- best_dist = dist;
- }
- miter2bevel = False;
- }
- }
- if (count == 2) {
- ZnGetButtPoints(ptr, &ptr[1], width, cap_style==CapProjecting,
- &outline[2], &outline[3]);
- }
- else if (join_style == JoinMiter) {
- if (ZnGetMiterPoints(ptr, &ptr[1], &ptr[2], width,
- &outline[2], &outline[3]) == False) {
- miter2bevel = True;
- ZnGetButtPoints(ptr, &ptr[1], width, 0, &outline[2], &outline[3]);
- }
- /*printf("2=%g+%g, 3=%g+%g\n",
- outline[2].x, outline[2].y, outline[3].x, outline[3].y);*/
- }
- else {
- ZnGetButtPoints(ptr, &ptr[1], width, 0, &outline[2], &outline[3]);
- }
- outline[4] = outline[0];
- /*printf("0=%g+%g, 1=%g+%g, 2=%g+%g, 3=%g+%g, 4=%g+%g\n",
- outline[0].x, outline[0].y, outline[1].x, outline[1].y,
- outline[2].x, outline[2].y, outline[3].x, outline[3].y,
- outline[4].x, outline[4].y);*/
- dist = ZnPolygonToPointDist(outline, 5, p);
- if (dist <= 0.0) {
- best_dist = 0.0;
- goto done;
- }
- else if (dist < best_dist) {
- best_dist = dist;
- }
- }
-
- /*
- * Test the final point if cap style is round. The code so far
- * has only handled the butt and projecting cases.
- */
- if (cap_style == CapRound) {
- dist = hypot(ptr->x - p->x, ptr->y - p->y) - h_width;
- if (dist <= 0.0) {
- best_dist = 0.0;
- goto done;
- }
- else if (dist < best_dist) {
- best_dist = dist;
- }
- }
-
- done:
- return best_dist;
-}
-
-
-/*
- * Return the distance of the given oval to the point given.
- * The oval is described by its bounding box <xbb,ybb,wbb,hbb>,
- * the thickness of its outline <width>. Return values are negative
- * if the point is inside.
- */
-ZnDim
-ZnOvalToPointDist(ZnPoint *center,
- ZnDim width,
- ZnDim height,
- ZnDim line_width,
- ZnPoint *p)
-{
- ZnReal x_delta, y_delta;
- /* ZnReal x_diameter, y_diameter;*/
- ZnDim scaled_distance;
- ZnDim distance_to_outline;
- ZnDim distance_to_center;
-
- /*
- * Compute the distance from the point given to the center
- * of the oval. Then compute the same distance in a coordinate
- * system where the oval is a circle with unit radius.
- */
-
- x_delta = p->x - center->x;
- y_delta = p->y - center->y;
- distance_to_center = hypot(x_delta, y_delta);
- scaled_distance = hypot(x_delta / ((width + line_width) / 2.0),
- y_delta / ((height + line_width) / 2.0));
-
- /*
- * If the scaled distance is greater than 1.0 the point is outside
- * the oval. Compute the distance to the edge and convert it back
- * to the original coordinate system. This distance is not much
- * accurate and can overestimate the real distance if the oval is
- * very eccentric.
- */
- if (scaled_distance > 1.0) {
- distance_to_outline = (distance_to_center / scaled_distance) * (scaled_distance - 1.0);
- return distance_to_outline;
- }
-
- /*
- * The point is inside the oval. Compute the distance as above and check
- * if the point is within the outline.
- */
- if (scaled_distance > 1.0e-10) {
- distance_to_outline = (distance_to_center / scaled_distance) * (1.0 - scaled_distance) - line_width;
- }
- else {
- /*
- * If the point is very close to the center avoid dividing by a
- * very small number, take another method.
- */
- if (width < height)
- distance_to_outline = (width - line_width) / 2;
- else
- distance_to_outline = (height - line_width) / 2;
- }
-
- if (distance_to_outline < 0.0)
- return 0.0;
- else
- return -distance_to_outline;
-}
-
-
-static int bezier_basis[][4] =
-{
- { -1, 3, -3, 1},
- { 3, -6, 3, 0},
- { -3, 3, 0, 0},
- { 1, 0, 0, 0}
-};
-
-/*
- **********************************************************************************
- *
- * Arc2Param --
- *
- * Given a Bezier curve describing an arc and an angle return the parameter
- * value for the intersection point between the arc and the ray at angle.
- *
- **********************************************************************************
- */
-#define EVAL(coeff, t) (((coeff[0]*t + coeff[1])*t + coeff[2]) * t + coeff[3])
-static ZnReal
-Arc2Param(ZnPoint *controls,
- ZnReal angle)
-{
- ZnReal coeff_x[4], coeff_y[4];
- ZnReal min_angle, min_t, max_angle, max_t, cur_angle, cur_t;
- int i, j, depth = 0;
-
- /* assume angle >= 0 */
- while (angle > M_PI) {
- angle -= 2 * M_PI;
- }
-
- for (i = 0; i < 4; i++) {
- coeff_x[i] = coeff_y[i] = 0.0;
- for (j = 0; j < 4; j++) {
- coeff_x[i] += bezier_basis[i][j] * controls[j].x;
- coeff_y[i] += bezier_basis[i][j] * controls[j].y;
- }
- }
-
- min_angle = atan2(controls[0].y, controls[0].x);
- max_angle = atan2(controls[3].y, controls[3].x);
- if (max_angle < min_angle) {
- min_angle -= M_PI+M_PI;
- }
- if (angle > max_angle) {
- angle -= M_PI+M_PI;
- }
-
- min_t = 0.0; max_t = 1.0;
-
- while (depth < 15) {
- cur_t = (max_t + min_t) / 2.0;
- cur_angle = atan2(EVAL(coeff_y, cur_t), EVAL(coeff_x, cur_t));
- if (angle > cur_angle) {
- min_t = cur_t;
- min_angle = cur_angle;
- }
- else {
- max_t = cur_t;
- max_angle = cur_angle;
- }
- depth += 1;
- }
-
- if ((max_angle-angle) < (angle-min_angle)) {
- return max_t;
- }
-
- return min_t;
-}
-#undef EVAL
-
-
-/*
- **********************************************************************************
- *
- * BezierSubdivide --
- *
- * Subdivide a Bezier curve given by controls at parameter t. Return
- * in controls, the first or the last part depending on boolean first.
- *
- **********************************************************************************
- */
-static void
-BezierSubdivide(ZnPoint *controls,
- ZnReal t,
- ZnBool first)
-{
- ZnReal s = 1.0 - t;
- ZnPoint r[7];
- ZnPoint a;
-
- r[0] = controls[0];
- r[6] = controls[3];
- a.x = s*controls[1].x + t*controls[2].x;
- a.y = s*controls[1].y + t*controls[2].y;
- r[1].x = s*r[0].x + t*controls[1].x;
- r[1].y = s*r[0].y + t*controls[1].y;
- r[2].x = s*r[1].x + t*a.x;
- r[2].y = s*r[1].y + t*a.y;
- r[5].x = s*controls[2].x + t*r[6].x;
- r[5].y = s*controls[2].y + t*r[6].y;
- r[4].x = s*a.x + t*r[5].x;
- r[4].y = s*a.y + t*r[5].y;
- r[3].x = s*r[2].x + t*r[4].x;
- r[3].y = s*r[2].y + t*r[4].y;
-
- if (first) {
- memcpy(controls, r, 4*sizeof(ZnPoint));
- }
- else {
- memcpy(controls, &r[3], 4*sizeof(ZnPoint));
- }
-}
-
-
-/*
- **********************************************************************************
- *
- * ZnGetBezierPoints --
- * Use recursive subdivision to approximate the curve. The subdivision stops
- * when the error is under eps.
- * This algorithm is adaptive, meaning that it computes the minimum number
- * of segments needed to render each curve part.
- *
- **********************************************************************************
- */
-void
-ZnGetBezierPoints(ZnPoint *p1,
- ZnPoint *c1,
- ZnPoint *c2,
- ZnPoint *p2,
- ZnList to_points,
- ZnReal eps)
-{
- ZnReal dist;
-
- dist = ZnLineToPointDist(p1, p2, c1, NULL);
- if ((dist < eps) && ((c1->x != c2->x) || (c1->y != c2->y))) {
- dist = ZnLineToPointDist(p1, p2, c2, NULL);
- }
-
- if (dist > eps) {
- ZnPoint mid_segm, new_c1, new_c2;
- /*
- * Subdivide the curve at t = 0.5
- * and compute each new curve.
- */
- mid_segm.x = (p1->x + 3*c1->x + 3*c2->x + p2->x) / 8.0;
- mid_segm.y = (p1->y + 3*c1->y + 3*c2->y + p2->y) / 8.0;
- new_c1.x = (p1->x + c1->x) / 2.0;
- new_c1.y = (p1->y + c1->y) / 2.0;
- new_c2.x = (p1->x + 2*c1->x + c2->x) / 4.0;
- new_c2.y = (p1->y + 2*c1->y + c2->y) / 4.0;
- ZnGetBezierPoints(p1, &new_c1, &new_c2, &mid_segm, to_points, eps);
-
- new_c1.x = (c1->x + 2*c2->x + p2->x) / 4.0;
- new_c1.y = (c1->y + 2*c2->y + p2->y) / 4.0;
- new_c2.x = (c2->x + (p2->x)) / 2.0;
- new_c2.y = (c2->y + (p2->y)) / 2.0;
- ZnGetBezierPoints(&mid_segm, &new_c1, &new_c2, p2, to_points, eps);
- }
- else {
- /*
- * Flat enough add the end to the current path.
- * The start should already be there.
- */
- ZnListAdd(to_points, p2, ZnListTail);
- }
-}
-
-
-/*
- **********************************************************************************
- *
- * ZnGetBezierPath --
- * Compute in to_points a new set of points describing a Bezier path based
- * on the control points given in from_points.
- * If more than four points are given, the algorithm iterate over the
- * set using the last point of a segment as the first point of the next.
- * If 3 points are left, they are interpreted as a Bezier segment with
- * coincident internal control points. If 2 points are left a straight
- * is emitted.
- *
- **********************************************************************************
- */
-void
-ZnGetBezierPath(ZnList from_points,
- ZnList to_points)
-{
- ZnPoint *fp;
- int num_fp, i;
-
- fp = ZnListArray(from_points);
- num_fp = ZnListSize(from_points);
-
- /*
- * make sure the output vector is empty, then add the first point.
- */
- ZnListEmpty(to_points);
- ZnListAdd(to_points, fp, ZnListTail);
-
- for (i = 0; i < num_fp; ) {
- if (i < (num_fp-3)) {
- ZnGetBezierPoints(fp, fp+1, fp+2, fp+3, to_points, 1.0);
- if (i < (num_fp-4)) {
- fp += 3;
- i += 3;
- }
- else {
- break;
- }
- }
- else if (i == (num_fp-3)) {
- ZnGetBezierPoints(fp, fp+1, fp+1, fp+2, to_points, 1.0);
- break;
- }
- else if (i == (num_fp-2)) {
- ZnListAdd(to_points, fp+1, ZnListTail);
- break;
- }
- }
-}
-
-
-/*
- **********************************************************************************
- *
- * ZnGetCirclePoints --
- * Return a pointer to an array of points describing a
- * circle arc of radius 1.0. The arc is described by start_angle,
- * end_angle and the type: 0 for arc, 1 for chord, 2 for pie slice,
- * 3 for a full circle (in which case start_angle and end_angle are
- * not used.
- * The number of points is returned in num_points. If type is not 3,
- * point_list must not be NULL. If not NULL, it is filled with the
- * computed points.
- *
- **********************************************************************************
- */
-ZnPoint *
-ZnGetCirclePoints(int type,
- int quality,
- ZnReal start_angle,
- ZnReal angle_extent,
- unsigned int *num_points,
- ZnList point_list)
-{
- static ZnPoint genarc_finest[] = { /* 128 */
- {1.0, 0.0},
- {0.99879545617, 0.0490676750517},
- {0.99518472653, 0.0980171417729},
- {0.989176509646, 0.146730476607},
- {0.980785279837, 0.195090324861},
- {0.970031252314, 0.24298018342},
- {0.956940334469, 0.290284681418},
- {0.941544063473, 0.336889858172},
- {0.923879530291, 0.382683437725},
- {0.903989290333, 0.42755509933},
- {0.88192126093, 0.471396743221},
- {0.857728605899, 0.514102751035},
- {0.831469607468, 0.555570240255},
- {0.803207525865, 0.595699312064},
- {0.773010446922, 0.634393292011},
- {0.74095111805, 0.671558962907},
- {0.707106772982, 0.707106789391},
- {0.671558945713, 0.740951133634},
- {0.634393274074, 0.773010461643},
- {0.595699293426, 0.803207539688},
- {0.555570220961, 0.83146962036},
- {0.514102731131, 0.857728617829},
- {0.471396722756, 0.881921271869},
- {0.427555078353, 0.903989300254},
- {0.382683416286, 0.923879539171},
- {0.336889836323, 0.94154407129},
- {0.290284659212, 0.956940341205},
- {0.242980160911, 0.970031257952},
- {0.195090302102, 0.980785284364},
- {0.146730453653, 0.98917651305},
- {0.0980171186795, 0.995184728805},
- {0.0490676518746, 0.998795457308},
- {-2.32051033331e-08, 1.0},
- {-0.0490676982289, 0.998795455031},
- {-0.0980171648663, 0.995184724256},
- {-0.146730499561, 0.989176506241},
- {-0.19509034762, 0.98078527531},
- {-0.24298020593, 0.970031246675},
- {-0.290284703624, 0.956940327733},
- {-0.33688988002, 0.941544055655},
- {-0.382683459163, 0.923879521411},
- {-0.427555120307, 0.903989280412},
- {-0.471396763686, 0.881921249991},
- {-0.514102770939, 0.85772859397},
- {-0.555570259549, 0.831469594576},
- {-0.595699330703, 0.803207512042},
- {-0.634393309949, 0.773010432201},
- {-0.6715589801, 0.740951102467},
- {-0.707106805799, 0.707106756574},
- {-0.740951149217, 0.671558928519},
- {-0.773010476365, 0.634393256136},
- {-0.803207553511, 0.595699274787},
- {-0.831469633252, 0.555570201666},
- {-0.857728629759, 0.514102711228},
- {-0.881921282808, 0.471396702291},
- {-0.903989310176, 0.427555057376},
- {-0.923879548052, 0.382683394847},
- {-0.941544079108, 0.336889814474},
- {-0.956940347941, 0.290284637006},
- {-0.97003126359, 0.242980138401},
- {-0.980785288892, 0.195090279343},
- {-0.989176516455, 0.146730430699},
- {-0.995184731079, 0.0980170955862},
- {-0.998795458447, 0.0490676286974},
- {-1.0, -4.64102066663e-08},
- {-0.998795453892, -0.049067721406},
- {-0.995184721981, -0.0980171879596},
- {-0.989176502836, -0.146730522515},
- {-0.980785270783, -0.195090370379},
- {-0.970031241037, -0.24298022844},
- {-0.956940320997, -0.29028472583},
- {-0.941544047838, -0.336889901869},
- {-0.923879512531, -0.382683480602},
- {-0.90398927049, -0.427555141284},
- {-0.881921239052, -0.471396784151},
- {-0.85772858204, -0.514102790842},
- {-0.831469581684, -0.555570278844},
- {-0.803207498218, -0.595699349341},
- {-0.77301041748, -0.634393327887},
- {-0.740951086883, -0.671558997294},
- {-0.707106740165, -0.707106822208},
- {-0.671558911325, -0.740951164801},
- {-0.634393238198, -0.773010491086},
- {-0.595699256149, -0.803207567335},
- {-0.555570182372, -0.831469646144},
- {-0.514102691324, -0.857728641689},
- {-0.471396681826, -0.881921293746},
- {-0.427555036399, -0.903989320097},
- {-0.382683373409, -0.923879556932},
- {-0.336889792626, -0.941544086926},
- {-0.2902846148, -0.956940354677},
- {-0.242980115891, -0.970031269229},
- {-0.195090256583, -0.980785293419},
- {-0.146730407745, -0.98917651986},
- {-0.0980170724928, -0.995184733354},
- {-0.0490676055202, -0.998795459585},
- {6.96153097774e-08, -1.0},
- {0.0490677445832, -0.998795452754},
- {0.098017211053, -0.995184719707},
- {0.146730545469, -0.989176499431},
- {0.195090393139, -0.980785266256},
- {0.242980250949, -0.970031235398},
- {0.290284748036, -0.956940314261},
- {0.336889923717, -0.94154404002},
- {0.382683502041, -0.923879503651},
- {0.427555162262, -0.903989260569},
- {0.471396804617, -0.881921228114},
- {0.514102810746, -0.85772857011},
- {0.555570298138, -0.831469568792},
- {0.59569936798, -0.803207484395},
- {0.634393345825, -0.773010402759},
- {0.671559014488, -0.740951071299},
- {0.707106838616, -0.707106723757},
- {0.740951180385, -0.671558894131},
- {0.773010505807, -0.63439322026},
- {0.803207581158, -0.59569923751},
- {0.831469659036, -0.555570163078},
- {0.857728653619, -0.51410267142},
- {0.881921304685, -0.471396661361},
- {0.903989330019, -0.427555015421},
- {0.923879565812, -0.38268335197},
- {0.941544094743, -0.336889770777},
- {0.956940361414, -0.290284592594},
- {0.970031274867, -0.242980093382},
- {0.980785297946, -0.195090233824},
- {0.989176523265, -0.146730384792},
- {0.995184735628, -0.0980170493994},
- {0.998795460724, -0.0490675823431},
- {1.0, 0.0}
- };
- static ZnPoint genarc_finer[] = { /* 64 */
- {1.0, 0.0},
- {0.99518472653, 0.0980171417729},
- {0.980785279837, 0.195090324861},
- {0.956940334469, 0.290284681418},
- {0.923879530291, 0.382683437725},
- {0.88192126093, 0.471396743221},
- {0.831469607468, 0.555570240255},
- {0.773010446922, 0.634393292011},
- {0.707106772982, 0.707106789391},
- {0.634393274074, 0.773010461643},
- {0.555570220961, 0.83146962036},
- {0.471396722756, 0.881921271869},
- {0.382683416286, 0.923879539171},
- {0.290284659212, 0.956940341205},
- {0.195090302102, 0.980785284364},
- {0.0980171186795, 0.995184728805},
- {-2.32051033331e-08, 1.0},
- {-0.0980171648663, 0.995184724256},
- {-0.19509034762, 0.98078527531},
- {-0.290284703624, 0.956940327733},
- {-0.382683459163, 0.923879521411},
- {-0.471396763686, 0.881921249991},
- {-0.555570259549, 0.831469594576},
- {-0.634393309949, 0.773010432201},
- {-0.707106805799, 0.707106756574},
- {-0.773010476365, 0.634393256136},
- {-0.831469633252, 0.555570201666},
- {-0.881921282808, 0.471396702291},
- {-0.923879548052, 0.382683394847},
- {-0.956940347941, 0.290284637006},
- {-0.980785288892, 0.195090279343},
- {-0.995184731079, 0.0980170955862},
- {-1.0, -4.64102066663e-08},
- {-0.995184721981, -0.0980171879596},
- {-0.980785270783, -0.195090370379},
- {-0.956940320997, -0.29028472583},
- {-0.923879512531, -0.382683480602},
- {-0.881921239052, -0.471396784151},
- {-0.831469581684, -0.555570278844},
- {-0.77301041748, -0.634393327887},
- {-0.707106740165, -0.707106822208},
- {-0.634393238198, -0.773010491086},
- {-0.555570182372, -0.831469646144},
- {-0.471396681826, -0.881921293746},
- {-0.382683373409, -0.923879556932},
- {-0.2902846148, -0.956940354677},
- {-0.195090256583, -0.980785293419},
- {-0.0980170724928, -0.995184733354},
- {6.96153097774e-08, -1.0},
- {0.098017211053, -0.995184719707},
- {0.195090393139, -0.980785266256},
- {0.290284748036, -0.956940314261},
- {0.382683502041, -0.923879503651},
- {0.471396804617, -0.881921228114},
- {0.555570298138, -0.831469568792},
- {0.634393345825, -0.773010402759},
- {0.707106838616, -0.707106723757},
- {0.773010505807, -0.63439322026},
- {0.831469659036, -0.555570163078},
- {0.881921304685, -0.471396661361},
- {0.923879565812, -0.38268335197},
- {0.956940361414, -0.290284592594},
- {0.980785297946, -0.195090233824},
- {0.995184735628, -0.0980170493994},
- {1.0, 0.0}
- };
- static ZnPoint genarc_fine[] = { /* 40 */
- {1.0, 0.0},
- {0.987688340232, 0.156434467332},
- {0.951056514861, 0.309016998789},
- {0.891006521028, 0.453990505942},
- {0.809016988919, 0.587785259802},
- {0.707106772982, 0.707106789391},
- {0.587785241028, 0.809017002559},
- {0.453990485266, 0.891006531563},
- {0.309016976719, 0.951056522032},
- {0.156434444413, 0.987688343862},
- {-2.32051033331e-08, 1.0},
- {-0.156434490252, 0.987688336602},
- {-0.309017020858, 0.95105650769},
- {-0.453990526618, 0.891006510493},
- {-0.587785278575, 0.809016975279},
- {-0.707106805799, 0.707106756574},
- {-0.809017016198, 0.587785222255},
- {-0.891006542098, 0.453990464591},
- {-0.951056529203, 0.30901695465},
- {-0.987688347492, 0.156434421493},
- {-1.0, -4.64102066663e-08},
- {-0.987688332972, -0.156434513171},
- {-0.951056500519, -0.309017042928},
- {-0.891006499958, -0.453990547294},
- {-0.80901696164, -0.587785297348},
- {-0.707106740165, -0.707106822208},
- {-0.587785203482, -0.809017029838},
- {-0.453990443915, -0.891006552633},
- {-0.309016932581, -0.951056536373},
- {-0.156434398574, -0.987688351122},
- {6.96153097774e-08, -1.0},
- {0.15643453609, -0.987688329342},
- {0.309017064997, -0.951056493349},
- {0.45399056797, -0.891006489423},
- {0.587785316122, -0.809016948},
- {0.707106838616, -0.707106723757},
- {0.809017043478, -0.587785184709},
- {0.891006563167, -0.453990423239},
- {0.951056543544, -0.309016910511},
- {0.987688354752, -0.156434375655},
- {1.0, 0.0}
- };
- static ZnPoint genarc_medium[] = { /* 20 */
- {1.0, 0.0},
- {0.951056514861, 0.309016998789},
- {0.809016988919, 0.587785259802},
- {0.587785241028, 0.809017002559},
- {0.309016976719, 0.951056522032},
- {-2.32051033331e-08, 1.0},
- {-0.309017020858, 0.95105650769},
- {-0.587785278575, 0.809016975279},
- {-0.809017016198, 0.587785222255},
- {-0.951056529203, 0.30901695465},
- {-1.0, -4.64102066663e-08},
- {-0.951056500519, -0.309017042928},
- {-0.80901696164, -0.587785297348},
- {-0.587785203482, -0.809017029838},
- {-0.309016932581, -0.951056536373},
- {6.96153097774e-08, -1.0},
- {0.309017064997, -0.951056493349},
- {0.587785316122, -0.809016948},
- {0.809017043478, -0.587785184709},
- {0.951056543544, -0.309016910511},
- {1.0, 0.0}
- };
- static ZnPoint genarc_coarse[] = { /* 10 */
- {1.0, 0.0},
- {0.809016988919, 0.587785259802},
- {0.309016976719, 0.951056522032},
- {-0.309017020858, 0.95105650769},
- {-0.809017016198, 0.587785222255},
- {-1.0, -4.64102066663e-08},
- {-0.80901696164, -0.587785297348},
- {-0.309016932581, -0.951056536373},
- {0.309017064997, -0.951056493349},
- {0.809017043478, -0.587785184709},
- {1.0, 0.0}
- };
- unsigned int num_p, i;
- ZnPoint *p, *p_from;
- ZnPoint center_p = { 0.0, 0.0 };
- ZnPoint start_p, wp;
- ZnReal iangle, end_angle=0;
-
- switch (quality) {
- case ZN_CIRCLE_COARSE:
- num_p = sizeof(genarc_coarse)/sizeof(ZnPoint);
- p = p_from = genarc_coarse;
- break;
- case ZN_CIRCLE_MEDIUM:
- num_p = sizeof(genarc_medium)/sizeof(ZnPoint);
- p = p_from = genarc_medium;
- break;
- case ZN_CIRCLE_FINER:
- num_p = sizeof(genarc_finer)/sizeof(ZnPoint);
- p = p_from = genarc_finer;
- break;
- case ZN_CIRCLE_FINEST:
- num_p = sizeof(genarc_finest)/sizeof(ZnPoint);
- p = p_from = genarc_finest;
- break;
- default:
- case ZN_CIRCLE_FINE:
- num_p = sizeof(genarc_fine)/sizeof(ZnPoint);
- p = p_from = genarc_fine;
- }
-
- /*printf("start: %g, extent: %g\n", start_angle, angle_extent);*/
- if (angle_extent == 2*M_PI) {
- type = 3;
- }
- if (type != 3) {
- end_angle = start_angle+angle_extent;
- if (angle_extent < 0) {
- iangle = start_angle;
- start_angle = end_angle;
- end_angle = iangle;
- }
- /*
- * normalize start_angle and end_angle.
- */
- if (start_angle < 0.0) {
- start_angle += 2.0*M_PI;
- }
- if (end_angle < 0.0) {
- end_angle += 2.0*M_PI;
- }
- if (end_angle < start_angle) {
- end_angle += 2.0*M_PI;
- }
- /*printf("---start: %g, end: %g\n", start_angle, end_angle);*/
- }
-
- /*
- * Now 0 <= start_angle < 2 * M_PI and start_angle <= end_angle.
- */
- if ((type != 3) || (point_list != NULL)) {
- if (type == 3) {
- ZnListAssertSize(point_list, num_p);
- p = ZnListArray(point_list);
- for (i = 0; i < num_p; i++, p++, p_from++) {
- *p = *p_from;
- }
- }
- else {
- ZnListEmpty(point_list);
- iangle = 2*M_PI / (num_p-1);
- start_p.x = cos(start_angle);
- start_p.y = sin(start_angle);
- ZnListAdd(point_list, &start_p, ZnListTail);
- i = (unsigned int) (start_angle / iangle);
- if ((i * iangle) < start_angle) {
- i++;
- }
- while (1) {
- if (start_angle + iangle <= end_angle) {
- if (i == num_p-1) {
- i = 0;
- }
- ZnListAdd(point_list, &p_from[i], ZnListTail);
- start_angle += iangle;
- i++;
- }
- else {
- wp.x = cos(end_angle);
- wp.y = sin(end_angle);
- ZnListAdd(point_list, &wp, ZnListTail);
- break;
- }
- }
- if (type == 1) {
- ZnListAdd(point_list, &start_p, ZnListTail);
- }
- else if (type == 2) {
- ZnListAdd(point_list, &center_p, ZnListTail);
- ZnListAdd(point_list, &start_p, ZnListTail);
- }
- }
- p = ZnListArray(point_list);
- num_p = ZnListSize(point_list);
- }
-
- *num_points = num_p;
- return p;
-}
-
-/*
- **********************************************************************************
- *
- * ZnGetArcPath --
- * Compute in to_points a set of Bezier control points describing an arc
- * path given the start angle, the stop angle and the type: 0 for arc,
- * 1 for chord, 2 for pie slice.
- * To obtain the actual polygonal shape, the client should use GetBezierPath
- * on the returned controls (after applying transform). The returned arc
- * is circular and centered on 0,0.
- *
- **********************************************************************************
- */
-static ZnReal arc_nodes_x[4] = { 1.0, 0.0, -1.0, 0.0 };
-static ZnReal arc_nodes_y[4] = { 0.0, 1.0, 0.0, -1.0 };
-static ZnReal arc_controls_x[8] = { 1.0, 0.55197, -0.55197, -1.0, -1.0, -0.55197, 0.55197, 1.0 };
-static ZnReal arc_controls_y[8] = { 0.55197, 1.0, 1.0, 0.55197, -0.55197, -1.0, -1.0, -0.55197 };
-void
-ZnGetArcPath(ZnReal start_angle,
- ZnReal end_angle,
- int type,
- ZnList to_points)
-{
- int start_quad, end_quad, quadrant;
- ZnPoint center_p = { 0.0, 0.0 };
- ZnPoint start_p = center_p;
-
- /*
- * make sure the output vector is empty.
- */
- ZnListEmpty(to_points);
-
- /*
- * normalize start_angle and end_angle.
- */
- start_angle = fmod(start_angle, 2.0*M_PI);
- if (start_angle < 0.0) {
- start_angle += 2.0*M_PI;
- }
- end_angle = fmod(end_angle, 2.0*M_PI);
- if (end_angle < 0.0) {
- end_angle += 2.0*M_PI;
- }
- if (start_angle >= end_angle) {
- if (start_angle == end_angle) {
- type = 3;
- }
- end_angle += 2.0*M_PI;
- }
-
- /*
- * Now 0 <= start_angle < 2 * M_PI and start_angle <= end_angle.
- */
-
- start_quad = (int) (start_angle / (M_PI / 2.0));
- end_quad = (int) (end_angle / (M_PI / 2.0));
-
- for (quadrant = start_quad; quadrant <= end_quad; quadrant++) {
- ZnPoint controls[4];
- ZnReal t;
-
- controls[0].x = arc_nodes_x[quadrant % 4];
- controls[0].y = arc_nodes_y[quadrant % 4];
- controls[1].x = arc_controls_x[2 * (quadrant % 4)];
- controls[1].y = arc_controls_y[2 * (quadrant % 4)];
- controls[2].x = arc_controls_x[2 * (quadrant % 4) + 1];
- controls[2].y = arc_controls_y[2 * (quadrant % 4) + 1];
- controls[3].x = arc_nodes_x[(quadrant + 1) % 4];
- controls[3].y = arc_nodes_y[(quadrant + 1) % 4];
-
- if (quadrant == start_quad) {
- t = Arc2Param(controls, start_angle);
- BezierSubdivide(controls, t, False);
- /*
- * The path is still empty and we have to create the first
- * vertex.
- */
- start_p = controls[0];
- ZnListAdd(to_points, &controls[0], ZnListTail);
- }
- if (quadrant == end_quad) {
- t = Arc2Param(controls, end_angle);
- if (!t) {
- break;
- }
- BezierSubdivide(controls, t, True);
- }
- ZnListAdd(to_points, &controls[1], ZnListTail);
- ZnListAdd(to_points, &controls[2], ZnListTail);
- ZnListAdd(to_points, &controls[3], ZnListTail);
- }
-
- if (type == 2) {
- ZnListAdd(to_points, &center_p, ZnListTail);
- /*
- * Can't add this one, it would be interpreted by GetBezierPath
- * as an off-curve control. The path should be closed by the client
- * after expansion by GetBezierPath.
- *
- ZnListAdd(to_points, &start_p, ZnListTail);
- */
- }
- else if (type == 1) {
- ZnListAdd(to_points, &start_p, ZnListTail);
- }
-}
-
-
-/*
- **********************************************************************************
- *
- * SmoothPathWithBezier --
- * Compute in to_points a new set of points describing a smoothed path based
- * on the path given in from_points. The algorithm use Bezier cubic curves.
- *
- **********************************************************************************
- */
-void
-ZnSmoothPathWithBezier(ZnPoint *fp,
- unsigned int num_fp,
- ZnList to_points)
-{
- ZnBool closed;
- ZnPoint s[4];
- unsigned int i;
-
- /*
- * make sure the output vector is empty
- */
- ZnListEmpty(to_points);
-
- /*
- * If the curve is closed, generates a Bezier curve that
- * spans the closure. Else simply add the first point to
- * the path.
- */
- if ((fp[0].x == fp[num_fp-1].x) && (fp[0].y == fp[num_fp-1].y)) {
- closed = True;
- s[0].x = 0.5*fp[num_fp-2].x + 0.5*fp[0].x;
- s[0].y = 0.5*fp[num_fp-2].y + 0.5*fp[0].y;
- s[1].x = 0.167*fp[num_fp-2].x + 0.833*fp[0].x;
- s[1].y = 0.167*fp[num_fp-2].y + 0.833*fp[0].y;
- s[2].x = 0.833*fp[0].x + 0.167*fp[1].x;
- s[2].y = 0.833*fp[0].y + 0.167*fp[1].y;
- s[3].x = 0.5*fp[0].x + 0.5*fp[1].x;
- s[3].y = 0.5*fp[0].y + 0.5*fp[1].y;
- ZnListAdd(to_points, s, ZnListTail);
- ZnGetBezierPoints(s, s+1, s+2, s+3, to_points, 1.0);
- }
- else {
- closed = False;
- ZnListAdd(to_points, &fp[0], ZnListTail);
- }
-
- for (i = 2; i < num_fp; i++, fp++) {
- /*
- * Setup the first two control points. This differ
- * for first segment of open curves.
- */
- if ((i == 2) && !closed) {
- s[0] = fp[0];
- s[1].x = 0.333*fp[0].x + 0.667*fp[1].x;
- s[1].y = 0.333*fp[0].y + 0.667*fp[1].y;
- }
- else {
- s[0].x = 0.5*fp[0].x + 0.5*fp[1].x;
- s[0].y = 0.5*fp[0].y + 0.5*fp[1].y;
- s[1].x = 0.167*fp[0].x + 0.833*fp[1].x;
- s[1].y = 0.167*fp[0].y + 0.833*fp[1].y;
- }
-
- /*
- * Setup the last two control points. This also differ
- * for last segment of open curves.
- */
- if ((i == num_fp-1) && !closed) {
- s[2].x = 0.667*fp[1].x + 0.333*fp[2].x;
- s[2].y = 0.667*fp[1].y + 0.333*fp[2].y;
- s[3] = fp[2];
- }
- else {
- s[2].x = 0.833*fp[1].x + 0.167*fp[2].x;
- s[2].y = 0.833*fp[1].y + 0.167*fp[2].y;
- s[3].x = 0.5*fp[1].x + 0.5*fp[2].x;
- s[3].y = 0.5*fp[1].y + 0.5*fp[2].y;
- }
-
- /*
- * If the first two points or the last two are equal
- * output the last control point. Else generate the
- * Bezier curve.
- */
- if (((fp[0].x == fp[1].x) && (fp[0].y == fp[1].y)) ||
- ((fp[1].x == fp[2].x) && (fp[1].y == fp[2].y))) {
- ZnListAdd(to_points, &s[3], ZnListTail);
- }
- else {
- ZnGetBezierPoints(s, s+1, s+2, s+3, to_points, 1.0);
- }
- }
-}
-
-
-/*
- **********************************************************************************
- *
- * FitBezier --
- * Fit a Bezier curve to a (sub)set of digitized points.
- *
- * From: An Algorithm for Automatically Fitting Digitized Curves
- * by Philip J. Schneider in "Graphics Gems", Academic Press, 1990
- *
- **********************************************************************************
- */
-
-static ZnReal
-V2DistanceBetween2Points(ZnPoint *a,
- ZnPoint *b)
-{
- ZnReal dx = a->x - b->x;
- ZnReal dy = a->y - b->y;
- return sqrt((dx*dx)+(dy*dy));
-}
-
-static ZnReal
-V2SquaredLength(ZnPoint *a)
-{
- return (a->x * a->x)+(a->y * a->y);
-}
-
-static ZnReal
-V2Length(ZnPoint *a)
-{
- return sqrt(V2SquaredLength(a));
-}
-
-static ZnPoint *
-V2Scale(ZnPoint *v,
- ZnReal newlen)
-{
- ZnReal len = V2Length(v);
- if (len != 0.0) {
- v->x *= newlen/len;
- v->y *= newlen/len;
- }
- return v;
-}
-
-static ZnPoint *
-V2Negate(ZnPoint *v)
-{
- v->x = -v->x; v->y = -v->y;
- return v;
-}
-
-static ZnPoint *
-V2Normalize(ZnPoint *v)
-{
- ZnReal len = sqrt(V2Length(v));
- if (len != 0.0) {
- v->x /= len;
- v->y /= len;
- }
- return v;
-}
-static ZnPoint *
-V2Add(ZnPoint *a,
- ZnPoint *b,
- ZnPoint *c)
-{
- c->x = a->x + b->x;
- c->y = a->y + b->y;
- return c;
-}
-
-static ZnReal
-V2Dot(ZnPoint *a,
- ZnPoint *b)
-{
- return (a->x*b->x) + (a->y*b->y);
-}
-
-static ZnPoint
-V2AddII(ZnPoint a,
- ZnPoint b)
-{
- ZnPoint c;
- c.x = a.x + b.x;
- c.y = a.y + b.y;
- return c;
-}
-
-static ZnPoint
-V2ScaleIII(ZnPoint v,
- ZnReal s)
-{
- ZnPoint result;
- result.x = v.x * s;
- result.y = v.y * s;
- return result;
-}
-
-static ZnPoint
-V2SubII(ZnPoint a,
- ZnPoint b)
-{
- ZnPoint c;
- c.x = a.x - b.x;
- c.y = a.y - b.y;
- return c;
-}
-
-/*
- * B0, B1, B2, B3, Bezier multipliers.
- */
-static ZnReal
-B0(ZnReal u)
-{
- ZnReal tmp = 1.0 - u;
- return tmp * tmp * tmp;
-}
-
-static ZnReal
-B1(ZnReal u)
-{
- ZnReal tmp = 1.0 - u;
- return 3 * u * (tmp * tmp);
-}
-
-static ZnReal
-B2(ZnReal u)
-{
- ZnReal tmp = 1.0 - u;
- return 3 * u * u * tmp;
-}
-
-static ZnReal
-B3(ZnReal u)
-{
- return u * u * u;
-}
-
-/*
- * ChordLengthParameterize --
- * Assign parameter values to digitized points
- * using relative distances between points.
- */
-static ZnReal *
-ChordLengthParameterize(ZnPoint *d,
- unsigned int first,
- unsigned int last)
-{
- unsigned int i;
- ZnReal *u;
-
- u = (ZnReal *) ZnMalloc((unsigned) (last-first+1) * sizeof(ZnReal));
-
- u[0] = 0.0;
- for (i = first+1; i <= last; i++) {
- u[i-first] = u[i-first-1] + V2DistanceBetween2Points(&d[i], &d[i-1]);
- }
-
- for (i = first + 1; i <= last; i++) {
- u[i-first] = u[i-first] / u[last-first];
- }
-
- return u;
-}
-
-/*
- * Bezier --
- * Evaluate a Bezier curve at a particular parameter value
- *
- */
-static ZnPoint
-BezierII(int degree,
- ZnPoint *V,
- ZnReal t)
-{
- int i, j;
- ZnPoint Q; /* Point on curve at parameter t */
- ZnPoint *Vtemp; /* Local copy of control points */
-
- /* Copy array */
- Vtemp = (ZnPoint *) ZnMalloc((unsigned)((degree+1) * sizeof (ZnPoint)));
- for (i = 0; i <= degree; i++) {
- Vtemp[i] = V[i];
- }
-
- /* Triangle computation */
- for (i = 1; i <= degree; i++) {
- for (j = 0; j <= degree-i; j++) {
- Vtemp[j].x = (1.0 - t) * Vtemp[j].x + t * Vtemp[j+1].x;
- Vtemp[j].y = (1.0 - t) * Vtemp[j].y + t * Vtemp[j+1].y;
- }
- }
-
- Q = Vtemp[0];
- ZnFree(Vtemp);
- return Q;
-}
-
-/*
- * NewtonRaphsonRootFind --
- * Use Newton-Raphson iteration to find better root.
- */
-static ZnReal
-NewtonRaphsonRootFind(ZnPoint *Q,
- ZnPoint P,
- ZnReal u)
-{
- ZnReal numerator, denominator;
- ZnPoint Q1[3], Q2[2]; /* Q' and Q'' */
- ZnPoint Q_u, Q1_u, Q2_u; /*u evaluated at Q, Q', & Q'' */
- ZnReal uPrime; /* Improved u */
- unsigned int i;
-
- /* Compute Q(u) */
- Q_u = BezierII(3, Q, u);
-
- /* Generate control vertices for Q' */
- for (i = 0; i <= 2; i++) {
- Q1[i].x = (Q[i+1].x - Q[i].x) * 3.0;
- Q1[i].y = (Q[i+1].y - Q[i].y) * 3.0;
- }
-
- /* Generate control vertices for Q'' */
- for (i = 0; i <= 1; i++) {
- Q2[i].x = (Q1[i+1].x - Q1[i].x) * 2.0;
- Q2[i].y = (Q1[i+1].y - Q1[i].y) * 2.0;
- }
-
- /* Compute Q'(u) and Q''(u) */
- Q1_u = BezierII(2, Q1, u);
- Q2_u = BezierII(1, Q2, u);
-
- /* Compute f(u)/f'(u) */
- numerator = (Q_u.x - P.x) * (Q1_u.x) + (Q_u.y - P.y) * (Q1_u.y);
- denominator = (Q1_u.x) * (Q1_u.x) + (Q1_u.y) * (Q1_u.y) +
- (Q_u.x - P.x) * (Q2_u.x) + (Q_u.y - P.y) * (Q2_u.y);
-
- /* u = u - f(u)/f'(u) */
- uPrime = u - (numerator/denominator);
- return (uPrime);
-}
-
-/*
- * Reparameterize --
- * Given set of points and their parameterization, try to find
- * a better parameterization.
- */
-static ZnReal *
-Reparameterize(ZnPoint *d,
- unsigned int first,
- unsigned int last,
- ZnReal *u,
- ZnPoint *bezCurve)
-{
- unsigned int nPts = last-first+1;
- unsigned int i;
- ZnReal *uPrime; /* New parameter values */
-
- uPrime = (ZnReal *) ZnMalloc(nPts * sizeof(ZnReal));
- for (i = first; i <= last; i++) {
- uPrime[i-first] = NewtonRaphsonRootFind(bezCurve, d[i], u[i-first]);
- }
- return (uPrime);
-}
-
-/*
- * GenerateBezier --
- * Use least-squares method to find Bezier control
- * points for region.
- */
-static void
-GenerateBezier(ZnPoint *d,
- unsigned int first,
- unsigned int last,
- ZnReal *uPrime,
- ZnPoint tHat1,
- ZnPoint tHat2,
- ZnPoint *bez_curve)
-{
- unsigned int i;
- ZnPoint *A0, *A1; /* Precomputed rhs for eqn */
- unsigned int num_points; /* Number of pts in sub-curve */
- ZnReal C[2][2]; /* Matrix C */
- ZnReal X[2]; /* Matrix X */
- ZnReal det_C0_C1; /* Determinants of matrices */
- ZnReal det_C0_X, det_X_C1;
- ZnReal alpha_l; /* Alpha values, left and right */
- ZnReal alpha_r;
- ZnPoint tmp; /* Utility variable */
-
- num_points = last - first + 1;
- A0 = (ZnPoint *) ZnMalloc(num_points * sizeof(ZnPoint));
- A1 = (ZnPoint *) ZnMalloc(num_points * sizeof(ZnPoint));
-
- /* Compute the A's */
- for (i = 0; i < num_points; i++) {
- ZnPoint v1, v2;
- v1 = tHat1;
- v2 = tHat2;
- V2Scale(&v1, B1(uPrime[i]));
- V2Scale(&v2, B2(uPrime[i]));
- A0[i] = v1;
- A1[i] = v2;
- }
-
- /* Create the C and X matrices */
- C[0][0] = 0.0;
- C[0][1] = 0.0;
- C[1][0] = 0.0;
- C[1][1] = 0.0;
- X[0] = 0.0;
- X[1] = 0.0;
-
- for (i = 0; i < num_points; i++) {
- C[0][0] += V2Dot(&A0[i], &A0[i]);
- C[0][1] += V2Dot(&A0[i], &A1[i]);
- C[1][0] = C[0][1];
- C[1][1] += V2Dot(&A1[i], &A1[i]);
-
- tmp = V2SubII(d[first + i],
- V2AddII(V2ScaleIII(d[first], B0(uPrime[i])),
- V2AddII(V2ScaleIII(d[first], B1(uPrime[i])),
- V2AddII(V2ScaleIII(d[last], B2(uPrime[i])),
- V2ScaleIII(d[last], B3(uPrime[i]))))));
-
- X[0] += V2Dot(&A0[i], &tmp);
- X[1] += V2Dot(&A1[i], &tmp);
- }
-
- /* Compute the determinants of C and X */
- det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
- det_C0_X = C[0][0] * X[1] - C[0][1] * X[0];
- det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
-
- /* Finally, derive alpha values */
- if (det_C0_C1 == 0.0) {
- det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12;
- }
- alpha_l = det_X_C1 / det_C0_C1;
- alpha_r = det_C0_X / det_C0_C1;
-
- /* If alpha negative, use the Wu/Barsky heuristic (see text) */
- if (alpha_l < 0.0 || alpha_r < 0.0) {
- ZnReal dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0;
-
- bez_curve[0] = d[first];
- bez_curve[3] = d[last];
- V2Add(&bez_curve[0], V2Scale(&tHat1, dist), &bez_curve[1]);
- V2Add(&bez_curve[3], V2Scale(&tHat2, dist), &bez_curve[2]);
- }
- else {
- /* First and last control points of the Bezier curve are */
- /* positioned exactly at the first and last data points */
- /* Control points 1 and 2 are positioned an alpha distance out */
- /* on the tangent vectors, left and right, respectively */
- bez_curve[0] = d[first];
- bez_curve[3] = d[last];
- V2Add(&bez_curve[0], V2Scale(&tHat1, alpha_l), &bez_curve[1]);
- V2Add(&bez_curve[3], V2Scale(&tHat2, alpha_r), &bez_curve[2]);
- }
- ZnFree(A0);
- ZnFree(A1);
-}
-
-/*
- * ComputeMaxError --
- * Find the maximum squared distance of digitized points
- * to fitted curve.
-*/
-static ZnReal
-ComputeMaxError(ZnPoint *d,
- unsigned int first,
- unsigned int last,
- ZnPoint *bez_curve,
- ZnReal *u,
- unsigned int *splitPoint)
-{
- unsigned int i;
- ZnReal maxDist; /* Maximum error */
- ZnReal dist; /* Current error */
- ZnPoint P; /* Point on curve */
- ZnPoint v; /* Vector from point to curve */
-
- *splitPoint = (last - first + 1)/2;
- maxDist = 0.0;
- for (i = first + 1; i < last; i++) {
- P = BezierII(3, bez_curve, u[i-first]);
- v = V2SubII(P, d[i]);
- dist = V2SquaredLength(&v);
- if (dist >= maxDist) {
- maxDist = dist;
- *splitPoint = i;
- }
- }
- return (maxDist);
-}
-
-/*
- * ComputeLeftTangent,
- * ComputeRightTangent,
- * ComputeCenterTangent --
- * Approximate unit tangents at endpoints and
- * center of digitized curve.
- */
-static ZnPoint
-ComputeLeftTangent(ZnPoint *d,
- unsigned int end)
-{
- ZnPoint tHat1;
- tHat1 = V2SubII(d[end+1], d[end]);
- tHat1 = *V2Normalize(&tHat1);
- return tHat1;
-}
-
-static ZnPoint
-ComputeRightTangent(ZnPoint *d,
- unsigned int end)
-{
- ZnPoint tHat2;
- tHat2 = V2SubII(d[end-1], d[end]);
- tHat2 = *V2Normalize(&tHat2);
- return tHat2;
-}
-
-
-static ZnPoint
-ComputeCenterTangent(ZnPoint *d,
- unsigned int center)
-{
- ZnPoint V1, V2, tHatCenter;
-
- V1 = V2SubII(d[center-1], d[center]);
- V2 = V2SubII(d[center], d[center+1]);
- tHatCenter.x = (V1.x + V2.x)/2.0;
- tHatCenter.y = (V1.y + V2.y)/2.0;
- tHatCenter = *V2Normalize(&tHatCenter);
- return tHatCenter;
-}
-
-static void
-FitCubic(ZnPoint *d,
- unsigned int first,
- unsigned int last,
- ZnPoint tHat1,
- ZnPoint tHat2,
- ZnReal error,
- ZnList controls)
-{
- ZnPoint *bez_curve; /* Control points of fitted Bezier curve*/
- ZnReal *u; /* Parameter values for point */
- ZnReal *uPrime; /* Improved parameter values */
- ZnReal max_err; /* Maximum fitting error */
- unsigned int splitPoint; /* Point to split point set at */
- unsigned int num_points; /* Number of points in subset */
- ZnReal iteration_err; /* Error below which you try iterating */
- unsigned int max_iter = 4; /* Max times to try iterating */
- ZnPoint tHatCenter; /* Unit tangent vector at splitPoint */
- unsigned int i;
-
- iteration_err = error * error;
- num_points = last - first + 1;
- ZnListAssertSize(controls, ZnListSize(controls)+4);
- bez_curve = ZnListAt(controls, ZnListSize(controls)-4);
-
- /* Use heuristic if region only has two points in it */
- if (num_points == 2) {
- ZnReal dist = V2DistanceBetween2Points(&d[last], &d[first]) / 3.0;
-
- bez_curve[0] = d[first];
- bez_curve[3] = d[last];
- V2Add(&bez_curve[0], V2Scale(&tHat1, dist), &bez_curve[1]);
- V2Add(&bez_curve[3], V2Scale(&tHat2, dist), &bez_curve[2]);
- return;
- }
-
- /* Parameterize points, and attempt to fit curve */
- u = ChordLengthParameterize(d, first, last);
- GenerateBezier(d, first, last, u, tHat1, tHat2, bez_curve);
-
- /* Find max deviation of points to fitted curve */
- max_err = ComputeMaxError(d, first, last, bez_curve, u, &splitPoint);
- if (max_err < error) {
- ZnFree(u);
- return;
- }
-
- /*
- * If error not too large, try some reparameterization
- * and iteration.
- */
- if (max_err < iteration_err) {
- for (i = 0; i < max_iter; i++) {
- uPrime = Reparameterize(d, first, last, u, bez_curve);
- GenerateBezier(d, first, last, uPrime, tHat1, tHat2, bez_curve);
- max_err = ComputeMaxError(d, first, last,
- bez_curve, uPrime, &splitPoint);
- if (max_err < error) {
- ZnFree(u);
- return;
- }
- ZnFree(u);
- u = uPrime;
- }
- }
-
- /* Fitting failed -- split at max error point and fit recursively */
- ZnFree(u);
- ZnListAssertSize(controls, ZnListSize(controls)-4);
- tHatCenter = ComputeCenterTangent(d, splitPoint);
- FitCubic(d, first, splitPoint, tHat1, tHatCenter, error, controls);
- V2Negate(&tHatCenter);
- FitCubic(d, splitPoint, last, tHatCenter, tHat2, error, controls);
-}
-
-void
-ZnFitBezier(ZnPoint *pts,
- unsigned int num_points,
- ZnReal error,
- ZnList controls)
-{
- ZnPoint tHat1, tHat2; /* Unit tangent vectors at endpoints */
-
- tHat1 = ComputeLeftTangent(pts, 0);
- tHat2 = ComputeRightTangent(pts, num_points-1);
- FitCubic(pts, 0, num_points-1, tHat1, tHat2, error, controls);
-}
-