/* autocrop plug-in for PCB Reduce the board dimensions to just enclose the elements. Copyright (C) 2007 Ben Jackson based on teardrops.c by Copyright (C) 2006 DJ Delorie Licensed under the terms of the GNU General Public License, version 2 or later. */ #include #include #include "global.h" #include "data.h" #include "hid.h" #include "misc.h" #include "create.h" #include "rtree.h" #include "undo.h" #include "move.h" static void * MyMoveViaLowLevel(DataTypePtr Data, PinTypePtr Via, LocationType dx, LocationType dy) { if (Data) { RestoreToPolygon(Data, VIA_TYPE, Via, Via); r_delete_entry(Data->via_tree, (BoxTypePtr) Via); } MOVE_VIA_LOWLEVEL(Via, dx, dy); if (Data) { r_insert_entry(Data->via_tree, (BoxTypePtr) Via, 0); ClearFromPolygon(Data, VIA_TYPE, Via, Via); } return Via; } static void * MyMoveLineLowLevel(DataTypePtr Data, LayerTypePtr Layer, LineTypePtr Line, LocationType dx, LocationType dy) { if (Data) { RestoreToPolygon(Data, LINE_TYPE, Layer, Line); r_delete_entry(Layer->line_tree, (BoxTypePtr) Line); } MOVE_LINE_LOWLEVEL(Line, dx, dy); if (Data) { r_insert_entry(Layer->line_tree, (BoxTypePtr) Line, 0); ClearFromPolygon(Data, LINE_TYPE, Layer, Line); } return Line; } static void * MyMoveArcLowLevel(DataTypePtr Data, LayerTypePtr Layer, ArcTypePtr Arc, LocationType dx, LocationType dy) { if (Data) { RestoreToPolygon(Data, ARC_TYPE, Layer, Arc); r_delete_entry(Layer->arc_tree, (BoxTypePtr) Arc); } MOVE_ARC_LOWLEVEL(Arc, dx, dy); if (Data) { r_insert_entry(Layer->arc_tree, (BoxTypePtr) Arc, 0); ClearFromPolygon(Data, ARC_TYPE, Layer, Arc); } return Arc; } static void * MyMovePolygonLowLevel(DataTypePtr Data, LayerTypePtr Layer, PolygonTypePtr Polygon, LocationType dx, LocationType dy) { if (Data) { r_delete_entry(Layer->polygon_tree, (BoxTypePtr) Polygon); } /* move.c actually only moves points, note no Data/Layer args */ MovePolygonLowLevel(Polygon, dx, dy); if (Data) { r_insert_entry(Layer->polygon_tree, (BoxTypePtr) Polygon, 0); InitClip(Data, Layer, Polygon); } return Polygon; } static void * MyMoveTextLowLevel(LayerTypePtr Layer, TextTypePtr Text, LocationType dx, LocationType dy) { if (Layer) r_delete_entry(Layer->text_tree, (BoxTypePtr) Text); MOVE_TEXT_LOWLEVEL(Text, dx, dy); if (Layer) r_insert_entry(Layer->text_tree, (BoxTypePtr) Text, 0); return Text; } /* * Move everything. Call our own 'MyMove*LowLevel' where they don't exist * in move.c. This gets very slow if there are large polygons present, * since every element move re-clears the poly, followed by the polys * moving and re-clearing everything again. */ static void MoveAll(LocationType dx, LocationType dy) { ELEMENT_LOOP(PCB->Data); { MoveElementLowLevel(PCB->Data, element, dx, dy); AddObjectToMoveUndoList(ELEMENT_TYPE, NULL, NULL, element, dx, dy); } END_LOOP; VIA_LOOP(PCB->Data); { MyMoveViaLowLevel(PCB->Data, via, dx, dy); AddObjectToMoveUndoList(VIA_TYPE, NULL, NULL, via, dx, dy); } END_LOOP; ALLLINE_LOOP(PCB->Data); { MyMoveLineLowLevel(PCB->Data, layer, line, dx, dy); AddObjectToMoveUndoList(LINE_TYPE, NULL, NULL, line, dx, dy); } ENDALL_LOOP; ALLARC_LOOP(PCB->Data); { MyMoveArcLowLevel(PCB->Data, layer, arc, dx, dy); AddObjectToMoveUndoList(ARC_TYPE, NULL, NULL, arc, dx, dy); } ENDALL_LOOP; ALLTEXT_LOOP(PCB->Data); { MyMoveTextLowLevel(layer, text, dx, dy); AddObjectToMoveUndoList(TEXT_TYPE, NULL, NULL, text, dx, dy); } ENDALL_LOOP; ALLPOLYGON_LOOP(PCB->Data); { /* * XXX MovePolygonLowLevel does not mean "no gui" like * XXX MoveElementLowLevel, it doesn't even handle layer * XXX tree activity. */ MyMovePolygonLowLevel(PCB->Data, layer, polygon, dx, dy); AddObjectToMoveUndoList(POLYGON_TYPE, NULL, NULL, polygon, dx, dy); } ENDALL_LOOP; } static int autocrop(int argc, char **argv, int x, int y) { int changed = 0; LocationType dx, dy, pad; BoxTypePtr box; box = GetDataBoundingBox(PCB->Data); /* handy! */ if (!box || (box->X1 == box->X2 || box->Y1 == box->Y2)) { /* board would become degenerate */ return 0; } /* * Now X1/Y1 are the distance to move the left/top edge * (actually moving all components to the left/up) such that * the exact edge of the leftmost/topmost component would touch * the edge. Reduce the move by the edge relief requirement XXX * and expand the board by the same amount. */ pad = PCB->minWid * 5; /* XXX real edge clearance */ dx = -box->X1 + pad; dy = -box->Y1 + pad; box->X2 += pad; box->Y2 += pad; /* * Round move to keep components grid-aligned, then translate the * upper coordinates into the new space. */ dx -= dx % (long) PCB->Grid; dy -= dy % (long) PCB->Grid; box->X2 += dx; box->Y2 += dy; /* * Avoid touching any data if there's nothing to do. */ if (dx == 0 && dy == 0 && PCB->MaxWidth == box->X2 && PCB->MaxHeight == box->Y2) { return 0; } /* Resize -- XXX cannot be undone */ PCB->MaxWidth = box->X2; PCB->MaxHeight = box->Y2; MoveAll(dx, dy); IncrementUndoSerialNumber(); ClearAndRedrawOutput(); SetChangedFlag(1); return 0; } static HID_Action autocrop_action_list[] = { {"autocrop", NULL, autocrop, NULL, NULL} }; REGISTER_ACTIONS (autocrop_action_list) void hid_autocrop_init() { register_autocrop_action_list(); }