polystitch.c

00001 /* PolyStitch plug-in for PCB
00002    http://www.delorie.com/pcb/polystitch.c
00003 
00004    Copyright (C) 2010 DJ Delorie <dj@delorie.com>
00005 
00006    Licensed under the terms of the GNU General Public License, version
00007    2 or later.
00008 
00009    Compile like this:
00010 
00011    gcc -I$HOME/geda/pcb-cvs/src -I$HOME/geda/pcb-cvs -O2 -shared polystitch.c -o polystitch.so
00012 
00013    The resulting polystitch.so goes in $HOME/.pcb/plugins/polystitch.so.
00014 
00015    Usage: PolyStitch()
00016 
00017    The polygon under the cursor (based on closest-corner) is stitched
00018    together with the polygon surrounding it on the same layer.  Use
00019    with pstoedit conversions where there's a "hole" in the shape -
00020    select the hole.
00021 
00022 */
00023 
00024 #include <stdio.h>
00025 #include <math.h>
00026 
00027 #include "global.h"
00028 #include "data.h"
00029 #include "macro.h"
00030 #include "create.h"
00031 #include "remove.h"
00032 #include "hid.h"
00033 #include "error.h"
00034 #include "rtree.h"
00035 #include "draw.h"
00036 #include "set.h"
00037 #include "polygon.h"
00038 #include "misc.h"
00039 
00040 static PolygonType *inner_poly, *outer_poly;
00041 static LayerType *poly_layer;
00042 
00043 static double
00044 ATAN2 (PointType a, PointType b)
00045 {
00046   if (a.X == b.X && a.Y == b.Y)
00047     return 0;
00048   return atan2 ((double)b.Y - a.Y, (double)b.X - a.X);
00049 }
00050 
00051 static double
00052 poly_winding (PolygonType *poly)
00053 {
00054   double winding, turn;
00055   double prev_angle, this_angle;
00056   int i, n;
00057 
00058   winding = 0;
00059 
00060   prev_angle = ATAN2(poly->Points[0], poly->Points[1]);
00061   n = poly->PointN;
00062   for (i=1; i<=n; i++)
00063     {
00064       this_angle = ATAN2 (poly->Points[i%n], poly->Points[(i+1)%n]);
00065       turn = this_angle - prev_angle;
00066       if (turn < -M_PI) turn += 2*M_PI;
00067       if (turn >  M_PI) turn -= 2*M_PI;
00068       winding += turn;
00069       prev_angle = this_angle;
00070     }
00071   return winding;
00072 }
00073 
00074 /* Given the X,Y, find the polygon and set inner_poly and poly_layer.  */
00075 static void
00076 find_crosshair_poly (int x, int y)
00077 {
00078   double best = 0, dist;
00079 
00080   inner_poly = NULL;
00081   poly_layer = NULL;
00082   
00083   VISIBLEPOLYGON_LOOP (PCB->Data);
00084   {
00085     /* layer, polygon */
00086     POLYGONPOINT_LOOP (polygon);
00087     {
00088       /* point */
00089       int dx = x - point->X;
00090       int dy = y - point->Y;
00091       dist = (double)dx*dx + (double)dy*dy;
00092       if (dist < best || inner_poly == NULL)
00093         {
00094           inner_poly = polygon;
00095           poly_layer = layer;
00096           best = dist;
00097         }
00098     }
00099     END_LOOP;
00100   }
00101   ENDALL_LOOP;
00102   if (!inner_poly)
00103     {
00104       Message("Cannot find any polygons");
00105       return;
00106     }
00107 }
00108 
00109 /* Set outer_poly to the enclosing poly.  We assume there's only
00110    one.  */
00111 static void
00112 find_enclosing_poly ()
00113 {
00114   outer_poly = NULL;
00115 
00116   POLYGON_LOOP (poly_layer);
00117   {
00118     if (polygon == inner_poly)
00119       continue;
00120     if (polygon->BoundingBox.X1 <= inner_poly->BoundingBox.X1
00121         && polygon->BoundingBox.X2 >= inner_poly->BoundingBox.X2
00122         && polygon->BoundingBox.Y1 <= inner_poly->BoundingBox.Y1
00123         && polygon->BoundingBox.Y2 >= inner_poly->BoundingBox.Y2)
00124       {
00125         outer_poly = polygon;
00126         return;
00127       }
00128   }
00129   END_LOOP;
00130   Message("Cannot find a polygon enclosing the one you selected");
00131 }
00132 
00133 static void
00134 check_windings ()
00135 {
00136   double iw, ow;
00137   int i, j;
00138 
00139   iw = poly_winding (inner_poly);
00140   ow = poly_winding (outer_poly);
00141   if (iw * ow > 0)
00142     {
00143       /* Wound in same direction, must reverse one.  */
00144       for (i=0, j=inner_poly->PointN-1;
00145            i<j;
00146            i++, j--)
00147         {
00148           PointType x = inner_poly->Points[i];
00149           inner_poly->Points[i] = inner_poly->Points[j];
00150           inner_poly->Points[j] = x;
00151         }
00152     }
00153 }
00154 
00155 /* Rotate the polygon point list around so that point N is the first
00156    one in the list.  */
00157 static void
00158 rotate_points (PolygonTypePtr poly, int n)
00159 {
00160   PointType *np;
00161   int n2 = poly->PointN - n;
00162 
00163   np = (PointType *) malloc (poly->PointN * sizeof(PointType));
00164   memcpy (np, poly->Points + n, n2 * sizeof (PointType));
00165   memcpy (np+n2, poly->Points, n * sizeof (PointType));
00166   memcpy (poly->Points, np, poly->PointN * sizeof(PointType));
00167   free (np);
00168 }
00169 
00170 /* Make sure the first and last point of the polygon are the same
00171    point, so we can stitch them properly.  */
00172 static void
00173 dup_endpoints (PolygonType *poly)
00174 {
00175   int n = poly->PointN;
00176   if (poly->Points[0].X == poly->Points[n-1].X
00177       && poly->Points[0].Y == poly->Points[n-1].Y)
00178     return;
00179   CreateNewPointInPolygon (poly, poly->Points[0].X, poly->Points[0].Y);
00180 }
00181 
00182 /* Find the two closest points between those polygons, and connect
00183    them.  We assume pstoedit winds the two polygons in opposite
00184    directions.  */
00185 static void
00186 stitch_them ()
00187 {
00188   int i, o;
00189   int ii, oo;
00190   double best = -1, dist;
00191 
00192   ErasePolygon (inner_poly);
00193   ErasePolygon (outer_poly);
00194 
00195   /* This is O(n^2) but there's not a lot we can do about that.  */
00196   for (i=0; i<inner_poly->PointN; i++)
00197     for (o=0; o<outer_poly->PointN; o++)
00198       {
00199         int dx = inner_poly->Points[i].X - outer_poly->Points[o].X;
00200         int dy = inner_poly->Points[i].Y - outer_poly->Points[o].Y;
00201         dist = (double)dx*dx + (double)dy*dy;
00202         if (dist < best || best < 0)
00203           {
00204             ii = i;
00205             oo = o;
00206             best = dist;
00207           }
00208       }
00209   if (ii != 0)
00210     rotate_points (inner_poly, ii);
00211   if (oo != 0)
00212     rotate_points (outer_poly, oo);
00213   dup_endpoints (inner_poly);
00214   dup_endpoints (outer_poly);
00215 
00216   r_delete_entry (poly_layer->polygon_tree, (BoxType *)inner_poly);
00217   r_delete_entry (poly_layer->polygon_tree, (BoxType *)outer_poly);
00218 
00219   for (i=0; i<inner_poly->PointN; i++)
00220     CreateNewPointInPolygon (outer_poly, inner_poly->Points[i].X, inner_poly->Points[i].Y);
00221 
00222   SetChangedFlag (true);
00223 
00224   outer_poly->NoHolesValid = 0;
00225   SetPolygonBoundingBox (outer_poly);
00226   r_insert_entry (poly_layer->polygon_tree, (BoxType *)outer_poly, 0);
00227   RemoveExcessPolygonPoints (poly_layer, outer_poly);
00228   InitClip (PCB->Data, poly_layer, outer_poly);
00229   DrawPolygon (poly_layer, outer_poly, 0);
00230   Draw ();
00231 
00232   RemovePolygon (poly_layer, inner_poly);
00233 }
00234 
00235 static int
00236 polystitch (int argc, char **argv, int x, int y)
00237 {
00238   find_crosshair_poly (x, y);
00239   if (inner_poly)
00240     {
00241       find_enclosing_poly ();
00242       if (outer_poly)
00243         {
00244           check_windings ();
00245           stitch_them ();
00246         }
00247     }
00248   return 0;
00249 }
00250 
00251 static HID_Action polystitch_action_list[] = {
00252   {"PolyStitch", "Select a corner on the inner polygon", polystitch,
00253    NULL, NULL}
00254 };
00255 
00256 REGISTER_ACTIONS (polystitch_action_list)
00257 
00258 void
00259 pcb_plugin_init()
00260 {
00261   register_polystitch_action_list();
00262 }

Generated on Tue Aug 17 15:28:04 2010 for pcb-plugins by  doxygen 1.4.6-NO