//EAGLE ULP "snap.ulp" //Copyright (c) 2012-07-09 Andreas Weidner // //Permission is hereby granted, free of charge, to any person obtaining a copy //of this software and associated documentation files (the "Software"), to deal //in the Software without restriction, including without limitation the rights //to use, copy, modify, merge, publish, distribute, sublicense, and/or sell //copies of the Software, and to permit persons to whom the Software is //furnished to do so, subject to the following conditions: // //The above copyright notice and this permission notice shall be included in //all copies or substantial portions of the Software. // //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE //SOFTWARE. #require 5.0600 //Earlier EAGLE versions (from 5.0 upward) MAY be able to run this ULP, but //were not properly tested #usage "en: " "

Snap objects to a grid

\n" "This program moves objects from schematics, boards or libraries, " "so that their origins (or end points) are on a given grid. The grid " "size and the objects to snap can be set with a dialog box or the command " "line.

" "© 2012-07-09 Andreas Weidner

" "

" "Usage:
" "
RUN snap [dx [dy]] [group|sheet] [objects]" "
" "
Sets the grid size to dx horizontally and dy " "vertically. If group is used, only objects inside the current " "group are moved, if sheet is used, only objects on the current " "schematics sheet are moved. If no objects are given, a snap " "dialog pops up. If objects are given, the snapping is done " "immediately (without a dialog). For a list of allowed objects, see " "below.
" "Examples:
" "
RUN snap
" "
Opens the snap dialog and lets the user set all options.
" "
RUN snap 0.1 0.2
" "
Sets the grid size to 0.1 horizontally and 0.2 " "vertically and shows the snap dialog.
" "
RUN snap 0.05 group all
" "
Snaps all supported objects belonging to the currently " "defined group to a grid of 0.05 in both directions.
" "
" "Supported objects:

" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "
EditorObjectsDescription
Schematic" "gatesMoves gates added from " "libraries. Junctions and nets connected directly to the gate pins are " "also moved.
netsMoves " "nets, busses and junctions. Cross-reference labels connected " "directly to net endpoints are also moved.
allMoves " "all of the above supported schematic objects.
Board" "elementsMoves elements added from " "libraries. Vias and signal wires connected directly to the element pads " "or SMDs are also moved.
signalsMoves " "signal wires, vias and polygons.
allMoves " "all of the above supported board objects.
Symbolpins" "Moves pins. If the pin locations " "are shared with other objects, these are also moved. If a group is " "defined, it will be changed during program execution.
allMoves " "all of the above supported symbol objects.
Package" "padsMoves pads.
smdsMoves " "SMDs.
allMoves " "all of the above supported package objects.


" "Hints:

    " "
  1. The grid size always uses the current editor unit. If you want to " "use a different unit, use the snap dialog.
  2. " "
  3. If no dy is given, the vertical grid is equal to the " "horizontal one. If no grid size is given, the current grid is used.
  4. " "
  5. Objects are snapped irrespective of their current visibility (even " "objects from hidden layers are moved). If you want to exclude some " "objects from snapping, first create a group that does not contain " "them.
  6. " "
  7. If group is used, but no group is currently defined, no " "objects are moved.
  8. " "
  9. Options can be abbreviated (e.g., gr instead of group) " "as long as there is no ambiguity (e.g., g could mean either " "group or gates).
  10. " "
", "de: " "

Objekte auf Gitter einrasten

\n" "Dieses Programm verschiebt Objekte im Schaltplan, auf der Platine " "oder in Bibliotheken, so daß ihre Nullpunkte (oder Endpunkte) auf einem " "definierten Raster liegen. Rasterabstand und einzurastende Objekte " "können per Dialogbox oder mit der Kommandozeile festgelegt werden.

" "© 2012-07-09 Andreas Weidner

" "

