686 lines
26 KiB
Plaintext
686 lines
26 KiB
Plaintext
#usage "<b>Teardrops - v1.05 (02/17/12)</b>\n"
|
|
"<p>"
|
|
"This ULP allows creation of teardrop-shaped connections from a board's vias/pads to their attached wire segments. "
|
|
"Making these connections teardrop shaped enhances manufacturability and reduces board failure by ensuring "
|
|
"connectivity between the segment and via/pad in cases where the via hole is not accurately drilled and "
|
|
"would otherwise sever the segment."
|
|
"<p>"
|
|
"<author>Original Author: Tad Artis (E3Eagle_removethis@E3Switch.com)<br>"
|
|
"Modifications: Bob Starr (rtzaudio@comcast.net)<br>"
|
|
"Modification: layer selectable (alf@cadsoft.de)<br>"
|
|
"</author>"
|
|
|
|
// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED
|
|
|
|
string HelpText = usage +
|
|
"<p><b>Implementation</b><br>"
|
|
"This ULP is a variant based on via_teardrops1.ulp by Tad Ardis originally. This version is designed to support pads as well. "
|
|
"An attempt has been made to implement teardrops in this ULP while maintaining DRC validity. "
|
|
"Teardrops are implemented as 'two' added wire segments attaching from the original signal, at a short radius from the via, to two tangential "
|
|
"points at the via's edge. These two attachments form a small triangle and then are further attached to the via's center in order to "
|
|
"avoid ratsnest dangling segment errors. The user can specify the radius from the via at which the teardrop begins."
|
|
"<p><b>Output</b><br>"
|
|
"The output of this program is an .scr script which may be run to add vias to all signals on the board. "
|
|
"This .scr file is also properly formatted to allow it to be read into a spreadsheet program as a .csv file which will be found to contain "
|
|
"detailed information about the signal and via to which each teardrop is being added. The spreadsheet "
|
|
"columns can be sorted and copied back to an .scr to simplify adding teardrops to only certain net classes, via sizes, layers, etc. "
|
|
"The spreadsheet format may also allow teardrops to be added to an unused signal layer which would facilitate cutting "
|
|
"them from undesired portions of the board and might create a cleaner situation for future board upgrades."
|
|
"<p><b>Warnings</b><br>"
|
|
"<b>This ULP should be used with forethought or on a copy of your board file just before plotting.</b><br>"
|
|
"It may difficult to remove teardrops once added. It may be difficult to run the teardrop ULP a second time on a board which "
|
|
"already has teardrops. It may be possible to alleviate these concerns by using the spreadsheet output or a find/replace "
|
|
"on the .scr file to send the "
|
|
"teardrops to unused signal layers which are then included when the origin signal layer is plotted.<br>"
|
|
"One should be aware that there are small hexogonal points at the ends of the added teardrop segments which protrude beyond "
|
|
"the tangent of the via. In most cases these protrusions are tiny and won't present any design rule violations."
|
|
"<p><b>Limitations</b><br>"
|
|
"The current software operates using mm units for .scr and only accepts units in mils from the user and in generated spreadsheet values."
|
|
"<p><b>Copyright</b><br>"
|
|
"Enhancements to the ULP are welcome without the necessity of contacting the author."
|
|
"<p>"
|
|
"This program is provided AS IS and without warranty of any kind, expressed or implied."
|
|
"This program may be freely modified and distributed."
|
|
"<p>"
|
|
"<hr>"
|
|
"<font color=red>All text in red shows differences with the previous version of this program.</font>"
|
|
"<hr>"
|
|
;
|
|
|
|
#require 5.1200
|
|
|
|
string Release = "1.06";
|
|
string ReleaseDate = "April 04, 2012"; // alf@cadsoft.de
|
|
|
|
string HistoryText1 =
|
|
"<p>"
|
|
"This program was based on the original via_teardrops1.ulp work by Tad Artis (E3Eagle_removethis@E3Switch.com).<br>"
|
|
"An attempt has been made to add usable support for teardropping pads as well in this adaptation."
|
|
"<p>"
|
|
"The following is a history of this program (most recent first)."
|
|
"<p>"
|
|
"Version 1.0"
|
|
"<ul>"
|
|
"<li>Initial version. Released on January 5, 2008.</li>"
|
|
"</ul>"
|
|
;
|
|
|
|
string HistoryText2 =
|
|
"<p>"
|
|
"The following is a history of this program (most recent first)."
|
|
"<p>"
|
|
"Version History"
|
|
"<ul>"
|
|
"<li>v1.00 - Initial release version.</li><p>"
|
|
"<li>v1.01 - Added teardropping of pads support.</li><p>"
|
|
"<li>v1.02 - Improved pad teardrop support.</li><p>"
|
|
"<li>v1.03 - Teardropping of square pad types added support.</li><p>"
|
|
"<li>v1.04 - Added fixes for Eagle v6.0 (Walter Mueller)</li><p>" //[MUEWA104]
|
|
"<li>v1.05 - Modified pad teardrops to fill better</li><p>" //[RES105]
|
|
"<li>v1.06 - Modified selectable layer</li><p>" //alfcadsoft.de
|
|
"</ul>"
|
|
;
|
|
|
|
int User_Pads = 0;
|
|
int User_Vias = 1;
|
|
real User_Tear_Radius = 1.5;
|
|
real User_Ignore_Width = 25;
|
|
real User_Ignore_sdratio = 1.5;
|
|
real uiw_internal;
|
|
|
|
int via_count;
|
|
int aring;
|
|
real radius, radius_sq, tangent_radius; //[MUEWA104]
|
|
int in1,in2;
|
|
int x_cross, y_cross;
|
|
real x1, x2, y1, y2; //[MUEWA104]
|
|
real t; //[MUEWA104]
|
|
int tstep; //[MUEWA104]
|
|
int changes = 0;
|
|
real xstep, ystep, xc, yc;
|
|
real astart,aend,sin_of_astart,cos_of_astart,wstep,w;
|
|
real tearseg_delta_w, tearseg_w, tearseg_len;
|
|
int tearseg_xend, tearseg_yend;
|
|
string vcount, fname, params;
|
|
|
|
numeric string LayerName[];
|
|
int LayerNum[];
|
|
int LayerCount = 0;
|
|
int LayerSel = 0;
|
|
numeric string TargetLayer[]; // 2012-04-04
|
|
int tCount = 0;
|
|
int LayActiv[];
|
|
int IsLayer[];
|
|
int TeardropLayer[];
|
|
|
|
// Output in dual-use .scr and spreadsheet .csv format
|
|
void output_hdr(void) {
|
|
printf("# SCR Command, Signal Name, Signal Class, Layer, Width, Via Drill, Via Layer Diameter, Via X, Via Y, Ratio segment_width/Via_drill_size\n");
|
|
return;
|
|
}
|
|
|
|
void output_via_wire(int layer, int width, int x1, int y1, int x2, int y2, UL_VIA V, UL_WIRE W, UL_SIGNAL S) {
|
|
if (!LayActiv[layer]) return; // 201-04-04
|
|
int lnum = TeardropLayer[layer];
|
|
printf("LAYER %d; WIRE '%s' %.8f (%.9f %.9f) (%.9f %.9f);\n", // #129, %s, %s, %d, %.1f, %.1f, %.1f, %.3f, %.3f, %.2f\n", // alf@cadsoft.de
|
|
lnum, S.name, u2mm(width), u2mm(x1), u2mm(y1), u2mm(x2), u2mm(y2)
|
|
//S.name, S.class.name, layer, u2mil(width), u2mil(V.drill), u2mil(V.diameter[layer]), u2mil(V.x), u2mil(V.y), real(width)/V.drill
|
|
);
|
|
return;
|
|
}
|
|
|
|
void output_pad_wire(int layer, int width, int x1, int y1, int x2, int y2, UL_PAD P, UL_WIRE W, UL_SIGNAL S) {
|
|
if (!LayActiv[layer]) return; // 201-04-04
|
|
int lnum = TeardropLayer[layer];
|
|
printf("LAYER %d; WIRE '%s' %.8f (%.9f %.9f) (%.9f %.9f);\n", // #139, %s, %s, %d, %.1f, %.1f, %.1f, %.3f, %.3f, %.2f\n", // alf@cadsoft.de
|
|
lnum, S.name, u2mm(width), u2mm(x1), u2mm(y1), u2mm(x2), u2mm(y2)
|
|
//P.name, P.name, layer, u2mil(width), u2mil(P.drill), u2mil(P.diameter[layer]), u2mil(P.x), u2mil(P.y), real(width)/P.drill
|
|
);
|
|
return;
|
|
}
|
|
|
|
void do_pads(UL_SIGNAL S) {
|
|
int shape;
|
|
|
|
S.contactrefs(C) {
|
|
if (C.contact.pad) {
|
|
via_count++;
|
|
|
|
sprintf(vcount, "Processing Pad #%d -- Signal %s", via_count, C.contact.pad.name);
|
|
status(vcount);
|
|
|
|
// find wires starting within teardrop-apex radius of the via and ending outside it.
|
|
|
|
//radius = C.contact.pad.drill * (User_Tear_Radius - 0.25); //[RES105]
|
|
radius = C.contact.pad.drill * (User_Tear_Radius - 0.5);
|
|
radius_sq = radius * radius;
|
|
|
|
S.wires(W) {
|
|
// quick check first. See if either wire endpoint is within a square of size 2 x radius of the pad.
|
|
in1 = 0;
|
|
in2 = 0;
|
|
|
|
if ((W.width < uiw_internal) && (real(W.width)/C.contact.pad.drill < User_Ignore_sdratio)) {
|
|
if (W.layer >= 1 && W.layer <= 16) {
|
|
shape = C.contact.pad.shape[W.layer];
|
|
if (shape == PAD_SHAPE_ROUND || shape == PAD_SHAPE_OCTAGON) {
|
|
// Passed user specified wires to ignore
|
|
if (abs(W.x1-C.contact.pad.x)<radius && abs(W.y1-C.contact.pad.y)<radius)
|
|
in1 = 1;
|
|
if (abs(W.x2-C.contact.pad.x)<radius && abs(W.y2-C.contact.pad.y)<radius)
|
|
in2 = 1;
|
|
|
|
if (in1 || in2){
|
|
if (in1) {
|
|
if ((real(W.x1-C.contact.pad.x)*(W.x1-C.contact.pad.x)+real(W.y1-C.contact.pad.y)*(W.y1-C.contact.pad.y)) > radius_sq) //[MUEWA104]
|
|
in1 = 0;
|
|
}
|
|
|
|
if (in2) {
|
|
if ((real(W.x2-C.contact.pad.x)*(W.x2-C.contact.pad.x)+real(W.y2-C.contact.pad.y)*(W.y2-C.contact.pad.y)) > radius_sq) //[MUEWA104]
|
|
in2 = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in1 ^ in2) {
|
|
changes = 1;
|
|
// Calculate point where wire crosses radius. Move calculations relative to via's center.
|
|
x1 = W.x1-C.contact.pad.x;
|
|
x2 = W.x2-C.contact.pad.x;
|
|
y1 = W.y1-C.contact.pad.y;
|
|
y2 = W.y2-C.contact.pad.y;
|
|
// Make x1,y1 always the end closest to via center.
|
|
if (in2) {
|
|
// Swap start/end
|
|
t = x1;
|
|
x1 = x2;
|
|
x2 = t;
|
|
t = y1;
|
|
y1 = y2;
|
|
y2 = t;
|
|
}
|
|
// Perhaps the line and arc intersections equations could be solved directly without too much trouble,
|
|
// but didn't feel like dealing with it and the special cases.
|
|
if (!W.arc) {
|
|
// Easiest to represent the line parametrically and step up it.
|
|
tstep = max(abs(y2-y1),abs(x2-x1));
|
|
xstep = (x2-x1)/real(tstep);
|
|
ystep = (y2-y1)/real(tstep);
|
|
tstep = tstep >>1;
|
|
|
|
// We're double testing points here, but don't feel like fixing.
|
|
for (t = 0; tstep > 0; t = t+tstep) {
|
|
xc = x1+xstep*t;
|
|
yc = y1+ystep*t;
|
|
|
|
if ((xc*xc + yc*yc) > radius_sq) {
|
|
// backup a little and increase resolution
|
|
t = t-tstep;
|
|
tstep = tstep >> 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// wire segment is an arc
|
|
// Get correct arc starting end angle for x1,y1
|
|
if ((x1+C.contact.pad.x) == W.arc.x1 && (y1+C.contact.pad.y) == W.arc.y1) {
|
|
astart = W.arc.angle1;
|
|
aend = W.arc.angle2;
|
|
}
|
|
else {
|
|
// Swap start/end
|
|
astart = W.arc.angle2;
|
|
aend = W.arc.angle1;
|
|
}
|
|
|
|
// wstep is the change in angle to the next point on the arc to test.
|
|
astart = PI/180 * astart;
|
|
aend = PI/180 * aend;
|
|
wstep = (aend-astart)/2; // make wstep 1/2 the difference
|
|
sin_of_astart = W.arc.radius*sin(astart);
|
|
cos_of_astart = W.arc.radius*cos(astart);
|
|
|
|
// dy = radius*(sin(astart+dw)-sin(astart))
|
|
// dx = radius*(cos(astart+dw)-cos(astart))
|
|
|
|
// w is the current angle on the arc being tested
|
|
// We're double testing points here, but don't feel like fixing.
|
|
|
|
for (w = astart+wstep; abs(wstep) > .00001; w = w+wstep) {
|
|
yc = y1+W.arc.radius*sin(w)-sin_of_astart;
|
|
xc = x1+W.arc.radius*cos(w)-cos_of_astart;
|
|
|
|
if ((xc*xc + yc*yc) > radius_sq) {
|
|
// backup a little and increase resolution
|
|
w = w-wstep;
|
|
wstep = wstep/2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine location of tangents of via back to wire.
|
|
// angular offset = sin (viaradius/(seg to via ctr distance))
|
|
|
|
int wmult = 1;
|
|
int width = W.width;
|
|
int aring = C.contact.pad.diameter[W.layer] - C.contact.pad.drill;
|
|
|
|
// printf("# width=%f, drill=%f, diameter=%f, aring=%f\n", u2mil(width), u2mil(C.contact.pad.drill), u2mil(C.contact.pad.diameter[W.layer]), u2mil(aring));
|
|
|
|
// [RES102] - Adjusted tangent divide ratios and added logic to determine
|
|
// if wire width is less than 10 mil or the annular ring is less than the wire width,
|
|
// double the wire width for better fill.
|
|
|
|
if ((u2mil(width) <= 10) || ((u2mil(aring) / 2.0) > u2mil(width)))
|
|
wmult = 2;
|
|
|
|
width = width * wmult;
|
|
|
|
//if (u2mil(C.contact.pad.diameter[W.layer]) >= 100)
|
|
// tangent_radius = (C.contact.pad.diameter[W.layer] - (W.width * wmult)) / 4; // [RES102] was 8
|
|
//else
|
|
// tangent_radius = (C.contact.pad.diameter[W.layer] - (W.width * wmult)) / 2; // [RES102] was 4
|
|
// [RES105]
|
|
tangent_radius = (C.contact.pad.diameter[W.layer] - (W.width * wmult)) / 2;
|
|
|
|
if (tangent_radius > 0) {
|
|
// don't bother if wire is bigger than via
|
|
|
|
tearseg_delta_w = asin(real(tangent_radius)/radius);
|
|
tearseg_len = sqrt(radius * radius - tangent_radius * tangent_radius);
|
|
|
|
if (xc == 0) {
|
|
if (yc < 0)
|
|
tearseg_w = PI*.5;
|
|
else
|
|
tearseg_w = -PI*.5;
|
|
}
|
|
else {
|
|
tearseg_w = atan (real(yc)/(xc));
|
|
}
|
|
|
|
// Get the right quadrant
|
|
if (xc > 0) tearseg_w = tearseg_w + PI;
|
|
|
|
tearseg_xend = C.contact.pad.x+xc + tearseg_len*cos(tearseg_w-tearseg_delta_w);
|
|
tearseg_yend = C.contact.pad.y+yc + tearseg_len*sin(tearseg_w-tearseg_delta_w);
|
|
|
|
// Suggest a script command.
|
|
output_pad_wire(W.layer, width, int(C.contact.pad.x+xc), int(C.contact.pad.y+yc), tearseg_xend, tearseg_yend, C.contact.pad, W, S);
|
|
// Now add segment to the center of the via so ratsnest doesn't show up as dangling.
|
|
output_pad_wire(W.layer, width, int(C.contact.pad.x), int(C.contact.pad.y), tearseg_xend, tearseg_yend, C.contact.pad, W, S);
|
|
|
|
// Now the other side of the tear
|
|
tearseg_xend = C.contact.pad.x+xc + tearseg_len*cos(tearseg_w+tearseg_delta_w);
|
|
tearseg_yend = C.contact.pad.y+yc + tearseg_len*sin(tearseg_w+tearseg_delta_w);
|
|
|
|
// Suggest a script command.
|
|
output_pad_wire(W.layer, width, int(C.contact.pad.x+xc), int(C.contact.pad.y+yc), tearseg_xend, tearseg_yend, C.contact.pad, W, S);
|
|
// Now add segment to the center of the via so ratsnest doesn't show up as dangling.
|
|
output_pad_wire(W.layer, width, int(C.contact.pad.x), int(C.contact.pad.y), tearseg_xend, tearseg_yend, C.contact.pad, W, S);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void do_vias(UL_SIGNAL S) {
|
|
S.vias(V) {
|
|
via_count++;
|
|
|
|
sprintf (vcount, "Processing Via #%d -- Signal %s", via_count, S.name);
|
|
status (vcount);
|
|
|
|
// find wires starting within teardrop-apex radius of the via and ending outside it.
|
|
radius = V.drill * User_Tear_Radius;
|
|
radius_sq = radius * radius;
|
|
|
|
S.wires(W) {
|
|
// quick check first. See if either wire endpoint is within a square of size 2 x radius of the via.
|
|
in1 = 0;
|
|
in2 = 0;
|
|
|
|
if ((W.width < uiw_internal) && (real(W.width)/V.drill < User_Ignore_sdratio)) {
|
|
// Passed user specified wires to ignore
|
|
|
|
if (abs(W.x1-V.x)<radius && abs(W.y1-V.y)<radius)
|
|
in1 = 1;
|
|
if (abs(W.x2-V.x)<radius && abs(W.y2-V.y)<radius)
|
|
in2 = 1;
|
|
|
|
if (in1 || in2) {
|
|
// Check location more carefully
|
|
// Check layer
|
|
if (W.layer >= V.start && W.layer <= V.end) {
|
|
if (in1) {
|
|
if ((real(W.x1-V.x)*(W.x1-V.x) + real(W.y1-V.y)*(W.y1-V.y)) > radius_sq) //[MUEWA104]
|
|
in1 = 0;
|
|
}
|
|
|
|
if (in2) {
|
|
if ((real(W.x2-V.x)*(W.x2-V.x) + real(W.y2-V.y)*(W.y2-V.y)) > radius_sq) //[MUEWA104]
|
|
in2 = 0;
|
|
}
|
|
}
|
|
else {
|
|
in1 = 0;
|
|
in2 = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in1 ^ in2) {
|
|
changes = 1;
|
|
|
|
// Calculate point where wire crosses radius. Move calculations relative to via's center.
|
|
x1 = W.x1-V.x;
|
|
x2 = W.x2-V.x;
|
|
y1 = W.y1-V.y;
|
|
y2 = W.y2-V.y;
|
|
|
|
// Make x1,y1 always the end closest to via center.
|
|
if (in2) {
|
|
// Swap start/end
|
|
t = x1;
|
|
x1 = x2;
|
|
x2 = t;
|
|
t = y1;
|
|
y1 = y2;
|
|
y2 = t;
|
|
}
|
|
|
|
// Perhaps the line and arc intersections equations could be solved directly without too much trouble,
|
|
// but didn't feel like dealing with it and the special cases.
|
|
|
|
if (!W.arc) {
|
|
// Easiest to represent the line parametrically and step up it.
|
|
|
|
tstep = max(abs(y2-y1),abs(x2-x1));
|
|
xstep = (x2-x1)/real(tstep);
|
|
ystep = (y2-y1)/real(tstep);
|
|
tstep = tstep >>1;
|
|
|
|
// We're double testing points here, but don't feel like fixing.
|
|
for (t = 0; tstep > 0; t = t+tstep) {
|
|
xc = x1+xstep*t;
|
|
yc = y1+ystep*t;
|
|
|
|
if ((xc*xc + yc*yc) > radius_sq) {
|
|
// backup a little and increase resolution
|
|
t = t-tstep;
|
|
tstep = tstep >> 1;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// wire segment is an arc
|
|
// Get correct arc starting end angle for x1,y1
|
|
|
|
if ((x1+V.x) == W.arc.x1 && (y1+V.y) == W.arc.y1) {
|
|
astart = W.arc.angle1;
|
|
aend = W.arc.angle2;
|
|
}
|
|
else {
|
|
// Swap start/end
|
|
astart = W.arc.angle2;
|
|
aend = W.arc.angle1;
|
|
}
|
|
|
|
// wstep is the change in angle to the next point on the arc to test.
|
|
astart = PI/180 * astart;
|
|
aend = PI/180 * aend;
|
|
wstep = (aend-astart)/2; // make wstep 1/2 the difference
|
|
sin_of_astart = W.arc.radius*sin(astart);
|
|
cos_of_astart = W.arc.radius*cos(astart);
|
|
|
|
// dy = radius*(sin(astart+dw)-sin(astart))
|
|
// dx = radius*(cos(astart+dw)-cos(astart))
|
|
|
|
// w is the current angle on the arc being tested
|
|
// We're double testing points here, but don't feel like fixing.
|
|
|
|
for (w = astart+wstep; abs(wstep) > .00001; w = w+wstep) {
|
|
yc = y1+W.arc.radius*sin(w)-sin_of_astart;
|
|
xc = x1+W.arc.radius*cos(w)-cos_of_astart;
|
|
|
|
if ((xc*xc + yc*yc) > radius_sq) {
|
|
// backup a little and increase resolution
|
|
w = w-wstep;
|
|
wstep = wstep/2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine location of tangents of via back to wire.
|
|
// angular offset = sin (viaradius/(seg to via ctr distance))
|
|
|
|
tangent_radius = (V.diameter[W.layer] - W.width)/2;
|
|
|
|
if (tangent_radius > 0) {
|
|
// don't bother if wire is bigger than via
|
|
tearseg_delta_w = asin(real(tangent_radius)/radius);
|
|
tearseg_len = sqrt (radius * radius - tangent_radius * tangent_radius);
|
|
|
|
if (xc == 0) {
|
|
if (yc < 0) tearseg_w = PI*.5;
|
|
else tearseg_w = -PI*.5;
|
|
}
|
|
else {
|
|
tearseg_w = atan (real(yc)/(xc));
|
|
}
|
|
|
|
// Get the right quadrant
|
|
if (xc > 0)
|
|
tearseg_w = tearseg_w + PI;
|
|
|
|
tearseg_xend = V.x+xc + tearseg_len*cos(tearseg_w-tearseg_delta_w);
|
|
tearseg_yend = V.y+yc + tearseg_len*sin(tearseg_w-tearseg_delta_w);
|
|
|
|
// Suggest a script command.
|
|
output_via_wire (W.layer, W.width, int(V.x+xc), int(V.y+yc), tearseg_xend, tearseg_yend, V, W, S);
|
|
// Now add segment to the center of the via so ratsnest doesn't show up as dangling.
|
|
output_via_wire (W.layer, W.width, int(V.x), int(V.y), tearseg_xend, tearseg_yend, V, W, S);
|
|
|
|
// Now the other side of the tear
|
|
tearseg_xend = V.x+xc + tearseg_len*cos(tearseg_w+tearseg_delta_w);
|
|
tearseg_yend = V.y+yc + tearseg_len*sin(tearseg_w+tearseg_delta_w);
|
|
|
|
// Suggest a script command.
|
|
output_via_wire (W.layer, W.width, int(V.x+xc), int(V.y+yc), tearseg_xend, tearseg_yend, V, W, S);
|
|
// Now add segment to the center of the via so ratsnest doesn't show up as dangling.
|
|
output_via_wire (W.layer, W.width, int(V.x), int(V.y), tearseg_xend, tearseg_yend, V, W, S);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void setlayerstatus(void) { // 2012-04-04 set layer status
|
|
string s[];
|
|
for (int n = 0; n < LayerCount; n++) {
|
|
int cnt = strsplit(s, LayerName[n], '\t');
|
|
int num = strtol(s[0]);
|
|
int target = strtol(s[1]);
|
|
IsLayer[num] = num;
|
|
TeardropLayer[num] = target;
|
|
if (s[2] == "yes" ) LayActiv[num] = 1;
|
|
else LayActiv[num] = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
void generate_tears(void) {
|
|
setlayerstatus();
|
|
board(B) {
|
|
uiw_internal = User_Ignore_Width/u2mil(1); // Convert user mil units to internal units.
|
|
via_count = 0;
|
|
fname = filesetext(B.name, "_AddTearDrops.scr");
|
|
|
|
if (!User_Vias && !User_Pads) {
|
|
dlgMessageBox("You must check vias and/or pads option.");
|
|
return;
|
|
}
|
|
|
|
output(fname, "wtD") {
|
|
printf("# Script generated to add teardrops to board vias.\n");
|
|
printf("# Script generated with '%s', *%s* at %s.\n", filename(argv[0]), EAGLE_SIGNATURE, t2string(time(), "Uyyyy-MM-dd hh:mm:ss"));
|
|
printf("# Script values in mm and spreadsheet values in mils:\n");
|
|
printf("# This script generated with the following user parameters:\n");
|
|
printf("# Teardrop Apex Radius Factor: %.2f. Ignoring segments >= %.1f mils. Ignoring segments with width/via_hole ratio >= %.1f. ",
|
|
User_Tear_Radius, User_Ignore_Width, User_Ignore_sdratio);
|
|
printf("\n");
|
|
output_hdr();
|
|
printf("SET UNDO_LOG OFF;\n");
|
|
printf("SET WIRE_BEND 2;\n"); // straight lines for drawing our teardrop wires.
|
|
printf("GRID MM;\n");
|
|
|
|
B.signals(S) {
|
|
if (User_Vias)
|
|
do_vias(S);
|
|
|
|
if (User_Pads)
|
|
do_pads(S);
|
|
}
|
|
|
|
printf("GRID LAST;\n");
|
|
printf("SET UNDO_LOG ON;\n");
|
|
|
|
if (changes) exit ("script '" + fname + "';\n");
|
|
else dlgMessageBox("No teardrops generated with current parameters");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
string settarget(string sellayer) { // 2012-04-04
|
|
string no_change = sellayer;
|
|
string l[];
|
|
strsplit(l, sellayer, '\t');
|
|
string s[];
|
|
strsplit(s, l[0], '-');
|
|
int activ;
|
|
if (l[2] == "yes") activ = 1;
|
|
|
|
int sel = strtol(l[1]) -1;
|
|
dlgDialog("Target Layer") {
|
|
dlgLabel("New layer for " + l[0]);
|
|
dlgGroup("Layer activ") {
|
|
dlgRadioButton("&no", activ);
|
|
dlgRadioButton("&yes", activ);
|
|
}
|
|
dlgComboBox(TargetLayer, sel) {
|
|
sellayer = s[0] + "\t" + TargetLayer[sel];
|
|
if (activ) sellayer += "\t" + "yes";
|
|
else sellayer += "\t" + "no";
|
|
}
|
|
dlgHBoxLayout {
|
|
dlgPushButton("+OK") {
|
|
sellayer = s[0] + "\t" + TargetLayer[sel];
|
|
if (activ) sellayer += "\t" + "yes";
|
|
else sellayer += "\t" + "no";
|
|
dlgAccept();
|
|
}
|
|
dlgPushButton("-Cancel") {
|
|
dlgReject();
|
|
sellayer = no_change;
|
|
}
|
|
}
|
|
};
|
|
return sellayer;
|
|
}
|
|
|
|
|
|
//*****************************
|
|
|
|
string mainscrtext =
|
|
"\nStatus bar will show progress while generating teardrops.\n\n"
|
|
"Apex Radius Factor sets the distance from the apex of a teardrop to the associated via's center. "
|
|
"The Radius Factor entered is multiplied by the via's drill-hole diameter to arrive at a physical distance."
|
|
"For example, with an entered radius factor of 1.5 operating on a via with a drill size of 10mils, the apex begins "
|
|
"15 mils from that particular via's center.";
|
|
|
|
if (!board) {
|
|
dlgMessageBox("You should run this ULP from an open board design.");
|
|
exit(0);
|
|
}
|
|
|
|
dlgDialog("Teardrops for Vias") {
|
|
if (!board) {
|
|
dlgMessageBox("You should run this ULP from an open board design.");
|
|
exit(-1);
|
|
}
|
|
board(B) {
|
|
B.layers(L) {
|
|
if (L.number <= 16) { // 2012-04-04
|
|
string lname;
|
|
if (L.used) {
|
|
sprintf(lname, "%d-%s\t%d-%s\tyes", L.number, L.name, L.number, L.name);
|
|
LayerName[LayerCount] = lname;
|
|
LayerNum[LayerCount] = L.number;
|
|
++LayerCount;
|
|
}
|
|
}
|
|
sprintf(TargetLayer[tCount], "%d-%s", L.number, L.name);
|
|
tCount++;
|
|
}
|
|
}
|
|
|
|
dlgHBoxLayout {
|
|
dlgTabWidget {
|
|
dlgTabPage("Processing") {
|
|
dlgHBoxLayout {
|
|
dlgGroup("Parameters") {
|
|
dlgSpacing(8);
|
|
dlgGridLayout {
|
|
dlgCell( 1, 0) dlgLabel("Teardrop Apex Radius Factor ");
|
|
dlgCell( 1, 1) dlgRealEdit(User_Tear_Radius, 0.5, 2.5);
|
|
|
|
dlgCell( 2, 0) dlgLabel("Ignore all segments >= ");
|
|
dlgCell( 2, 1) dlgRealEdit(User_Ignore_Width, 4.0, 400.0);
|
|
dlgCell( 2, 2) dlgLabel(" mils");
|
|
|
|
dlgCell( 4, 0) dlgLabel("Ignore all segments/drill ratios >= ");
|
|
dlgCell( 4, 1) dlgRealEdit(User_Ignore_sdratio, 0.1, 99.0);
|
|
}
|
|
dlgVBoxLayout {
|
|
dlgGroup("Options") {
|
|
dlgCheckBox("&Teardrop Vias", User_Vias);
|
|
dlgCheckBox("&Teardrop Pads", User_Pads);
|
|
dlgSpacing(8);
|
|
dlgLabel("Click on layer to select :");
|
|
dlgListView("Layer Source\tTeardrop Target\tactiv", LayerName, LayerSel) {
|
|
LayerName[LayerSel] = settarget(LayerName[LayerSel]);
|
|
}
|
|
}
|
|
dlgStretch(1);
|
|
dlgSpacing(10);
|
|
dlgHBoxLayout {
|
|
dlgPushButton("Teardrop Board") generate_tears();
|
|
dlgPushButton("-Cancel") dlgReject();
|
|
}
|
|
}
|
|
}
|
|
dlgTextView(mainscrtext);
|
|
}
|
|
} // tab page
|
|
|
|
dlgTabPage("Overview") {
|
|
dlgTextView(HelpText);
|
|
}
|
|
|
|
dlgTabPage("History") {
|
|
dlgTextView(Release + " " + ReleaseDate + " " + HistoryText2);
|
|
}
|
|
}
|
|
}
|
|
};
|