X-Authentication-Warning: delorie.com: mail set sender to geda-user-bounces using -f X-Recipient: geda-user AT delorie DOT com X-Cam-AntiVirus: no malware found X-Cam-ScannerInfo: http://www.cam.ac.uk/cs/email/scanner/ Message-ID: <1402630167.26608.5.camel@pcjc2lap> Subject: [geda-user] First cut at file-format (and rendering) support for arc-segments in polygon contours From: Peter Clifton To: geda-user AT delorie DOT com Date: Fri, 13 Jun 2014 04:29:27 +0100 Organization: Clifton Electronics Content-Type: multipart/mixed; boundary="=-JNhRtckRCiqUODnUc5lN" X-Mailer: Evolution 3.10.4-0ubuntu1 Mime-Version: 1.0 Sender: "Peter C.J. Clifton" Reply-To: geda-user AT delorie DOT com --=-JNhRtckRCiqUODnUc5lN Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 7bit Very first cut test... missing the following: Save support File format version bump Sanity checking of new file syntax - somewhat different to our usual Correct bounds update of the polygon, taking into account possible protrusion of arc-segments Rendering the arc segments when dragging the polygon Ability to edit / create these radiused features within PCB Semantic tagging of the contours to allow STEP / gerber / IDF to use the true contour Sane encapsulation of the data (right now, it adds to the PointType structure, because I was being lazy) No checks for production of self-intersecting contours (these would go in the edit tool though). -- Peter Clifton Clifton Electronics --=-JNhRtckRCiqUODnUc5lN Content-Disposition: attachment; filename*0=0001-Add-support-for-arc-segments-in-polygon-object-outli.pat; filename*1=ch Content-Type: text/x-patch; name="0001-Add-support-for-arc-segments-in-polygon-object-outli.patch"; charset="UTF-8" Content-Transfer-Encoding: 7bit From 10f361d8421698208c3e877924b1f92faf477278 Mon Sep 17 00:00:00 2001 From: Peter Clifton Date: Fri, 13 Jun 2014 01:22:11 +0100 Subject: [PATCH] Add support for arc segments in polygon object outline definitions Test with a polygon like this: Polygon("clearpoly") ( [5.0000mm 7.0000mm included_angle: 90] [7.0000mm 5.0000mm] [28.000mm 5.0000mm included_angle: 90] [30.000mm 7.0000mm] [30.000mm 28.000mm included_angle: 90] [28.000mm 30.000mm] [7.0000mm 30.000mm included_angle: 90] [5.0000mm 28.000mm] ) NOTE THAT THE ARC BOUNDS ARE NOT YET INCLUDED IN THE BOUNDING BOX CALCULATION, SO THIS WILL BREAK VARIOUS THINGS PRETTY HORRIBLY IF AN ARC PROTRUDES AS AN EXTREME FEATURE IN EITHER THE +/-X or +/-Y DIRECTIONS. --- src/action.c | 6 ++- src/copy.c | 2 +- src/create.c | 11 +++-- src/create.h | 2 +- src/global.h | 2 + src/hid/common/hidgl.c | 2 + src/insert.c | 2 +- src/parse_l.l | 1 + src/parse_y.y | 10 ++-- src/polygon.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++-- src/polygon.h | 17 +++++++ 11 files changed, 168 insertions(+), 18 deletions(-) diff --git a/src/action.c b/src/action.c index 5d2b4df..184cefa 100644 --- a/src/action.c +++ b/src/action.c @@ -1428,7 +1428,8 @@ NotifyMode (void) { CreateNewPointInPolygon (&Crosshair.AttachedPolygon, Crosshair.AttachedLine.Point2.X, - Crosshair.AttachedLine.Point2.Y); + Crosshair.AttachedLine.Point2.Y, + 0); /* copy the coordinates */ Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X; @@ -1519,7 +1520,8 @@ NotifyMode (void) { CreateNewPointInPolygon (&Crosshair.AttachedPolygon, Crosshair.AttachedLine.Point2.X, - Crosshair.AttachedLine.Point2.Y); + Crosshair.AttachedLine.Point2.Y, + 0); /* copy the coordinates */ Crosshair.AttachedLine.Point1.X = Crosshair.AttachedLine.Point2.X; diff --git a/src/copy.c b/src/copy.c index b0cd9c8..a580e8e 100644 --- a/src/copy.c +++ b/src/copy.c @@ -101,7 +101,7 @@ CopyPolygonLowLevel (PolygonType *Dest, PolygonType *Src) CreateNewHoleInPolygon (Dest); hole++; } - CreateNewPointInPolygon (Dest, Src->Points[n].X, Src->Points[n].Y); + CreateNewPointInPolygon (Dest, Src->Points[n].X, Src->Points[n].Y, Src->Points[n].included_angle); } SetPolygonBoundingBox (Dest); Dest->Flags = Src->Flags; diff --git a/src/create.c b/src/create.c index a1c8867..64a22ec 100644 --- a/src/create.c +++ b/src/create.c @@ -576,10 +576,10 @@ CreateNewPolygonFromRectangle (LayerType *Layer, if (!polygon) return (polygon); - CreateNewPointInPolygon (polygon, X1, Y1); - CreateNewPointInPolygon (polygon, X2, Y1); - CreateNewPointInPolygon (polygon, X2, Y2); - CreateNewPointInPolygon (polygon, X1, Y2); + CreateNewPointInPolygon (polygon, X1, Y1, 0); + CreateNewPointInPolygon (polygon, X2, Y1, 0); + CreateNewPointInPolygon (polygon, X2, Y2, 0); + CreateNewPointInPolygon (polygon, X1, Y2, 0); SetPolygonBoundingBox (polygon); if (!Layer->polygon_tree) Layer->polygon_tree = r_create_tree (NULL, 0, 0); @@ -644,13 +644,14 @@ CreateNewPolygon (LayerType *Layer, FlagType Flags) * creates a new point in a polygon */ PointType * -CreateNewPointInPolygon (PolygonType *Polygon, Coord X, Coord Y) +CreateNewPointInPolygon (PolygonType *Polygon, Coord X, Coord Y, Angle included_angle) { PointType *point = GetPointMemoryInPolygon (Polygon); /* copy values */ point->X = X; point->Y = Y; + point->included_angle = included_angle; point->ID = ID++; return (point); } diff --git a/src/create.h b/src/create.h index fa49a1b..5b6cfdd 100644 --- a/src/create.h +++ b/src/create.h @@ -51,7 +51,7 @@ ArcType * CreateNewArcOnLayer (LayerType *, Coord, Coord, Coord, Coord, Angle, A PolygonType * CreateNewPolygonFromRectangle (LayerType *, Coord, Coord, Coord, Coord, FlagType); TextType * CreateNewText (LayerType *, FontType *, Coord, Coord, unsigned, int, char *, FlagType); PolygonType * CreateNewPolygon (LayerType *, FlagType); -PointType * CreateNewPointInPolygon (PolygonType *, Coord, Coord); +PointType * CreateNewPointInPolygon (PolygonType *, Coord, Coord, Angle included_angle); PolygonType * CreateNewHoleInPolygon (PolygonType *polygon); void RefdesMapInit (void); ElementType * CreateNewElement (DataType *, FontType *, FlagType, char *, char *, char *, Coord, Coord, BYTE, int, FlagType, bool); diff --git a/src/global.h b/src/global.h index 8a0d6ee..6e4bd9c 100644 --- a/src/global.h +++ b/src/global.h @@ -236,8 +236,10 @@ typedef struct /* a line/polygon point */ { Coord X, Y, X2, Y2; /* so Point type can be cast as BoxType */ long int ID; + Angle included_angle; } PointType; + /* Lines, rats, pads, etc. */ typedef struct { ANYLINEFIELDS; diff --git a/src/hid/common/hidgl.c b/src/hid/common/hidgl.c index 0c4ac20..559365c 100644 --- a/src/hid/common/hidgl.c +++ b/src/hid/common/hidgl.c @@ -749,10 +749,12 @@ fill_contour (PLINE *contour) borast_traps_t traps; /* If the contour is round, then call hidgl_fill_circle to draw it. */ +#if 0 if (contour->is_round) { hidgl_fill_circle (contour->cx, contour->cy, contour->radius); return; } +#endif /* If we don't have a cached set of tri-strips, compute them */ if (contour->tristrip_vertices == NULL) { diff --git a/src/insert.c b/src/insert.c index a254998..8868d8b 100644 --- a/src/insert.c +++ b/src/insert.c @@ -180,7 +180,7 @@ InsertPointIntoPolygon (LayerType *Layer, PolygonType *Polygon) */ ErasePolygon (Polygon); r_delete_entry (Layer->polygon_tree, (BoxType *) Polygon); - save = *CreateNewPointInPolygon (Polygon, InsertX, InsertY); + save = *CreateNewPointInPolygon (Polygon, InsertX, InsertY, 0); for (n = Polygon->PointN - 1; n > InsertAt; n--) Polygon->Points[n] = Polygon->Points[n - 1]; diff --git a/src/parse_l.l b/src/parse_l.l index 4f64880..a4e0a05 100644 --- a/src/parse_l.l +++ b/src/parse_l.l @@ -123,6 +123,7 @@ Groups { return(T_GROUPS); } Styles { return(T_STYLES); } Polygon { return(T_POLYGON); } Hole { return(T_POLYGON_HOLE); } +included_angle: { return(T_INCLUDED_ANGLE); } Arc { return(T_ARC); } NetList { return(T_NETLIST); } Net { return(T_NET); } diff --git a/src/parse_y.y b/src/parse_y.y index 6cd4c7b..8e2225c 100644 --- a/src/parse_y.y +++ b/src/parse_y.y @@ -119,7 +119,7 @@ static Coord new_units (PLMeasure m); %token T_FILEVERSION T_PCB T_LAYER T_VIA T_RAT T_LINE T_ARC T_RECTANGLE T_TEXT T_ELEMENTLINE %token T_ELEMENT T_PIN T_PAD T_GRID T_FLAGS T_SYMBOL T_SYMBOLLINE T_CURSOR -%token T_ELEMENTARC T_MARK T_GROUPS T_STYLES T_POLYGON T_POLYGON_HOLE T_NETLIST T_NET T_CONN +%token T_ELEMENTARC T_MARK T_GROUPS T_STYLES T_POLYGON T_POLYGON_HOLE T_INCLUDED_ANGLE T_NETLIST T_NET T_CONN %token T_AREA T_THERMAL T_DRC T_ATTRIBUTE %token T_UMIL T_CMIL T_MIL T_IN T_NM T_UM T_MM T_M T_KM T_PX %type symbolid @@ -1184,11 +1184,15 @@ polygonpoint /* xcoord ycoord */ : '(' measure measure ')' { - CreateNewPointInPolygon(Polygon, OU ($2), OU ($3)); + CreateNewPointInPolygon(Polygon, OU ($2), OU ($3), 0); } | '[' measure measure ']' { - CreateNewPointInPolygon(Polygon, NU ($2), NU ($3)); + CreateNewPointInPolygon(Polygon, NU ($2), NU ($3), 0); + } + | '[' measure measure T_INCLUDED_ANGLE number ']' + { + CreateNewPointInPolygon(Polygon, NU ($2), NU ($3), $5); } ; diff --git a/src/polygon.c b/src/polygon.c index 4623b52..13aac8e 100644 --- a/src/polygon.c +++ b/src/polygon.c @@ -111,6 +111,7 @@ dicer output is used for HIDs which cannot render things with holes #define SUBTRACT_LINE_BATCH_SIZE 20 static double rotate_circle_seg[4]; +static double bw_rotate_circle_seg[4]; void polygon_init (void) @@ -120,6 +121,9 @@ polygon_init (void) rotate_circle_seg[0] = cos_ang; rotate_circle_seg[1] = -sin_ang; rotate_circle_seg[2] = sin_ang; rotate_circle_seg[3] = cos_ang; + + bw_rotate_circle_seg[0] = cos_ang; bw_rotate_circle_seg[1] = sin_ang; + bw_rotate_circle_seg[2] = -sin_ang; bw_rotate_circle_seg[3] = cos_ang; } Cardinal @@ -273,6 +277,51 @@ ContourToPoly (PLINE * contour) return p; } +static void +degree_circle (PLINE * c, Coord X, Coord Y /* <- Center */, Vector v /* First point, already laid by caller */, Angle sweep) +{ + /* We don't re-add a point at v, nor do we add the last point, sweep degrees around from (X,Y)-v */ + double e1, e2, t1; + int i, range; + +// poly_InclVertex (c->head.prev, poly_CreateNode (v)); + + /* move vector to origin */ + e1 = (v[0] - X) * POLY_CIRC_RADIUS_ADJ; + e2 = (v[1] - Y) * POLY_CIRC_RADIUS_ADJ; + + if (sweep > 0) + { + /* NB: the caller added the first vertex, and will add the last vertex, hence the -1 */ + range = POLY_CIRC_SEGS * sweep / 360 - 1; + for (i = 0; i < range; i++) + { + /* rotate the vector */ + t1 = rotate_circle_seg[0] * e1 + rotate_circle_seg[1] * e2; + e2 = rotate_circle_seg[2] * e1 + rotate_circle_seg[3] * e2; + e1 = t1; + v[0] = X + ROUND (e1); + v[1] = Y + ROUND (e2); + poly_InclVertex (c->head.prev, poly_CreateNode (v)); + } + } + else + { + /* NB: the caller added the first vertex, and will add the last vertex, hence the -1 */ + range = POLY_CIRC_SEGS * -sweep / 360 - 1; + for (i = 0; i < range; i++) + { + /* rotate the vector */ + t1 = bw_rotate_circle_seg[0] * e1 + bw_rotate_circle_seg[1] * e2; + e2 = bw_rotate_circle_seg[2] * e1 + bw_rotate_circle_seg[3] * e2; + e1 = t1; + v[0] = X + ROUND (e1); + v[1] = Y + ROUND (e2); + poly_InclVertex (c->head.prev, poly_CreateNode (v)); + } + } +} + static POLYAREA * original_poly (PolygonType * p) { @@ -291,8 +340,7 @@ original_poly (PolygonType * p) /* No current contour? Make a new one starting at point */ /* (or) Add point to existing contour */ - v[0] = p->Points[n].X; - v[1] = p->Points[n].Y; + v[0] = p->Points[n].X, v[1] = p->Points[n].Y; if (contour == NULL) { if ((contour = poly_NewContour (v)) == NULL) @@ -303,6 +351,77 @@ original_poly (PolygonType * p) poly_InclVertex (contour->head.prev, poly_CreateNode (v)); } + if (p->Points[n].included_angle != 0) + { + Cardinal next_n; + Coord px, py; + Coord nx, ny; + Coord hx, hy; + Coord cx, cy; + double p_to_h_dist; + double c_to_h_dist; + double unit_hcx, unit_hcy; + + next_n = n + 1; + if (next_n == p->PointN || + (hole < p->HoleIndexN && next_n == p->HoleIndex[hole])) + next_n = (hole == 0) ? 0 : p->HoleIndex[hole - 1]; + + /* XXX: Compute center of arc */ + + px = p->Points[ n].X, py = p->Points[ n].Y; + nx = p->Points[next_n].X, ny = p->Points[next_n].Y; + + /* Find the point halfway between the to points the arc spans */ + hx = px + (nx - px) / 2; + hy = py + (ny - py) / 2; + + /* The arc center lies on a line passing through hx, hy, perpendicular + * to the direction between our two end-points. + * + * n + * / | + * / |h + * -----c----|-------------- line passing (hx, hy), perpendicular to p[n]-p[next_n] + * \ | + * \ | + * p + * + * Find cx, cy. + * + * We know that c-p[n] = radius. (But we don't know that radius). + * We have the included angle, /_ p[n].c.p[next_n] + * |(hx,hy)-p[n]| = sin(angle/2) * radius + * + * tan(ang/2) = |(hx,hy)-p[n]| / |(hx,hy)-(cx,cy)| + * + * |(hx,hy)-(cx,cy)| = |(hx,hy)-p[n]| / tan(ang/2) + * + */ + + p_to_h_dist = sqrt (pow(nx - py, 2) + pow (ny - py, 2)) / 2.; + c_to_h_dist = p_to_h_dist / tan (TO_RADIANS (p->Points[n].included_angle) / 2.); + + unit_hcx = (float)-(hy - py) / p_to_h_dist; + unit_hcy = (float)(hx - px) / p_to_h_dist; + + cx = hx + unit_hcx * c_to_h_dist; + cy = hy + unit_hcy * c_to_h_dist; + +#if 0 /* DEBUG TO SHOW THE CENTER OF THE ARC */ + v[0] = cx, v[1] = cy; + poly_InclVertex (contour->head.prev, poly_CreateNode (v)); + v[0] = p->Points[n].X, v[1] = p->Points[n].Y; +#endif + + degree_circle (contour, cx, cy, v, p->Points[n].included_angle); + +#if 0 /* DEBUG TO SHOW THE CENTER OF THE ARC */ + v[0] = cx, v[1] = cy; /* DEBUG TO SHOW THE CENTER OF THE ARC */ + poly_InclVertex (contour->head.prev, poly_CreateNode (v)); +#endif + } + /* Is current point last in contour? If so process it. */ if (n == p->PointN - 1 || (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1)) @@ -419,6 +538,7 @@ frac_circle (PLINE * c, Coord X, Coord Y, Vector v, int fraction) } } + /* create a circle approximation from lines */ POLYAREA * CirclePoly (Coord x, Coord y, Coord radius) @@ -1837,9 +1957,9 @@ MorphPolygon (LayerType *layer, PolygonType *poly) return false; many = true; v = &p->contours->head; - CreateNewPointInPolygon (newone, v->point[0], v->point[1]); + CreateNewPointInPolygon (newone, v->point[0], v->point[1], 0); for (v = v->next; v != &p->contours->head; v = v->next) - CreateNewPointInPolygon (newone, v->point[0], v->point[1]); + CreateNewPointInPolygon (newone, v->point[0], v->point[1], 0); newone->BoundingBox.X1 = p->contours->xmin; newone->BoundingBox.X2 = p->contours->xmax + 1; newone->BoundingBox.Y1 = p->contours->ymin; @@ -1951,7 +2071,8 @@ PolyToPolygonsOnLayer (DataType *Destination, LayerType *Layer, do { CreateNewPointInPolygon (Polygon, node->point[0], - node->point[1]); + node->point[1], + 0); } while ((node = node->next) != &pline->head); diff --git a/src/polygon.h b/src/polygon.h index a02fd99..369efea 100644 --- a/src/polygon.h +++ b/src/polygon.h @@ -37,11 +37,28 @@ #define POLY_CIRC_SEGS 40 #define POLY_CIRC_SEGS_F ((float)POLY_CIRC_SEGS) +#if 0 +/* THIS IS BROKEN: + * + * IT BREAKS THE CIRCULARITY OF CIRULAR CONTORS, AS THE FIRST + * FIRST VERTEX ADDED BY CirclePoly IS NOT RADIUS ADJUSTED. + * + * IT BREAKS CIRCULARITY OF ALIGMENT BETWEEN A LINE AND ITS END-CAPS, + * LEADING TO MORE COMPLEX CONTOURS FOR COMMON LINE-LINE INTERSECTIONS, + * SUCH AS 90 AND 45 DEGREE ANGLES + * + * IT WAS INTENDED TO AVOID DRC ERRORS WITH "TOO-CLOSE" FEATURES, + * BUT COULD OTHERWISE CAUSE THEM FOR "TOO THIN" FEATURES - INSIDE/OUTSIDE + * CONTOUR APPROXIMATION NEEDS TO BE CONTROLED DEPENDING ON THE REQUIREMENT + */ /* adjustment to make the segments outline the circle rather than connect * points on the circle: 1 - cos (\alpha / 2) < (\alpha / 2) ^ 2 / 2 */ #define POLY_CIRC_RADIUS_ADJ (1.0 + M_PI / POLY_CIRC_SEGS_F * \ M_PI / POLY_CIRC_SEGS_F / 2.0) +#else +#define POLY_CIRC_RADIUS_ADJ 1.0 +#endif /* polygon diverges from modelled arc no more than MAX_ARC_DEVIATION * thick */ #define POLY_ARC_MAX_DEVIATION 0.02 -- 1.9.1 --=-JNhRtckRCiqUODnUc5lN--