" "Aufruf:
" "
RUN snap [dx [dy]] [group|sheet] [Objekte]" "
" "
Setzt den Gitterabstand auf dx horizontal und dy " "vertikal. Ist group angegeben, werden nur Objekte innerhalb der " "aktuellen Gruppe verschoben, ist sheet angegeben, werden nur " "Objekte auf der aktuellen Schaltplanseite verschoben. Sind keine " "Objekte angegeben, wird ein Auswahldialog angezeigt. Werden " "Objekte verwendet, findet der Rastvorgang sofort (und ohne " "Dialogfenster) statt. Eine Liste unterstützter Objekte befindet sich " "unten.
" "Beispiele:
" "
RUN snap
" "
Öffnet den Einrastdialog, in dem der Benutzer alle Optionen " "einstellt.
" "
RUN snap 0.1 0.2
" "
Setzt den Gitterabstand auf 0.1 horizontal und 0.2 " "vertikal und zeigt den Einrastdialog an.
" "
RUN snap 0.05 group all
" "
Verschiebt alle unterstützten Objekte, die sich in der " "momentan definierten Gruppe befinden, auf ein Raster von 0.05" " in beiden Richtungen.
" "
" "Unterstützte Objekte:

" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "
EditorObjekteBeschreibung
Schaltplan" "gatesVerschiebt aus Bibliotheken " "eingefügte Gatter. Direkt mit den Gatteranschlüssen verbundene " "Netze und Kreuzungspunkte werden ebenfalls verschoben.
netsVerschiebt " "Netze, Busse und Kreuzungspunkte. Direkt mit Netzenden verbundene " "Querverweistexte werden ebenfalls verschoben.
allVerschiebt " "alle oben angegebenen Schaltplanobjekte.
Platine" "elementsVerschiebt aus Bibliotheken " "eingefügte Elemente. Direkt mit den Bauteilanschlüssen verbundene " "Kupferbahnen und Durchkontaktierungen werden ebenfalls verschoben.
signals" "Verschiebt mit Signalen verbundene Kupferbahnen, " "Durchkontaktierungen und Polygone.
allVerschiebt " "alle oben angegebenen Platinenobjekte.
Symbolpins" "Verschiebt Gatteranschlüsse. " "Stimmen deren Positionen mit anderen Objekten überein, werden diese " "ebenfalls verschoben. Ist eine Gruppe definiert, wird diese während der " "Programmausführung verändert.
allVerschiebt " "alle oben angegebenen Symbolobjekte.
Gehäuse" "padsVerschiebt Lötaugen.
smdsVerschiebt " "SMD-Lötflächen.
allVerschiebt " "alle oben angegebenen Gehäuseobjekte.


" "Hinweise:

    " "
  1. Der Rasterabstand verwendet immer die aktuelle Editor-Einheit. Zur " "Benutzung einer anderen Einheit kann der Einrastdialog angezeigt " "werden.
  2. " "
  3. Ist kein dy angegeben, wird das vertikale Raster gleich dem " "horizontalen gesetzt. Wird gar kein Gitterabstand angegeben, wird " "das aktuelle Raster verwendet.
  4. " "
  5. Objekte werden unabhängig von ihrer aktuellen Sichtbarkeit " "eingerastet (auch Objekte in ausgeblendeten Ebenen werden verschoben). " "Wollen Sie einige Objekte vom Einrasten ausnehmen, erzeugen Sie zuerst " "eine Gruppe ohne sie.
  6. " "
  7. Wird group übergeben, wenn momentan keine Gruppe definiert " "ist, wird kein Objekt verschoben.
  8. " "
  9. Die Optionen können abgekürzt werden (z.B. gr statt group" "), wenn dadurch keine Mißverständnisse entstehen (z.B. könnte g" " sowohl group als auch gates bedeuten).
