I hate answering my own question, but here I go. I hope I don't get get points for answering, that would be weird, only for accepting an answer? (BTW, I did not get any response on the Element14 forum.)
The solution is to use the DRAW command, not ROUTE. DRAW will place a wire segment, exactly where you specify (unlike ROUTE, which tries to connect to an unrouted airwire. ROUTE is essentially useless in a script.). The next issue is via's: I can't (or don't want to) distinguish between a manual via and an autorouted via, so I keep all via's that connect two (or more) manual wire segments. Other via's are deleted.
So what my final script does is:
prepare a ripup command
for all copper segments that are not 0.01 wide (the width I use for autorouting)
check both endpoints for a via at that location
prepare the via to be resurrected when it is visited the 2nd time
prepare a command that resurrects the copper segment
execute the prepared commands
Note that it will probably not work for more than two layers, nor for other things than wire segments at the copper layer.
IMHO the whole concept of the eagle ULP and command languages is troublesome. An ULP runs in a read-only environment, the only way it can affect the circuit, board or library is by creating a list of commands. This eliminates some useful programming techniques, but worse is that the commands were not designed to be easily created from an ULP. You need all kinds of transformations (in this case: coordinates, shape names) to translate from the ULP world to the CMD world.
(edit) Before you run this ULP, set the 'wire bend' selection to allow arbitrary angles, otherwise eagle will try to adapt the resurrected wires to the allowed angles, which can result in a bloody mess. IMHO this is another example of the problem with ULP/SCR.
This is the ULP code:
// gather the commands that must be run on exit
string RunOnExit = "";
void cmd( string s ) { RunOnExit += s + "\n"; }
// return an x or y position in the form that can be used in a command
real f( int x ){
board( B ) switch( B.grid.unit ) {
case 0: return u2mic(x);
case 1: return u2mm(x);
case 2: return u2mil(x);
case 3: return u2inch(x);
}
}
// return the string form of the a via's shape
string sn( int x ){
if( x == VIA_SHAPE_SQUARE ) return "square";
if( x == VIA_SHAPE_ROUND ) return "round";
if( x == VIA_SHAPE_OCTAGON ) return "octagon";
if( x == VIA_SHAPE_ANNULUS ) return "annulus";
if( x == VIA_SHAPE_THERMAL ) return "thermal";
return "unknown-via-shape";
}
// count the number of times x occurs in s
int n_ocurrences( string s, string x ){
int i, n = 0;
while( 1 ){
i = strstr( s, x );
if( i == -1 ) return n;
s = strsub( s, i + strlen( x ));
n++;
}
}
// add a via, but only when it is visited the second time
string via_list = "";
void add_via( int a, int b ){
// for all via's
board( B ) B.signals( S ) S.vias( V ){
// if the via is at the current location
if(( V.x == a ) && ( V.y == b )){
string s, coo;
// the coordinates of the via are used as its identification
sprintf( coo, "(%.6f %.6f)", f( V.x ), f( V.y ));
// if this is the second visit to this via
via_list += coo;
if( n_ocurrences( via_list, coo ) == 2 ){
// resurrect this via
sprintf( s, "VIA '%s' %f %s %s;",
S.name, f( V.drill ), sn( V.shape[ 1 ] ), coo );
cmd( s );
}
}
}
}
if( !board ){
dlgMessageBox("start this ULP in Board", "OK");
exit( 0 );
}
board( B ){
// first delete all coper segments,
// later we will resurrect what we want to keep
cmd( "RIPUP;" );
// for all wire segments in the top and bottom copper layers
B.signals(S) S.wires(W) {
if( ( W.layer == 1 ) || ( W.layer == 16 ) ){
// that are not 0.01 width (that is what the autorouter uses)
if( f( W.width ) != 0.01 ){
string s;
// resurrect via's adjacent to this wire segment
add_via( W.x1, W.y1 );
add_via( W.x2, W.y2 );
sprintf( s, "CHANGE LAYER %d;", W.layer );
cmd( s );
// resurrect this wire segment
sprintf(
s, "WIRE '%s' %f (%.6f %.6f) (%.6f %.6f);",
S.name, f( W.width),
f(W.x1), f(W.y1), f(W.x2), f(W.y2));
cmd( s );
}
}
}
// dlgMessageBox( RunOnExit, "OK");
exit( RunOnExit );
}
One major problem that prevented proper version control was the binary library format that Eagle 5 and earlier use. I guess you are already aware that they changed the format to plain text XML with Eagle 6.
We use Subversion for version control, but thats just a personal choice. Thanks to the XML format we can now perform diffs and thus following changes became much easier. It is now sufficient to check the changes during the commit, rather than having to make notes about it during development.
Our general approach:
All our libraries share one folder in the trunk. We have one "common" library where all components are clumped together, that don't match the content of one of the other more specialized libraries. From time to time we extract fitting "groups" of components to their own library, cleaning up the common one.
We always check out the whole trunk to our working copy, which resides in a subdirectory of Eagles \lib folder. All files are attributed as "needs-lock" to prevent corruption, but this may no longer be necessary since we could make manual corrections to XML.
Its a good thing to include the general part numbers when writing the SVN logs. So its easy to check the revision history to prevent doing some work twice.
As a side note: If you require a project specific library that contains all components of a specific board, there is an ULP script that comes with default Eagle installation. It extracts all used components into a single lbr file.
In the end, the internal library management from eagle is just not good and that is something we can't do much about.
Best Answer
You can define key board shortcuts.
Goto Options -> Assign -> New
You can select any key combination for the Shortcut.(Eg:Ctrl + F). Activating this will invoke the ULP
My eagle version is 6.2.0