polycombine.c

00001 /* PolyCombine plug-in for PCB
00002 
00003    Copyright (C) 2010 Peter Clifton <pcjc2@cam.ac.uk>
00004 
00005    Licensed under the terms of the GNU General Public License, version 2.
00006 
00007    Compile like this:
00008 
00009    gcc -I$HOME/pcbsrc/git/src -I$HOME/pcbsrc/git -O2 -shared polycombine.c -o polycombine.so
00010 
00011    The resulting polycombine.so goes in $HOME/.pcb/plugins/polycombine.so.
00012 
00013    Usage: PolyCombine()
00014 
00015    The selected polygons are combined together according to the ordering of their points.
00016 */
00017 
00018 #include <stdio.h>
00019 #include <math.h>
00020 
00021 #include "global.h"
00022 #include "data.h"
00023 #include "macro.h"
00024 #include "create.h"
00025 #include "remove.h"
00026 #include "hid.h"
00027 #include "error.h"
00028 #include "rtree.h"
00029 #include "polygon.h"
00030 #include "polyarea.h"
00031 #include "assert.h"
00032 #include "strflags.h"
00033 #include "find.h"
00034 #include "misc.h"
00035 #include "draw.h"
00036 #include "undo.h"
00037 
00038 static POLYAREA *
00039 original_poly (PolygonType * p, bool *forward)
00040 {
00041   PLINE *contour = NULL;
00042   POLYAREA *np = NULL;
00043   Cardinal n;
00044   Vector v;
00045   int hole = 0;
00046 
00047   *forward = true;
00048 
00049   if ((np = poly_Create ()) == NULL)
00050     return NULL;
00051 
00052   /* first make initial polygon contour */
00053   for (n = 0; n < p->PointN; n++)
00054     {
00055       /* No current contour? Make a new one starting at point */
00056       /*   (or) Add point to existing contour */
00057 
00058       v[0] = p->Points[n].X;
00059       v[1] = p->Points[n].Y;
00060       if (contour == NULL)
00061         {
00062           if ((contour = poly_NewContour (v)) == NULL)
00063             return NULL;
00064         }
00065       else
00066         {
00067           poly_InclVertex (contour->head.prev, poly_CreateNode (v));
00068         }
00069 
00070       /* Is current point last in contour? If so process it. */
00071       if (n == p->PointN - 1 ||
00072           (hole < p->HoleIndexN && n == p->HoleIndex[hole] - 1))
00073         {
00074           poly_PreContour (contour, TRUE);
00075 
00076           /* Log the direction in which the outer contour was specified */
00077           if (hole == 0)
00078             *forward = (contour->Flags.orient == PLF_DIR);
00079 
00080           /* make sure it is a positive contour (outer) or negative (hole) */
00081           if (contour->Flags.orient != (hole ? PLF_INV : PLF_DIR))
00082             poly_InvContour (contour);
00083           assert (contour->Flags.orient == (hole ? PLF_INV : PLF_DIR));
00084 
00085           poly_InclContour (np, contour);
00086           contour = NULL;
00087           assert (poly_Valid (np));
00088 
00089           hole++;
00090         }
00091   }
00092   return np;
00093 }
00094 
00095 typedef struct poly_tree poly_tree;
00096 
00097 struct poly_tree {
00098   PolygonType *polygon;
00099   bool forward;
00100   POLYAREA *polyarea;
00101   poly_tree *parent;
00102   poly_tree *child;
00103   poly_tree *prev;
00104   poly_tree *next;
00105 };
00106 
00107 /*                      ______
00108  *  ___________________|_  P6 |             +P1 ____ +P6
00109  * | P1                | |    |              |
00110  * |   _____      ____ |_|____|             -P2 ____ -P4 ____ -P5
00111  * |  |P2   |    |P5  |  |                   |
00112  * |  | []  |    |____|  |                  +P3
00113  * |  |  P3 |            |
00114  * |  |_____|            |
00115  * |                     |
00116  * |          ___        |
00117  * |         |P4 |       |
00118  * |         |___|       |
00119  * |                     |
00120  * |_____________________|
00121  *
00122  * As we encounter each polygon, it gets a record. We need to check
00123  * whether it contains any of the polygons existing in our tree. If
00124  * it does, it will become the parent of them. (Check breadth first).
00125  *
00126  * When processing, work top down (breadth first), although if the
00127  * contours can be assumed not to overlap, we can drill down in this
00128  * order: P1, P2, P3, P4, P5, P6.
00129  */
00130 
00131 static bool
00132 PolygonContainsPolygon (POLYAREA *outer, POLYAREA *inner)
00133 {
00134 //  int contours_isect;
00135   /* Should check outer contours don't intersect? */
00136 //  contours_isect = Touching (outer, inner);
00137   /* Cheat and assume simple single contour polygons for now */
00138 //  return contours_isect ?
00139 //           0 : poly_ContourInContour (outer->contours, inner->contours);
00140   return poly_ContourInContour (outer->contours, inner->contours);
00141 }
00142 
00143 
00144 static poly_tree *
00145 insert_node_recursive (poly_tree *start_point, poly_tree *to_insert)
00146 {
00147   poly_tree *cur_node, *next = NULL;
00148 //  bool to_insert_isects_cur_node;   /* Intersection */
00149   bool to_insert_contains_cur_node; /* Containment */
00150   bool cur_node_contains_to_insert; /* Containment */
00151   bool placed_to_insert = false;
00152 
00153   poly_tree *return_root = start_point;
00154 
00155   if (start_point == NULL)
00156     {
00157 //      printf ("start_point is NULL, so returning to_insert\n");
00158       //to_insert->parent = !!; UNDEFINED
00159       return to_insert;
00160     }
00161 
00162   /* Investigate the start point and its peers first */
00163   for (cur_node = start_point; cur_node != NULL; cur_node = next)
00164     {
00165       next = cur_node->next;
00166 
00167 //      to_insert_isects_cur_node = IsPolygonInPolygon (to_insert->polygon, cur_node->polygon);
00168       to_insert_contains_cur_node = PolygonContainsPolygon (to_insert->polyarea, cur_node->polyarea);
00169 
00170 #if 0
00171       printf ("Inspecting polygon %ld %s, curnode is %ld %s: to_insert_isects_cur_node = %d, to_insert_contains_cur_node = %i\n",
00172               to_insert->polygon->ID,
00173               to_insert->forward ? "FWD" : "BWD",
00174               cur_node->polygon->ID,
00175               cur_node->forward ? "FWD" : "BWD",
00176               to_insert_isects_cur_node,
00177               to_insert_contains_cur_node);
00178 
00179       if (to_insert_isects_cur_node) /* Place as peer of this node? */
00180         {
00181         }
00182 #endif
00183 
00184       if (to_insert_contains_cur_node) /* Should be a parent of this node */
00185         {
00186           /* Remove cur_node from its peers */
00187           if (cur_node->prev)
00188             cur_node->prev->next = cur_node->next;
00189           if (cur_node->next)
00190             cur_node->next->prev = cur_node->prev;
00191 
00192           /* If we've not yet got a home, insert the to_insert node where cur_node was previously */
00193           if (!placed_to_insert)
00194             {
00195               to_insert->parent = cur_node->parent;
00196               to_insert->next = cur_node->next;
00197               to_insert->prev = cur_node->prev;
00198               if (to_insert->prev)
00199                 to_insert->prev->next = to_insert;
00200               if (to_insert->next)
00201                 to_insert->next->prev = to_insert;
00202               placed_to_insert = true;
00203 
00204               if (cur_node == start_point)
00205                 return_root = to_insert;
00206             }
00207 
00208           /* Prepend cur_node to our list of children */
00209           cur_node->parent = to_insert;
00210 
00211           cur_node->prev = NULL;
00212           cur_node->next = to_insert->child;
00213           if (to_insert->child)
00214             to_insert->child->prev = cur_node;
00215           to_insert->child = cur_node;
00216 
00217         }
00218     }
00219 
00220   if (placed_to_insert)
00221     {
00222 //      printf ("Returning new root %ld\n", return_root->polygon->ID);
00223       return return_root;
00224     }
00225 //    return (to_insert->parent == NULL) ? to_insert : to_insert->parent;
00226 
00227   /* Ok, so we still didn't find anywhere which the to_insert contour contained,
00228    * we need to start looking at the children of the start_point and its peers.
00229    */
00230 //  printf ("Looking at child nodes of the start_point\n");
00231 
00232   /* Investigate the start point and its peers first */
00233   for (cur_node = start_point; cur_node != NULL; cur_node = next)
00234     {
00235       next = cur_node->next;
00236 
00237       cur_node_contains_to_insert = PolygonContainsPolygon (cur_node->polyarea, to_insert->polyarea);
00238 
00239 #if 0
00240       printf ("Inspecting polygon %ld, curnode is %ld: cur_node_contains_to_insert = %d\n",
00241               to_insert->polygon->ID, cur_node->polygon->ID,
00242               cur_node_contains_to_insert);
00243 #endif
00244 
00245       /* Need to look at the child, ONLY if we know it engulfs our to_insert polygon */
00246       if (cur_node_contains_to_insert)
00247         {
00248           /* Can't set the parent within the call, so: */
00249           to_insert->parent = cur_node;
00250           cur_node->child = insert_node_recursive (cur_node->child, to_insert);
00251           return start_point;
00252           //return cur_node->parent;
00253         }
00254     }
00255 
00256 //  if (!placed_to_insert)
00257     /* prepend to_insert polygon to peer of start_point ? */
00258 //  printf ("Prepending as peer to start_poly\n");
00259   to_insert->parent = start_point->parent;
00260   to_insert->prev = NULL;
00261   to_insert->next = start_point;
00262   to_insert->next->prev = to_insert;
00263 
00264   return to_insert;
00265 }
00266 
00267 static POLYAREA *
00268 compute_polygon_recursive (poly_tree *root, POLYAREA *accumulate)
00269 {
00270   POLYAREA *res;
00271   poly_tree *cur_node;
00272   for (cur_node = root; cur_node != NULL; cur_node = cur_node->next)
00273     {
00274       /* Process this element */
00275 //      printf ("Processing node %ld %s\n", cur_node->polygon->ID, cur_node->forward ? "FWD" : "BWD");
00276       poly_Boolean_free (accumulate, cur_node->polyarea, &res, cur_node->forward ? PBO_UNITE : PBO_SUB);
00277       accumulate = res;
00278 
00279       /* And its children if it has them */
00280       if (cur_node->child)
00281         {
00282 //          printf ("Processing children\n");
00283           accumulate = compute_polygon_recursive (cur_node->child, accumulate);
00284         }
00285     }
00286   return accumulate;
00287 }
00288 
00289 static int
00290 polycombine (int argc, char **argv, int x, int y)
00291 {
00292   POLYAREA *res;
00293   bool forward;
00294 //  bool outer;
00295   POLYAREA *np;
00296 //  POLYAREA *pa;
00297 //  PLINE *pline;
00298 //  VNODE *node;
00299 //  PolygonType *Polygon;
00300   LayerType *Layer = NULL;
00301   poly_tree *root = NULL;
00302   poly_tree *this_node;
00303 
00304   /* First pass to combine the forward and backward contours */
00305   VISIBLEPOLYGON_LOOP (PCB->Data);
00306   {
00307     if (!TEST_FLAG (SELECTEDFLAG, polygon))
00308       continue;
00309 
00310     /* Pick the layer of the first polygon we find selected */
00311     if (Layer == NULL)
00312       Layer = layer;
00313 
00314     /* Only combine polygons on the same layer */
00315     if (Layer != layer)
00316       continue;
00317 
00318     np = original_poly (polygon, &forward);
00319 
00320     /* Build a poly_tree record */
00321     this_node = calloc (1, sizeof (poly_tree));
00322     this_node->polygon = polygon;
00323     this_node->forward = forward;
00324     this_node->polyarea = np;
00325 
00326     /* Check where we should place the node in the tree */
00327     root = insert_node_recursive (root, this_node);
00328 
00329     //RemovePolygon (layer, polygon);
00330   }
00331   ENDALL_LOOP;
00332 
00333   /* Now perform a traversal of the tree, computing a polygon */
00334   res = compute_polygon_recursive (root, NULL);
00335 
00336   SaveUndoSerialNumber ();
00337 
00338   /* Second pass to remove the input polygons */
00339   VISIBLEPOLYGON_LOOP (PCB->Data);
00340   {
00341     if (!TEST_FLAG (SELECTEDFLAG, polygon))
00342       continue;
00343 
00344     /* Only combine polygons on the same layer */
00345     if (Layer != layer)
00346       continue;
00347 
00348     RemovePolygon (layer, polygon);
00349   }
00350   ENDALL_LOOP;
00351 
00352   /* Now de-construct the resulting polygon into raw PCB polygons */
00353   PolyToPolygonsOnLayer (PCB->Data, Layer, res,
00354                          string_to_pcbflags ("clearpoly", NULL));
00355   RestoreUndoSerialNumber ();
00356   IncrementUndoSerialNumber ();
00357   Draw ();
00358 
00359   return 0;
00360 }
00361 
00362 static HID_Action polycombine_action_list[] = {
00363   {"PolyCombine", "???", polycombine,
00364    NULL, NULL}
00365 };
00366 
00367 REGISTER_ACTIONS (polycombine_action_list)
00368 
00369 void
00370 pcb_plugin_init()
00371 {
00372   register_polycombine_action_list();
00373 }

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