" //----- FUNCTIONS COPIED FROM THE INCLUDE FILE 'awtools.inc' //----- (By default, this program uses several functions from an include file //----- 'awtools.inc'. To make the ULP independent of any includes for easier //----- distribution, the necessary function have been copied to this file //----- (unsorted). If 'awtools.inc' IS available and its usage desired, //----- uncomment the next line and delete all text up to (and including) //----- 'END OF TEXT COPIED FROM AWTOOLS.INC' //#include "awtools.inc" string AWExitCommand=""; //Global string that takes up script commands to execute after ULP exitting string AWTrim(string Text) { //Removes leading and trailing blanks, tabs and line feeds from TEXT string NewText,Character; NewText=Text; //Remove leading blanks and tabs Character=strsub(NewText,0,1); while ((Character==" ") || (Character=="\t") || (Character=="\n")) { NewText=strsub(NewText,1); Character=strsub(NewText,0,1); } //Ditto with trailing ones Character=strsub(NewText,strlen(NewText)-1); while ((Character==" ") || (Character=="\t") || (Character=="\n")) { NewText=strsub(NewText,0,strlen(NewText)-1); Character=strsub(NewText,strlen(NewText)-1); } return NewText; } int AWParameterFound(string Name,int Abbreviate) { //Returns 1 if the ULP was started with the (case-insensitive) command line //parameter NAME (0 otherwise). If ABBREVIATE<>0, the parameter is also found //when it was typed abbreviated int Nr; string Text; //Exit if no parameter or no name was given Name=strupr(AWTrim(Name)); if ((argc<2) || (!Name)) return 0; //Check all given parameters separately for (Nr=1;Nr=0.05); } int OffGridY(int Number) { //Returns 1 if the Y coordinate NUMBER (in editor units) is off grid //(0 otherwise) return (abs((u2mic(Number)/GridY)-round(u2mic(Number)/GridY))*GridY>=0.05); } int PointOffGrid(int x,int y) { //Returns 1 if the point (X,Y) in editor units is off grid (0 otherwise) return ((OffGridX(x)) || (OffGridY(y))); } int PointNeedsMoving(int x,int y,int Grouped) { //Returns 1 if the point (X,Y) in editor units must be moved (0 otherwise) if ((PointOffGrid(x,y)) && ((dlgScope!=scopeGroup) || (Grouped))) return 1; else return 0; } real SnapX(int Number) { //Returns the grid X coordinate (in micron) next to NUMBER (in editor units) return round(u2mic(Number)/GridX)*GridX; } real SnapY(int Number) { //Returns the grid Y coordinate (in micron) next to NUMBER (in editor units) return round(u2mic(Number)/GridY)*GridY; } string SnapPoint(int x,int y) { //Returns the script command to snap the point (x,y) in editor units string Result; sprintf(Result,"MOVE (%f %f) (%f %f);\n",u2mic(x),u2mic(y),SnapX(x), SnapY(y)); return Result; } string SnapGroupedPoint(int x,int y) { //Returns the script command to snap EVERYTHING that can be selected at the //point (x,y) in editor units string Result; sprintf(Result,"GROUP (%f %f) (%f %f) (%f %f) (>%f %f);\n"+ "MOVE (>%f %f) (%f %f);\n", u2mic(x)-0.1,u2mic(y)-0.1,u2mic(x)-0.1,u2mic(y)+0.1, u2mic(x)+0.1,u2mic(y)+0.1,u2mic(x)+0.1,u2mic(y)-0.1, u2mic(x),u2mic(y),SnapX(x),SnapY(y)); return Result; } //----- GRID CALCULATION FUNCTIONS ----- void SetDialogGrid(int GridUnit) { //Fills the dialog box entries with the current grid distance (in microns), //converting it to the desired GRIDUNIT int IntX,IntY; IntX=GridX/u2mic(1); IntY=GridY/u2mic(1); if (GridUnit==GRID_UNIT_MIC) { dlgGridX=u2mic(IntX); dlgGridY=u2mic(IntY); } else if (GridUnit==GRID_UNIT_MM) { dlgGridX=u2mm(IntX); dlgGridY=u2mm(IntY); } else if (GridUnit==GRID_UNIT_MIL) { dlgGridX=u2mil(IntX); dlgGridY=u2mil(IntY); } else if (GridUnit==GRID_UNIT_INCH) { dlgGridX=u2inch(IntX); dlgGridY=u2inch(IntY); } dlgGridUnit=GridUnit; OldUnit=GridUnit; } void GetDialogGrid(int GridUnit) { //Extracts the desired grid distance (in micron) from the dialog settings, //converting the values from the GRIDUNIT int IntX,IntY; if (GridUnit==GRID_UNIT_MIC) { IntX=dlgGridX/u2mic(1); IntY=dlgGridY/u2mic(1); } else if (GridUnit==GRID_UNIT_MM) { IntX=dlgGridX/u2mm(1); IntY=dlgGridY/u2mm(1); } else if (GridUnit==GRID_UNIT_MIL) { IntX=dlgGridX/u2mil(1); IntY=dlgGridY/u2mil(1); } else if (GridUnit==GRID_UNIT_INCH) { IntX=dlgGridX/u2inch(1); IntY=dlgGridY/u2inch(1); } GridX=u2mic(IntX); GridY=u2mic(IntY); } void GetDefaultGrid() { //Extracts the desired grid from the grid settings of the current editor //window and the command line parameters and puts them into the corresponding //global variables real Distance; int DistUnit,IntDistance; //Get the current grid unit from the active window if (schematic) schematic(S) { dlgGridUnit=S.grid.unit; DistUnit=S.grid.unitdist; Distance=S.grid.distance; } if (board) board(B) { dlgGridUnit=B.grid.unit; DistUnit=B.grid.unitdist; Distance=B.grid.distance; } if (library) library(L) { dlgGridUnit=L.grid.unit; DistUnit=L.grid.unitdist; Distance=L.grid.distance; } if (DistUnit==GRID_UNIT_MIC) IntDistance=Distance/u2mic(1); else if (DistUnit==GRID_UNIT_MM) IntDistance=Distance/u2mm(1); else if (DistUnit==GRID_UNIT_MIL) IntDistance=Distance/u2mil(1); else if (DistUnit==GRID_UNIT_INCH) IntDistance=Distance/u2inch(1); GridX=u2mic(IntDistance); GridY=GridX; //Convert the microns above into the current grid unit and put the result //into the dialog fields SetDialogGrid(dlgGridUnit); //Return, if no command line parameters were given if (argc<2) return; //The first parameter (if available and a proper number) is always the grid //distance in X direction (in the current grid unit) if (strtod(argv[1])) dlgGridX=strtod(argv[1]); //The second (if available and a proper number) is the Y distance (otherwise //the Y distance is the same as the X distance if ((argc>=3) && (strtod(argv[2]))) { dlgGridY=strtod(argv[2]); dlgGridEqual=0; } else dlgGridY=dlgGridX; //If the command line parameters have changed the dialog's grid settings, //convet those back to microns for internal use GetDialogGrid(dlgGridUnit); //For later passes, the grid settings are ALWAYS given in CORRECT microns, so //in this case, just read them again if (AWParameterFound("microns",0)) { GridX=strtod(argv[1]); GridY=strtod(argv[2]); } } //----- SCOPE SETTING FUNCTIONS ----- void GetDefaultScope() { //Calculates the default SCOPE setting based on the currently defined group //and the command line parameters. The setting ALL is possible under all //circumstances dlgScope=scopeAll; //For the second and third ULP pass, do NOT change the default scope //automatically, but ONLY take the scope from the ULP parameters if (!AWParameterFound("microns",0)) { //In a schematics, use GROUP if a group is defined, or SHEET otherwise if (sheet) { sheet(SH) if (ingroup(SH)) dlgScope=scopeGroup; else dlgScope=scopeSheet; } //In a board, use GROUP if a group is defined, or ALL otherwise if (board) board(B) if (ingroup(B)) dlgScope=scopeGroup; //(In the symbol or package editors, use ALL by default) } //If the command line parameters say otherwise, use those settings if (AWParameterFound("group",1)) dlgScope=scopeGroup; if ((AWParameterFound("sheet",1)) && (schematic)) dlgScope=scopeSheet; } //----- OBJECT SETTING FUNCTIONS ----- void ResetSnapObjects() { //Switches off the snapping procedures for all supported objects and inhibits //the display of the snap dialog dlgSnapPins=0; dlgSnapPads=0; dlgSnapSMDs=0; dlgSnapElements=0; dlgSnapSignals=0; dlgSnapSignalsPass2=0; dlgSnapGates=0; dlgSnapNets=0; dlgSnapNetsPass2=0; NoDialog=1; } void GetDefaultObjects() { //Extracts the objects to snap from the command line parameters and decides, //whether or not the snap dialog needs to be shown. If any objects ARE given //in the command line, the dialog needs NOT be shown if (schematic) { if (AWParameterFound("all",1) || AWParameterFound("gates",1) || AWParameterFound("nets",1) || AWParameterFound("netspass2",0)) ResetSnapObjects(); if (AWParameterFound("all",1) || AWParameterFound("gates",1)) dlgSnapGates=1; if (AWParameterFound("all",1) || (AWParameterFound("nets",1) && !AWParameterFound("netspass2",0))) dlgSnapNets=1; if (AWParameterFound("netspass2",0)) dlgSnapNetsPass2=1; } if (board) { if (AWParameterFound("all",1) || AWParameterFound("elements",1) || AWParameterFound("signals",1) || AWParameterFound("signalspass2",0)) ResetSnapObjects(); if (AWParameterFound("all",1) || AWParameterFound("elements",1)) dlgSnapElements=1; if (AWParameterFound("all",1) || (AWParameterFound("signals",1) && !AWParameterFound("signalspass2",0))) dlgSnapSignals=1; if (AWParameterFound("signalspass2",0)) dlgSnapSignalsPass2=1; } //Even if objects ARE given in the command line, show the dialog if the //desired grid settings are erraneous if ((GridX<0) || ((GridX<1) && (GridX>0)) || (GridX>500000) || (GridY<0) || ((GridY<1) && (GridY>0)) || (GridY>500000)) NoDialog=0; } //----- DIALOG FUNCTIONS ----- void ChangeUnit() { //Redisplays the dialog's edit fields if the desired grid unit is changed GetDialogGrid(OldUnit); SetDialogGrid(dlgGridUnit); dlgRedisplay(); } void ShowDistanceGroup() { //Creates all dialog elements of the 'GRID DISTANCE' group dlgGroup(AWLocalise(" GRID DISTANCE: "," RASTERABSTAND: ")) { dlgHBoxLayout { dlgLabel("X: "); dlgRealEdit(dlgGridX,0,1e6); dlgSpacing(20); dlgCheckBox("=",dlgGridEqual) dlgAccept(2); if (!dlgGridEqual) { dlgSpacing(12); dlgLabel("Y: "); dlgRealEdit(dlgGridY,0,1e6); } else dlgLabel("Y"); } dlgHBoxLayout { dlgRadioButton(AWLocalise("Micrometer (mic)","Mikrometer (mic)"), dlgGridUnit) ChangeUnit(); dlgRadioButton(AWLocalise("Millimeter (mm)","Millimeter (mm)"), dlgGridUnit) ChangeUnit(); dlgRadioButton("Mil (1/1000\")",dlgGridUnit) ChangeUnit(); dlgRadioButton(AWLocalise("Inch (\")","Zoll (\")"),dlgGridUnit) ChangeUnit(); } } } void ShowScopeGroup() { //Creates all dialog elements of the 'OBJECT SCOPE' group plus the following //'OK' and 'Cancel' buttons dlgGroup(AWLocalise(" OBJECT SCOPE: "," GELTUNGSBEREICH: ")) { dlgRadioButton(AWLocalise("Only objects in the current group (if "+ "defined)","Nur Objekte aus der aktuellen Gruppe (wenn definiert)"), dlgScope); if (schematic) { dlgRadioButton(AWLocalise("All objects on all schematic sheets (if "+ "available)","Alle Objekte auf allen Schaltplanseiten"),dlgScope); dlgRadioButton(AWLocalise("Only objects on the current sheet","Nur "+ "Objekte auf der aktuellen Schaltplanseite"),dlgScope); } else dlgRadioButton(AWLocalise("All objects in the current window","Alle "+ "Objekte im aktuellen Fenster"),dlgScope); } dlgHBoxLayout { dlgStretch(0); dlgPushButton("+OK") { if (dlgGridEqual) dlgGridY=dlgGridX; GetDialogGrid(dlgGridUnit); //In case of grid settings problems, complain if ((GridX<1) || (GridY<1)) dlgMessageBox(AWLocalise(":The grid distance must be larger than 1um.", ":Der Rasterabstand muß größer als 1um sein.")); else if ((GridX>500000) || (GridY>500000)) dlgMessageBox(AWLocalise(":The grid distance must be smaller than "+ "500mm.",":Der Rasterabstand muß kleiner als 500mm sein.")); else dlgAccept(1); } dlgPushButton(AWLocalise("-Cancel","-Abbrechen")) dlgReject(0); } } //----- SNAPPING PROCEDURES FOR THE SYMBOL EDITOR ----- string SnapPins() { //Returns the script text necessary to snap the desired pins from the //current symbol string Result="",Text; symbol(S) S.pins(P) if (PointNeedsMoving(P.x,P.y,ingroup(P))) //Unfortunately, pins can only be moved while displaying the 'symbols' //layer. This can create problems, because instead of the desired pin, //sometimes the WIRE at this point is moved (especially valid for //'point' pins). Therefore, build a SMALL group around each pin and //move it INCLUDING the wire(s) Result+=SnapGroupedPoint(P.x,P.y); if (Result) Result="DISPLAY None Symbols;\n"+ "GRID Mic Finest;\n"+ Result+ "GRID Last;\n"+ "DISPLAY Last;\n"; return Result; } void SnapSymbol() { //Snaps all desired objects in the current symbol editor window int Result=2; //Do nothing, if the current window is not the symbol editor if (!symbol) return; //If a dialog is desired, show it as often as necessary (until the user has //selected all options). The dialog returns 0 for 'cancel', 1 for 'OK' and 2 //for 'not yet finished' if (NoDialog) Result=1; while (Result==2) Result=dlgDialog(AWLocalise("Snap symbol objects","Symbolobjekte "+ "einrasten")) { ShowDistanceGroup(); dlgGroup(AWLocalise(" OBJECT TYPES: "," OBJEKTTYPEN: ")) dlgCheckBox("Pins",dlgSnapPins); ShowScopeGroup(); }; //Exit the ULP, if the dialog was cancelled or no objects were selected if ((!Result) || (!dlgSnapPins)) exit(0); //If pins are to be snapped, do just that if (dlgSnapPins) AWExitCommand+=SnapPins(); //Process the exit command exit(AWExitCommand); } //----- SNAPPING PROCEDURES FOR THE PACKAGE EDITOR ----- string SnapPadsSMDs(int Pads,int SMDs) { //Returns the script text necessary to snap the desired pads and SMDs from //the current package string Result="",Text; package(P) P.contacts(C) if (PointNeedsMoving(C.x,C.y,ingroup(C))) if (((Pads) && (C.pad)) || ((SMDs) && (C.smd))) { sprintf(Text,"MOVE %s (%.1f %.1f);\n",C.name,SnapX(C.x),SnapY(C.y)); Result+=Text; } if (Result) { Result="DISPLAY None Top Bottom Pads;\n"+ "GRID Mic Finest;\n"+ Result+ "GRID Last;\n"+ "DISPLAY Last;\n"; } return Result; } void SnapPackage() { //Snaps all desired objects in the current package editor window int Result=2; //Do nothing, if the current window is not the package editor if (!package) return; //If a dialog is desired, show it as often as necessary (until the user has //selected all options). The dialog returns 0 for 'cancel', 1 for 'OK' and 2 //for 'not yet finished' if (NoDialog) Result=1; while (Result==2) Result=dlgDialog(AWLocalise("Snap package objects","Gehäuseobjekte "+ "einrasten")) { ShowDistanceGroup(); dlgGroup(AWLocalise(" OBJECT TYPES: "," OBJEKTTYPEN: ")) { dlgHBoxLayout { dlgCheckBox("Pads",dlgSnapPads); dlgCheckBox("SMDs",dlgSnapSMDs); } } ShowScopeGroup(); }; //Exit the ULP, if the dialog was cancelled or no objects were selected if ((!Result) || ((!dlgSnapPads) && (!dlgSnapSMDs))) exit(0); //If objects are to be snapped, do just that if ((dlgSnapPads) || (dlgSnapSMDs)) AWExitCommand+=SnapPadsSMDs(dlgSnapPads,dlgSnapSMDs); //Process the exit command exit(AWExitCommand); } //----- POINT LIST FUNCTIONS ----- //Snapping vias/wires or junctions/nets is NOT so trivial, because when moving //ONE (line end) point, several others connected to it might also be moved //automatically. Therefore, don't move these objects separately, but keep a //list of POINTS to be moved and do the whole moving in several passes. The //idea for this method of moving was taken from the CadSoft ULPs //'snap-on-grid-sch.ulp' and 'cmd-snap-board.ulp' (even though the actual //source code wasn't) int PointX[],PointY[],PointLayer[],PointCount=0; //The point list: (X,Y) coordinates, point layer, list size int PointLayerUsed[]; //A separate entry for each layer defines whether this layer contains //objects that need moving (1) or not (0) int IsNewPoint(int x,int y,int Layer) { //Returns 1 if the point (X,Y) on the LAYER does NOT yet exist in the point //list (0 otherwise) int Nr; for (Nr=0;Nr