aboutsummaryrefslogtreecommitdiff
path: root/generic/Geo.c
diff options
context:
space:
mode:
authorlecoanet2000-01-12 15:08:46 +0000
committerlecoanet2000-01-12 15:08:46 +0000
commit94bb0261c52d727cb4ea538a83ba9c9cbd59b241 (patch)
treef647361d578c22e0ee7b89c3f8d10633f32ac107 /generic/Geo.c
parent5dab22c586699a140a16a084eead0a0115c4e934 (diff)
downloadtkzinc-94bb0261c52d727cb4ea538a83ba9c9cbd59b241.zip
tkzinc-94bb0261c52d727cb4ea538a83ba9c9cbd59b241.tar.gz
tkzinc-94bb0261c52d727cb4ea538a83ba9c9cbd59b241.tar.bz2
tkzinc-94bb0261c52d727cb4ea538a83ba9c9cbd59b241.tar.xz
Adaptation des ent�tes.
Importation du code sur la gestion des Beziers et routines g�om�triques. Modification du code de g�n�ration des Beziers. Correction de bugs dans PolylineToPointDist, PolygonInBBox, PolylineInBBox.
Diffstat (limited to 'generic/Geo.c')
-rw-r--r--generic/Geo.c1844
1 files changed, 1844 insertions, 0 deletions
diff --git a/generic/Geo.c b/generic/Geo.c
new file mode 100644
index 0000000..203d3d4
--- /dev/null
+++ b/generic/Geo.c
@@ -0,0 +1,1844 @@
+/*
+ * Geo.c -- Implementation of common geometric routines.
+ *
+ * Authors : Patrick Lecoanet.
+ * Creation date :
+ *
+ * $Id$
+ */
+
+/*
+ * Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet --
+ *
+ * This code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this code; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/*
+ * 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__ " $";
+
+
+/*
+ * Compute the origin of the rectangle given
+ * by position, anchor, width and height.
+ */
+void
+Anchor2Origin(RadarPoint *position,
+ RadarDim width,
+ RadarDim height,
+ RadarAnchor anchor,
+ RadarPoint *origin)
+{
+ switch (anchor) {
+ case RadarAnchorCenter:
+ origin->x = position->x - width/2;
+ origin->y = position->y - height/2;
+ break;
+ case RadarAnchorNW:
+ *origin = *position;
+ break;
+ case RadarAnchorN:
+ origin->x = position->x - width/2;
+ origin->y = position->y;
+ break;
+ case RadarAnchorNE:
+ origin->x = position->x - width;
+ origin->y = position->y;
+ break;
+ case RadarAnchorE:
+ origin->x = position->x - width;
+ origin->y = position->y - height/2;
+ break;
+ case RadarAnchorSE:
+ origin->x = position->x - width;
+ origin->y = position->y - height;
+ break;
+ case RadarAnchorS:
+ origin->x = position->x - width/2;
+ origin->y = position->y - height;
+ break;
+ case RadarAnchorSW:
+ origin->x = position->x;
+ origin->y = position->y - height;
+ break;
+ case RadarAnchorW:
+ 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
+Origin2Anchor(RadarPoint *origin,
+ RadarDim width,
+ RadarDim height,
+ RadarAnchor anchor,
+ RadarPoint *position)
+{
+ switch (anchor) {
+ case RadarAnchorCenter:
+ position->x = origin->x + width/2;
+ position->y = origin->y + height/2;
+ break;
+ case RadarAnchorNW:
+ *position = *origin;
+ break;
+ case RadarAnchorN:
+ position->x = origin->x + width/2;
+ position->y = origin->y;
+ break;
+ case RadarAnchorNE:
+ position->x = origin->x + width;
+ position->y = origin->y;
+ break;
+ case RadarAnchorE:
+ position->x = origin->x + width;
+ position->y = origin->y + height/2;
+ break;
+ case RadarAnchorSE:
+ position->x = origin->x + width;
+ position->y = origin->y + height;
+ break;
+ case RadarAnchorS:
+ position->x = origin->x + width/2;
+ position->y = origin->y + height;
+ break;
+ case RadarAnchorSW:
+ position->x = origin->x;
+ position->y = origin->y + height;
+ break;
+ case RadarAnchorW:
+ position->x = origin->x;
+ position->y = origin->y + height/2;
+ break;
+ }
+}
+
+
+void
+BBox2XRect(RadarBBox *bbox,
+ XRectangle *r)
+{
+ r->x = REAL_TO_INT(bbox->orig.x);
+ r->y = REAL_TO_INT(bbox->orig.y);
+ r->width = REAL_TO_INT(bbox->corner.x) - r->x;
+ r->height = REAL_TO_INT(bbox->corner.y) - r->y;
+}
+
+
+void
+GetStringBBox(char *str,
+ RadarFont font,
+ RadarPos x,
+ RadarPos y,
+ RadarBBox *str_bbox)
+{
+ Tk_FontMetrics fm;
+
+ str_bbox->orig.x = x;
+ str_bbox->corner.x = x + RadarTextWidth(font, str, 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
+ResetBBox(RadarBBox *bbox)
+{
+ bbox->orig.x = bbox->orig.y = 0;
+ bbox->corner = bbox->orig;
+}
+
+
+void
+CopyBBox(RadarBBox *bbox_from,
+ RadarBBox *bbox_to)
+{
+ bbox_to->orig = bbox_from->orig;
+ bbox_to->corner = bbox_from->corner;
+}
+
+
+void
+IntersectBBox(RadarBBox *bbox1,
+ RadarBBox *bbox2,
+ RadarBBox *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)) {
+ ResetBBox(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);
+ }
+}
+
+
+RadarBool
+IsEmptyBBox(RadarBBox *bbox)
+{
+ return (bbox->orig.x >= bbox->corner.x) || (bbox->orig.y >= bbox->corner.y);
+}
+
+
+void
+AddBBoxToBBox(RadarBBox *bbox,
+ RadarBBox *bbox2)
+{
+ if (IsEmptyBBox(bbox2)) {
+ return;
+ }
+ if (IsEmptyBBox(bbox)) {
+ CopyBBox(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
+AddPointToBBox(RadarBBox *bbox,
+ RadarPos px,
+ RadarPos py)
+{
+ if (IsEmptyBBox(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
+AddPointsToBBox(RadarBBox *bbox,
+ RadarPoint *points,
+ int num_points)
+{
+ int x1, y1, x2, y2, cur;
+
+ if (points == NULL) {
+ return;
+ }
+
+ if (num_points == 0) {
+ return;
+ }
+
+ if (IsEmptyBBox(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;
+ bbox->corner.x = x2 + 1;
+ bbox->corner.y = y2 + 1;
+}
+
+
+void
+AddStringToBBox(RadarBBox *bbox,
+ char *str,
+ RadarFont font,
+ RadarPos cx,
+ RadarPos cy)
+{
+ RadarBBox str_bbox;
+
+ GetStringBBox(str, font, cx, cy, &str_bbox);
+ AddBBoxToBBox(bbox, &str_bbox);
+}
+
+RadarBool
+PointInBBox(RadarBBox *bbox,
+ RadarPos x,
+ RadarPos 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
+BBoxInBBox(RadarBBox *bbox1,
+ RadarBBox *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
+LineInBBox(RadarPoint *p1,
+ RadarPoint *p2,
+ RadarBBox *bbox)
+{
+ RadarBool p1_inside = PointInBBox(bbox, p1->x, p1->y);
+ RadarBool p2_inside = PointInBBox(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 {
+ double slope = ((double) p2->y - p1->y) / ((double) p2->x - p1->x);
+ int low, high, x, y;
+ int bbox_width = bbox->corner.x - bbox->orig.x;
+ int 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;
+}
+
+
+/*
+ * ShiftLine --
+ * 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
+ShiftLine(RadarPoint *p1,
+ RadarPoint *p2,
+ RadarReal dist,
+ RadarPoint *p3,
+ RadarPoint *p4)
+{
+ static int shift_table[129];
+ RadarBool dx_neg, dy_neg;
+ int dx, dy;
+
+ /*
+ * Initialize the conversion table.
+ */
+ if (shift_table[0] == 0) {
+ int i;
+ double 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 = p2->x - p1->x;
+ dy = 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 <= dx) {
+ dy = ((dist * shift_table[(dy*128)/dx]) + 64) / 128;
+ if (!dx_neg) {
+ dy = -dy;
+ }
+ p3->y += dy;
+ }
+ else {
+ dx = ((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.
+ */
+RadarBool
+IntersectLines(RadarPoint *a1,
+ RadarPoint *a2,
+ RadarPoint *b1,
+ RadarPoint *b2,
+ RadarPoint *pi)
+{
+ int 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 = -(-2*p + q)/(2*q); /*-(-p + q/2)/q;*/
+ }
+ else {
+ pi->x = (2*p + q)/(2*q); /*(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 = -(-2*p + q)/(2*q); /*-(-p + q/2)/q;*/
+ }
+ else {
+ pi->y = (2*p + q)/(2*q); /*(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
+InsetPolygon(RadarPoint *p,
+ int num_points,
+ RadarDim inset)
+{
+ RadarPoint *p1, *p2;
+ RadarPoint new_p1, new_p2;
+ RadarPoint shift1, shift2;
+ 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;
+ }
+
+ ShiftLine(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
+GetButtPoints(RadarPoint *p1,
+ RadarPoint *p2,
+ int width,
+ RadarBool projecting,
+ RadarPoint *c1,
+ RadarPoint *c2)
+{
+ double w_2 = width/2.0;
+ double length = hypot(p2->x - p1->x, p2->y - p1->y);
+ double 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 ;-)
+ */
+RadarBool
+GetMiterPoints(RadarPoint *p1,
+ RadarPoint *p2,
+ RadarPoint *p3,
+ int width,
+ RadarPoint *c1,
+ RadarPoint *c2)
+{
+ static double deg11 = (11.0*2.0*M_PI)/360.0;
+ double theta1; /* angle of p2-p1 segment. */
+ double theta2; /* angle of p2-p3 segment. */
+ double theta; /* angle of the joint */
+ double theta3; /* angle of bisector of the joint toward
+ * the external point of the joint. */
+ double dist; /* distance of the external points
+ * of the corner from the mid point
+ * p2. */
+ double 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
+PolylineInBBox(RadarPoint *points,
+ int num_points,
+ int width,
+ int cap_style,
+ int join_style,
+ RadarBBox *bbox)
+{
+ int count, inside = -1;
+ RadarBool do_miter_as_bevel;
+ RadarPoint 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 (OvalInBBox(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) {
+ GetButtPoints(&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 {
+ GetButtPoints(&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 (PolygonInBBox(poly, 4, bbox) != inside) {
+ return 0;
+ }
+ do_miter_as_bevel = False;
+ }
+ }
+
+ /*
+ * Opposite vertex of the edge.
+ */
+ if (count == 2) {
+ GetButtPoints(points, &points[1], width, cap_style == CapProjecting,
+ &poly[2], &poly[3]);
+ }
+ else if (join_style == JoinMiter) {
+ if (GetMiterPoints(points, &points[1], &points[2], width,
+ &poly[2], &poly[3]) == False) {
+ do_miter_as_bevel = True;
+ GetButtPoints(points, &points[1], width, 0, &poly[2], &poly[3]);
+ }
+ }
+ else {
+ GetButtPoints(points, &points[1], width, 0, &poly[2], &poly[3]);
+ }
+
+ if (PolygonInBBox(poly, 4, bbox) != inside) {
+ return 0;
+ }
+ }
+
+ /*
+ * Test a circle around the last point if CapRound.
+ */
+ if (cap_style == CapRound) {
+ if (OvalInBBox(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.
+ */
+int
+PolygonInBBox(RadarPoint *points,
+ int num_points,
+ RadarBBox *bbox)
+{
+ int inside, count;
+ RadarPoint *p, *head, *first, *second;
+ RadarBool closed;
+
+ p = head = points;
+ closed = True;
+ /*
+ * 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;
+ }
+
+ /*
+ * Get the status of the first edge.
+ */
+ inside = LineInBBox(&points[0], &points[1], bbox);
+ if (inside == 0) {
+ return 0;
+ }
+ for (count = num_points; 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 (LineInBBox(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 (PolygonToPointDist(points, num_points, &bbox->orig) <= 0.0) {
+ 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
+OvalInBBox(RadarPoint *center,
+ int width,
+ int height,
+ RadarBBox *bbox)
+{
+ RadarPoint origin, corner;
+ int w_2, h_2;
+ double 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.
+ */
+RadarBool
+PointInAngle(int start_angle,
+ int angle_extent,
+ RadarPoint *p)
+{
+ double 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 = (REAL_TO_INT(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)));
+}
+
+
+/*
+ * Return the distance of the given point to the rectangle
+ * described by rect. Return negative values for points in
+ * the rectangle.
+ */
+double
+RectangleToPointDist(RadarBBox *bbox,
+ RadarPoint *p)
+{
+ double new_dist, dist;
+ RadarPoint p1, p2;
+
+ p1.x = bbox->orig.x;
+ p1.y = p2.y = bbox->orig.y;
+ p2.x = bbox->corner.x;
+ dist = LineToPointDist(&p1, &p2, p);
+ if (dist == 0.0) {
+ return 0.0;
+ }
+
+ p1 = p2;
+ p2.y = bbox->corner.y;
+ new_dist = LineToPointDist(&p1, &p2, p);
+ dist = MIN(dist, new_dist);
+ if (dist == 0.0) {
+ return 0.0;
+ }
+
+ p1 = p2;
+ p2.x = bbox->orig.x;
+ new_dist = LineToPointDist(&p1, &p2, p);
+ dist = MIN(dist, new_dist);
+ if (dist == 0.0) {
+ return 0.0;
+ }
+
+ p1 = p2;
+ p2.y = bbox->orig.y;
+ new_dist = LineToPointDist(&p1, &p2, p);
+ dist = MIN(dist, new_dist);
+
+ if (PointInBBox(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>..
+ */
+double
+LineToPointDist(RadarPoint *p1,
+ RadarPoint *p2,
+ RadarPoint *p)
+{
+ double x, y;
+ int 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 {
+ double a1, a2, b1, b2;
+
+ a1 = ((double) (p2->y - p1->y)) / ((double) (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;
+ }
+ }
+ }
+
+ /* 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.
+ */
+double
+PolygonToPointDist(RadarPoint *points,
+ int num_points,
+ RadarPoint *p)
+{
+ double best_distance;
+ int intersections;
+ int x_int, y_int;
+ RadarPoint *first_point;
+ double x, y, dist;
+ RadarPoint 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 = 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 edge */
+ 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);
+ if ((p->y < y) && (p->x < p1.x) && (p->x >= p2.x)) {
+ intersections++;
+ }
+ }
+ else {
+ x_int = MIN(p2.x, p->x);
+ x_int = MAX(x_int, p1.x);
+ if ((p->y < y) && (p->x < p2.x) && (p->x >= p1.x)) {
+ intersections++;
+ }
+ }
+ x = x_int;
+ }
+
+ /* Other */
+ else {
+ double a1, b1, a2, b2;
+
+ a1 = ((double) (p2.y - p1.y)) / ((double) (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.
+ */
+double
+PolylineToPointDist(RadarPoint *points,
+ int num_points,
+ int width,
+ int cap_style,
+ int join_style,
+ RadarPoint *p)
+{
+ RadarBool miter2bevel = False;
+ int count;
+ RadarPoint *ptr;
+ RadarPoint outline[5];
+ double 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) {
+ GetButtPoints(&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 {
+ GetButtPoints(&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 = PolygonToPointDist(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) {
+ GetButtPoints(ptr, &ptr[1], width, cap_style==CapProjecting,
+ &outline[2], &outline[3]);
+ }
+ else if (join_style == JoinMiter) {
+ if (GetMiterPoints(ptr, &ptr[1], &ptr[2], width,
+ &outline[2], &outline[3]) == False) {
+ miter2bevel = True;
+ GetButtPoints(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 {
+ GetButtPoints(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 = PolygonToPointDist(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.
+ */
+double
+OvalToPointDist(RadarPoint *center,
+ int width,
+ int height,
+ unsigned int line_width,
+ RadarPoint *p)
+{
+ double x_delta, y_delta;
+ /* double x_diameter, y_diameter;*/
+ double scaled_distance;
+ double distance_to_outline;
+ double 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 = ((double) (width - line_width)) / 2;
+ else
+ distance_to_outline = ((double) (height - line_width)) / 2;
+ }
+
+ if (distance_to_outline < 0.0)
+ return 0.0;
+ else
+ return -distance_to_outline;
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * GetBezierPoints --
+ * 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.
+ *
+ **********************************************************************************
+ */
+static void
+GetBezierPoints(RadarPoint *controls,
+ RadarList to_points,
+ double eps)
+{
+ RadarReal dist2;
+ RadarPoint mid_segm, mid_cord, delta;
+
+ /*
+ * Compute distance between cord center and curve at t = 0.5
+ */
+ mid_segm.x = (controls[0].x + 3*controls[1].x + 3*controls[2].x + controls[3].x) / 8.0;
+ mid_segm.y = (controls[0].y + 3*controls[1].y + 3*controls[2].y + controls[3].y) / 8.0;
+ mid_cord.x = (controls[0].x + controls[3].x) / 2.0;
+ mid_cord.y = (controls[0].y + controls[3].y) / 2.0;
+ delta.x = mid_segm.x - mid_cord.x;
+ delta.y = mid_segm.y - mid_cord.y;
+ dist2 = delta.x*delta.x + delta.y*delta.y;
+
+ if (dist2 > eps) {
+ RadarPoint new_controls[4];
+ /*
+ * Subdivide the curve at t = 0.5
+ * and compute each new curve.
+ */
+ new_controls[0] = controls[0];
+ new_controls[1].x = (controls[0].x + controls[1].x) / 2.0;
+ new_controls[1].y = (controls[0].y + controls[1].y) / 2.0;
+ new_controls[2].x = (controls[0].x + 2*controls[1].x + controls[2].x) / 4.0;
+ new_controls[2].y = (controls[0].y + 2*controls[1].y + controls[2].y) / 4.0;
+ new_controls[3] = mid_segm;
+ GetBezierPoints(new_controls, to_points, eps);
+
+ new_controls[0] = mid_segm;
+ new_controls[1].x = (controls[1].x + 2*controls[2].x + controls[3].x) / 4.0;
+ new_controls[1].y = (controls[1].y + 2*controls[2].y + controls[3].y) / 4.0;
+ new_controls[2].x = (controls[2].x + (controls[3].x)) / 2.0;
+ new_controls[2].y = (controls[2].y + (controls[3].y)) / 2.0;
+ new_controls[3] = controls[3];
+ GetBezierPoints(new_controls, to_points, eps);
+ }
+ else {
+ /*
+ * Flat enough add the end to the current path.
+ * The start should already be there.
+ */
+ RadarListAdd(to_points, &controls[3], RadarListTail);
+ }
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * GetBezierPath --
+ * 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
+GetBezierPath(RadarList from_points,
+ RadarList to_points)
+{
+ RadarPoint *fp;
+ int num_fp, i;
+ RadarPoint s[4];
+
+ fp = (RadarPoint *) RadarListArray(from_points);
+ num_fp = RadarListSize(from_points);
+
+ /*
+ * make sure the output vector is empty, then add the first point.
+ */
+ RadarListEmpty(to_points);
+ RadarListAdd(to_points, &fp[0], RadarListTail);
+
+ for (i = 0; i < num_fp; ) {
+ if (i < (num_fp-3)) {
+ GetBezierPoints(fp, to_points, 1.0);
+ fp += 3;
+ i += 3;
+ }
+ else if (i == (num_fp-3)) {
+ s[0] = fp[0];
+ s[1] = s[2] = fp[1];
+ s[3] = fp[2];
+ GetBezierPoints(s, to_points, 1.0);
+ break;
+ }
+ else if (i == (num_fp-2)) {
+ RadarListAdd(to_points, &fp[1], RadarListTail);
+ break;
+ }
+ }
+}
+
+/*
+ **********************************************************************************
+ *
+ * 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
+SmoothPathWithBezier(RadarList from_points,
+ RadarList to_points)
+{
+ RadarPoint *fp;
+ int num_fp;
+ RadarBool closed;
+ RadarPoint s[4];
+ int i;
+
+ fp = (RadarPoint *) RadarListArray(from_points);
+ num_fp = RadarListSize(from_points);
+
+ /*
+ * make sure the output vector is empty
+ */
+ RadarListEmpty(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 = REAL_TO_INT(0.5*fp[num_fp-2].x + 0.5*fp[0].x);
+ s[0].y = REAL_TO_INT(0.5*fp[num_fp-2].y + 0.5*fp[0].y);
+ s[1].x = REAL_TO_INT(0.167*fp[num_fp-2].x + 0.833*fp[0].x);
+ s[1].y = REAL_TO_INT(0.167*fp[num_fp-2].y + 0.833*fp[0].y);
+ s[2].x = REAL_TO_INT(0.833*fp[0].x + 0.167*fp[1].x);
+ s[2].y = REAL_TO_INT(0.833*fp[0].y + 0.167*fp[1].y);
+ s[3].x = REAL_TO_INT(0.5*fp[0].x + 0.5*fp[1].x);
+ s[3].y = REAL_TO_INT(0.5*fp[0].y + 0.5*fp[1].y);
+ RadarListAdd(to_points, s, RadarListTail);
+ GetBezierPoints(s, to_points, 1.0);
+ }
+ else {
+ closed = False;
+ RadarListAdd(to_points, &fp[0], RadarListTail);
+ }
+
+ 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 = REAL_TO_INT(0.333*fp[0].x + 0.667*fp[1].x);
+ s[1].y = REAL_TO_INT(0.333*fp[0].y + 0.667*fp[1].y);
+ }
+ else {
+ s[0].x = REAL_TO_INT(0.5*fp[0].x + 0.5*fp[1].x);
+ s[0].y = REAL_TO_INT(0.5*fp[0].y + 0.5*fp[1].y);
+ s[1].x = REAL_TO_INT(0.167*fp[0].x + 0.833*fp[1].x);
+ s[1].y = REAL_TO_INT(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 = REAL_TO_INT(0.667*fp[1].x + 0.333*fp[2].x);
+ s[2].y = REAL_TO_INT(0.667*fp[1].y + 0.333*fp[2].y);
+ s[3] = fp[2];
+ }
+ else {
+ s[2].x = REAL_TO_INT(0.833*fp[1].x + 0.167*fp[2].x);
+ s[2].y = REAL_TO_INT(0.833*fp[1].y + 0.167*fp[2].y);
+ s[3].x = REAL_TO_INT(0.5*fp[1].x + 0.5*fp[2].x);
+ s[3].y = REAL_TO_INT(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))) {
+ RadarListAdd(to_points, &s[3], RadarListTail);
+ }
+ else {
+ GetBezierPoints(s, to_points, 1.0);
+ }
+ }
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * GetLineEnd --
+ * Compute the points describing the given line end style at point p1 for
+ * the line p1,p2. Point p1 is adjusted to fit the line end.
+ * If bbox is non null, it is filled with the bounding box of the end.
+ *
+ * For the time being this procedure handles open/filled arrows.
+ *
+ * Here are the parameters describing arrows.
+ *
+ * * | ARROW_SHAPE_C
+ * ** |
+ * * ***************************
+ * * *
+ * * * +p1 +p2
+ * | * |*
+ * | * ***************************
+ * | | **
+ * | | *
+ * | | |
+ * |---| | ARROW_SHAPE_A
+ * | |
+ * |-------| ARROW_SHAPE_B
+ *
+ **********************************************************************************
+ */
+void
+GetLineEnd(RadarPoint *p1,
+ RadarPoint *p2,
+ unsigned int line_width,
+ int cap_style,
+ LineEnd end_style,
+ RadarPoint *points)
+{
+ RadarReal dx, dy, length, temp, backup;
+ RadarReal frac_height, sin_theta, cos_theta;
+ RadarReal vert_x, vert_y;
+ RadarReal shape_a, shape_b, shape_c;
+
+ if (end_style != NULL) {
+ shape_a = end_style->shape_a + 0.001;
+ shape_b = end_style->shape_b + 0.001;
+ shape_c = end_style->shape_c + line_width/2.0 + 0.001;
+
+ frac_height = (line_width/2.0) / shape_c;
+ dx = p1->x - p2->x;
+ dy = p1->y - p2->y;
+ length = hypot(dx, dy);
+ if (length == 0) {
+ sin_theta = cos_theta = 0.0;
+ }
+ else {
+ sin_theta = dy/length;
+ cos_theta = dx/length;
+ }
+
+ if (cap_style != CapProjecting) {
+ temp = frac_height;
+ }
+ else {
+ temp = line_width / shape_c;
+ }
+ backup = temp * shape_b + shape_a * (1.0 - temp) / 2.0;
+ points[0].x = points[5].x = p1->x + backup * cos_theta;
+ points[0].y = points[5].y = p1->y + backup * sin_theta;
+
+ vert_x = points[0].x - shape_a*cos_theta;
+ vert_y = points[0].y - shape_a*sin_theta;
+ temp = shape_c*sin_theta;
+ points[1].x = REAL_TO_INT(points[0].x - shape_b*cos_theta + temp);
+ points[4].x = REAL_TO_INT(points[1].x - 2*temp);
+ temp = shape_c*cos_theta;
+ points[1].y = REAL_TO_INT(points[0].y - shape_b*sin_theta - temp);
+ points[4].y = REAL_TO_INT(points[1].y + 2*temp);
+ points[2].x = REAL_TO_INT(points[1].x*frac_height + vert_x*(1.0-frac_height));
+ points[2].y = REAL_TO_INT(points[1].y*frac_height + vert_y*(1.0-frac_height));
+ points[3].x = REAL_TO_INT(points[4].x*frac_height + vert_x*(1.0-frac_height));
+ points[3].y = REAL_TO_INT(points[4].y*frac_height + vert_y*(1.0-frac_height));
+ }
+}