Eu odeio responder minha própria pergunta, mas aqui vou eu. Espero não receber pontos por responder, isso seria estranho, apenas por aceitar uma resposta? (BTW, não recebi nenhuma resposta no fórum Element14.)
A solução é usar o comando DRAW, não ROUTE. DRAW colocará um segmento de fio, exatamente onde você especificar (ao contrário de ROUTE, que tenta se conectar a um fio aéreo não roteado. ROUTE é essencialmente inútil em um script). A próxima questão é via: não consigo (ou não quero) distinguir entre uma via manual e uma via autorouted, por isso mantenho todas as via que conectam dois (ou mais) segmentos de cabos manuais. Outras via são excluídas.
Então, o que meu script final faz é:
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
Observe que provavelmente não funcionará por mais de duas camadas, nem por outras coisas que não sejam segmentos de fios na camada de cobre.
IMHO todo o conceito da ULP da águia e das linguagens de comando é problemático. Um ULP é executado em um ambiente somente leitura, a única maneira de afetar o circuito, a placa ou a biblioteca é criando uma lista de comandos. Isso elimina algumas técnicas de programação úteis, mas o pior é que os comandos não foram projetados para serem facilmente criados a partir de um ULP. Você precisa de todos os tipos de transformações (neste caso: coordenadas, nomes de formas) para traduzir do mundo ULP para o mundo CMD.
(edit) Antes de executar este ULP, defina a seleção 'wire bend' para permitir ângulos arbitrários; caso contrário, o eagle tentará adaptar os fios ressuscitados aos ângulos permitidos, o que pode resultar em uma confusão sangrenta. IMHO este é outro exemplo do problema com ULP / SCR.
Este é o código ULP:
// 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 );
}