/* POLYRND (GED) -- Change Polygon Corners to Arcs or 45 Degree Segments */ /* POLYRND (GED) -- Polygonecken abschraegen/runden */ /* // Copyright (c) 1999-2012 Oliver Bartels F+E, Muenchen // Author: Roman Ludwig // Changes History: // rl (120427) RELEASED FOR BAE V7.8. // rl (101019) RELEASED FOR BAE V7.6. // rl (091021) RELEASED FOR BAE V7.4. // rl (081014) RELEASED FOR BAE V7.2. // rl (071029) RELEASED FOR BAE V7.0. // rl (060829) RELEASED FOR BAE V6.8. // rl (050906) RELEASED FOR BAE V6.6. // rl (050117) ENHANCEMENT: // Added single 45 degree corner function. // Added 45 degree corner deletion functions. // rl (040811) RELEASED FOR BAE V6.4. // rl (040803) ENHANCEMENT: // Added single arc delete function. // rl (040604) ENHANCEMENT: // Improved polygon corner pick. // Fixed problem with first to last segment angle. // rl (040228) ENHANCEMENT: // Added group polygon arcs remove function. // rl (031012) ENHANCEMENT: // Added documentary line wraparound corner processing. // rl (030904) RELEASED FOR BAE V6.2. // rl (021209) RELEASED FOR BAE V6.0. // rl (021112) ENHANCEMENT: // Added preservation of corner radius for current session. // rl (020618) RELEASED FOR BAE V5.4. // rl (020429) ENHANCEMENT: // Added rule transfer support. // Fixed problem with board outline processing. // rl (010625) RELEASED FOR BAE V5.0. // rl (000524) RELEASED FOR BAE V4.6. // rl (991012) RELEASED FOR BAE V4.4. // rl (990611) ORIGINAL CODING. // // DESCRIPTION // // The polyrnd User Language program automatically converts // polygon corners of selectable polygons of the currently loaded // layout to polygon arc segments with a freely selectable radius. // // NOTES // // 45 degree mode cuts 90 degree corners with 45 degree segments // at radius distance from corner. */ // Includes #include "pop.ulh" // User Language popup utilities #include "lay.ulh" // User Language layout utilities // Disable undo state request #pragma ULCALLERNOUNDO // Messages string UPRABORT = M_UPRABORT(); string REPDONE = M("Es wurden keine Fehler festgestellt.", "Operation completed without errors."); string UPRSELPOLY = M("Flaeche waehlen!","Select Polygon!"); string UPRSELPOINT = M("Flaecheneckpunkt waehlen!", "Select Polygon Corner!"); string UPRSELSEGMENT = M("Flaechensegment waehlen!", "Select Polygon Segment!"); string UPRSELMODE = M("Flaechenecken-Umwandlungsmodus waehlen!", "Select Polygon Corner Processing Mode!"); string UPRPOLYRND = M("&Kreisboegen","&Arcs"); string UPRPOLYSRND = M("Kreisboegen &einzeln","Arcs &Single"); string UPRPOLY45 = M("&45 Grad Knicke","&45 Degrees Angles"); string UPRPOLYS45 = M("45 G&rad Knicke einzeln", "45 Deg&rees Angles Single"); string UPRPOLYNRND = M("%&Boegen entfernen","%&Eliminate Arcs"); string UPRPOLYPRND = M("E&inzelboegen entfernen","E&liminate Arcs Single"); string UPRPOLYN45 = M("45 Gr&ad Knicke entfernen", "El&iminate 45 Degree Angles"); string UPRPOLYP45 = M("Ein&zel 45 Grad Knicke entfernen", "Eli&minate 45 Degree Arcs Single"); string UPRPOLYGRND = M("%&Gruppe Kreisboegen","%&Group Arcs"); string UPRPOLYG45 = M("Gruppe 4&5 Grad Knicke", "Group 4&5 Degrees Angles"); string UPRPOLYGNRND = M("Gru&ppe Boegen entfernen", "Grou&p Eliminate Arcs"); string UPRPOLYGN45 = M("Gr&uppe 45 Grad Knicke entfernen", "Gr&oup Eliminate 45 Degree Angles"); string UPRPOLYRCRADM = M("Radius [mm] ? ","Radius [mm] ? "); string UPRPOLYRCRADI = M("Radius [Inch] ? ","Radius [Inch] ? "); string UPRPOLYSEGM = M("Segmentknicklaenge [mm] ? ", "Segment Cut Length [mm] ? "); string UPRPOLYSEGI = M("Segmentknicklaenge [Inch] ? ", "Segment Cut Length [Inch] ? "); string ERRPOLY = M("Fehler beim Erzeugen der neuen Flaeche!", "Error creating new polygon!"); string ERRDELPOLY = M("Fehler beim Loeschen der alten Flaeche!", "Error deleting old polygon!"); string ERRPOLYGLUED = M("Flaeche ist verankert!","Polygon is glued!"); string ERRINPVAL = M_ERRINPVAL(); // INI file parameter name definitions #define PAR_DCORNERRAD "DCORNERR_GED" // Default corner radius [m] // Globals #define SMALLVAL 0.00000001 // Small compare value #define SMALLANG 0.00001 // Small angle compare value double polyrad /* Polygon corner radius */; double abspolyrad /* Absolute polygon corner radius */; double px, py /* Polygon corner coordinates */; int POLYMODE /* Polygon processing mode */; int pointfound /* Point found flag */; double PI = cvtangle(180.0,1,2) /* (Constant) PI value */; double PRECT = cvtangle(45.0,1,2) /* Positive rect. check value */; double NRECT = cvtangle(-45.0,1,2) /* Negative rect. check value */; double DEFPOLYRAD = bae_inidblval(PAR_DCORNERRAD,0.0002) /* Default corner radius [m] */; // Main program void main() { index L_FIGURE fig /* Figure list index */; index L_FIGURE dfig /* Delete figure list element */; index L_FIGURE nfig /* New figure list index */; index L_POLY poly /* Polygon index */; string poly_dis = "," /* Polygon items disable */; string g_dis = "," /* Group items disable */; index L_CNET cnet /* Connection list index */; string tree /* Tree name */; int layer /* Polygon layer */; int typ /* Polygon type */; int mvis /* Polygon mirror visibility */; int fixed /* Polygon fixed flag */; int grpmode = 0 /* Group processing mode */; STRINGS rl /* Rule list */; int rn /* Rule count */; int cflag = 0 /* Change flag */; // Abort if invalid plan class if (bae_planddbclass()==DDBCLUNDEF) error_class(); // Check if element type scan if (bae_iniintval(PAR_METYPSCAN,1)==1 && !bae_peekiact()) { forall (poly) { poly_dis=""; break; } forall (fig where fig.GROUP && fig.TYP==L_FIGPOLY) { g_dis=""; break; } } else { // Enable all menu items poly_dis=g_dis=""; } // Select the polygon processing mode bae_defmenusel(-1); bae_promptdialog(UPRSELMODE); if ((POLYMODE=bae_askmenu(13,poly_dis+UPRPOLYRND,poly_dis+UPRPOLYSRND, poly_dis+UPRPOLY45,poly_dis+UPRPOLYS45,poly_dis+UPRPOLYNRND, poly_dis+UPRPOLYPRND,poly_dis+UPRPOLYN45,poly_dis+UPRPOLYP45, g_dis+UPRPOLYGRND,g_dis+UPRPOLYG45,g_dis+UPRPOLYGNRND, g_dis+UPRPOLYGN45,UPRABORT))<0 || POLYMODE>11) error_abort(); // Get the corner radius if (varget(VAR_CORNERR,polyrad)) polyrad=DEFPOLYRAD; // Transform group modes switch (POLYMODE) { // Group round corners case 8 : POLYMODE=0; grpmode=1; break; // 45 degree corners case 9 : POLYMODE=2; grpmode=1; break; // Remove group polygon corners case 10 : POLYMODE=4; grpmode=1; break; // Remove group 45 degree corners case 11 : POLYMODE=6; grpmode=1; break; default : } switch (POLYMODE) { // Round corners case 0 : case 1 : if (askdist( polyrad,lay_defusrunit()?UPRPOLYRCRADI:UPRPOLYRCRADM,1) || polyrad==0.0) // Invalid input value error(ERRINPVAL); break; // 45 degree corners case 2 : case 3 : if (polyrad<0) polyrad=(-polyrad); if (askdist( polyrad,lay_defusrunit()?UPRPOLYSEGI:UPRPOLYSEGM,0) || polyrad==0.0) // Invalid input value error(ERRINPVAL); break; default : } abspolyrad=fabs(polyrad); if (grpmode) { // Save current state for undo bae_callmenu(MNU_BAESAVESTATE); // Process all group polygons forall (fig where fig.GROUP && fig.TYP==L_FIGPOLY && !(fig.FIXED&2)) { // Clear the internal polygon point list bae_clearpoints(); pointfound=0; // Scan the polygon data if (lay_scanfelem(fig,0.0,0.0,0.0,1,0, NULL,rndpolyfunc,NULL,NULL,NULL,NULL,NULL)) error_scan(); // Check if polygon points found if (pointfound) { // Get polygon data rn= fig.RULEOBJID>=0 ? rs_getfigrules(fig,rl) : 0; layer=fig.POLY.LAYER; typ=fig.POLY.TYP; mvis=fig.POLY.MVIS; fixed=fig.FIXED; if (lay_gettreeidx(fig.POLY.TREE,cnet)!=0) tree=""; else tree=cnet.NAME; // Delete old polygon dfig=fig; if (ged_delelem(dfig)) error(ERRDELPOLY); // Store new polygon if (ged_storepoly(layer,typ,tree,mvis&0x13)) error(ERRPOLY); // Check if polygon should be fixed if (fixed && lay_lastfigelem(nfig)==0) ged_elemfixchg(nfig,fixed); // Attach rules if (rn>0 && lay_lastfigelem(nfig)==0 && lay_rulefigatt(nfig,rl)) rs_error(-1); } } } else { // Loop while pick element found bae_prtdialog((POLYMODE&1)==1 ? (POLYMODE==7 ? UPRSELSEGMENT : UPRSELPOINT) : UPRSELPOLY); while (ged_pickelem(fig,((POLYMODE&1)==1 && POLYMODE!=7) ? L_FIGPOLYC : L_FIGPOLY)==0) { if (fig.FIXED&2) error(ERRPOLYGLUED); // Save current state for undo if (cflag==0) { bae_callmenu(MNU_BAESAVESTATE); cflag=1; } // Get the pick point bae_wsmouse(px,py,0); // Clear the internal polygon point list bae_clearpoints(); pointfound=0; // Scan the polygon data if (lay_scanfelem(fig,0.0,0.0,0.0,1,0, NULL,rndpolyfunc,NULL,NULL,NULL,NULL,NULL)) error_scan(); // Check if polygon points found if (pointfound) { // Get polygon data rn= fig.RULEOBJID>=0 ? rs_getfigrules(fig,rl) : 0; layer=fig.POLY.LAYER; typ=fig.POLY.TYP; mvis=fig.POLY.MVIS; fixed=fig.FIXED; if (lay_gettreeidx(fig.POLY.TREE,cnet)!=0) tree=""; else tree=cnet.NAME; // Delete old polygon dfig=fig; if (typ==L_POLYBRDOUT) if (ged_delelem(dfig)) error(ERRDELPOLY); // Store new polygon if (ged_storepoly(layer,typ,tree,mvis&0x13)) error(ERRPOLY); // Check if polygon should be fixed if (fixed && lay_lastfigelem(nfig)==0) ged_elemfixchg(nfig,fixed); // Attach rules if (rn>0 && lay_lastfigelem(nfig)==0 && lay_rulefigatt(nfig,rl)) rs_error(-1); if (typ!=L_POLYBRDOUT) { // Delete old polygon if (ged_delelem(dfig)) error(ERRDELPOLY); if (lay_lastfigelem(nfig)==0) ged_drawelem(nfig,DM_SET); } // Make polygon visible for further picks bae_postprocess(); } } } // Screen redraw screenredraw(); if (POLYMODE<3) varset(VAR_CORNERR,polyrad); // Done bae_prtdialog(REPDONE); } int rndpolyfunc(index L_POLY poly,int layer, int polyinws,int tree,index L_LEVEL level) /* // Polygon scan function. Generates rounded polygon point list // Return value : // zero if done or (-1) on error // Parameters : // index L_POLY poly : Polygon index // int layer : Polygon layer // int polyinws : Polygon in workspace flag // int tree : Polygon tree // index L_LEVEL level : Polygon level */ { struct ppoint { // Polygon point structure double x,y /* Point coordinates */; int typ /* Point type */; } ppl[],npl[] /* Polygon point list */; int ppn = 0 /* Polygon point count */; int npn = 0 /* New polygon point count */; index L_POINT point /* Polygon point index */; double x,y /* Point coordinate buffers */; double sl1,sl2,slb /* Segment lengths */; double sx1,sx2 /* Segment X coordinates */; double sy1,sy2 /* Segment Y coordinates */; double wx,wy /* Bisector vector coordinates */; double wl /* Bisector length */; double smang /* Smaller segment angle */; double rang /* Real segment angle */; double det /* Line cross determinate */; double mindist /* Minimum distance */; double dist /* Current distance */; int sidx /* Polygon start index */; int eidx /* Polygon end index */; int last /* Last point output flag */; int polytyp = poly.TYP /* Polygon type */; int i /* Loop control variable */; // Scan points forall (point of poly) { // Add point to the polygon point list ppl[ppn].x=point.X; ppl[ppn].y=point.Y; ppl[ppn].typ=point.TYP; // Check if last segment exists if (ppn>1 && point.TYP==0 && ppl[ppn-1].typ==0 && ppl[ppn-2].typ==0) { // Get the polygon segments sx1=ppl[ppn-1].x-point.X; sy1=ppl[ppn-1].y-point.Y; sx2=ppl[ppn-2].x-ppl[ppn-1].x; sy2=ppl[ppn-2].y-ppl[ppn-1].y; // Check if redundant point if ((fabs(sx1)<=SMALLVAL && fabs(sy1)<=SMALLVAL) || (fabs(sx2)<=SMALLVAL && fabs(sy2)<=SMALLVAL) || fabs(sx1*sy2-sx2*sy1)<=(SMALLVAL*SMALLVAL)) // Drop redundant point ppl[ppn-1]=ppl[ppn]; else // Increment point count ppn++; } else { // Increment point count ppn++; } } // Abort if less than 2 polygon points if (ppn<2) return(0); // Search pick segment if (POLYMODE==7) { for (i=1;iSMALLVAL) continue; // Check the segment range if (ppl[i-1].x>ppl[i].x) { if (pxppl[i-1].x) continue; } else { if (pxppl[i].x) continue; } if (ppl[i-1].y>ppl[i].y) { if (pyppl[i-1].y) continue; } else { if (pyppl[i].y) continue; } px=ppl[i].x; py=ppl[i].y; break; } } // Search pick corner else if ((POLYMODE&1)==1) { mindist=(-1.0); for (i=0;i=0.0) { px=x; py=y; } } // Test if the polygon type is valid switch (polytyp) { // Copper case L_POLYCOPPASS : // Connected copper case L_POLYCOPACT : // Documentary area case L_POLYDOCAREA : // Board outline case L_POLYBRDOUT : // Split power plane area case L_POLYSPPAREA : // Keepout area case L_POLYKEEPOUT : // Copper fill workarea case L_POLYCOPFILL : // Store the wrap around points ppl[ppn]=ppl[0]; ppl[ppn+1]=ppl[1]; ppl[ppn+2]=ppl[2]; ppl[ppn+3]=ppl[3]; // Check if arc center start point if (ppl[0].typ==0) { sidx=0; eidx=ppn+1; } else { sidx=1; eidx=ppn+2; } last=0; break; // Documentary line case L_POLYDOCLINE : if (fabs(ppl[0].x-ppl[ppn-1].x)<=SMALLVAL && fabs(ppl[0].y-ppl[ppn-1].y)<=SMALLVAL) { // Store the wrap around points ppl[ppn]=ppl[1]; ppl[ppn+1]=ppl[2]; ppl[ppn+2]=ppl[3]; ppl[ppn+3]=ppl[4]; if (ppl[1].typ==0) { sidx=0; eidx=ppn; } else { sidx=2; eidx=ppn+2; } last=2; } else { sidx=0; if (POLYMODE==4 || POLYMODE==5 || POLYMODE==6 || POLYMODE==7) { eidx=ppn; ppl[ppn]=ppl[1]; } else { eidx=ppn-1; } last=1; } break; // Others default : return(0); } // Set polygon point found flag pointfound=1; // Check if 45 degree segment elimination mode if (POLYMODE==6 || POLYMODE==7) { // Store first polygon point npl[npn].x=ppl[sidx].x; npl[npn].y=ppl[sidx].y; npl[npn].typ=ppl[sidx].typ; npn++; // Loop for all segments for (i=sidx+1;iSMALLVAL) continue; if (i>1 && ppl[i-2].typ==0) { // Get previous polygon segment vector if (fabs(ppl[i-2].x-ppl[i-1].x)<=SMALLVAL) ppl[i-1].y=ppl[i].y; else if (fabs(ppl[i-2].y-ppl[i-1].y)<=SMALLVAL) ppl[i-1].x=ppl[i].x; } else if (i<(ppn-1) && ppl[i+1].typ==0) { // Compare previous polygon segment vector if (fabs(ppl[i+1].x-ppl[i].x)<=SMALLVAL) ppl[i].y=ppl[i-1].y; else if (fabs(ppl[i+1].y-ppl[i].y)<=SMALLVAL) ppl[i].x=ppl[i-1].x; } } // Store optimized polygon for (i=sidx+1;i2 && fabs(npl[0].x-npl[npn-1].x)<=SMALLVAL && fabs(npl[0].y-npl[npn-1].y)<=SMALLVAL) { sx1=npl[npn-1].x-npl[npn-2].x; sy1=npl[npn-1].y-npl[npn-2].y; sx2=npl[0].x-npl[1].x; sy2=npl[0].y-npl[1].y; // Check if redundant point if ((fabs(sx1)<=SMALLVAL && fabs(sy1)<=SMALLVAL) || (fabs(sx2)<=SMALLVAL && fabs(sy2)<=SMALLVAL) || fabs(sx1*sy2-sx2*sy1)<=(SMALLVAL*SMALLVAL)) { // Remove redundant point npl[0]=npl[npn-2]; npn--; } } else if (last==2) { sx1=npl[npn-1].x-npl[0].x; sy1=npl[npn-1].y-npl[0].y; sx2=npl[0].x-npl[1].x; sy2=npl[0].y-npl[1].y; // Check if redundant point if ((fabs(sx1)<=SMALLVAL && fabs(sy1)<=SMALLVAL) || (fabs(sx2)<=SMALLVAL && fabs(sy2)<=SMALLVAL) || fabs(sx1*sy2-sx2*sy1)<=(SMALLVAL*SMALLVAL)) { // Remove redundant point npl[0]=npl[npn-1]; npn--; } } // Store polygon points for (i=0;i2 && fabs(npl[0].x-npl[npn-1].x)<=SMALLVAL && fabs(npl[0].y-npl[npn-1].y)<=SMALLVAL) { sx1=npl[npn-1].x-npl[npn-2].x; sy1=npl[npn-1].y-npl[npn-2].y; sx2=npl[0].x-npl[1].x; sy2=npl[0].y-npl[1].y; // Check if redundant point if ((fabs(sx1)<=SMALLVAL && fabs(sy1)<=SMALLVAL) || (fabs(sx2)<=SMALLVAL && fabs(sy2)<=SMALLVAL) || fabs(sx1*sy2-sx2*sy1)<=(SMALLVAL*SMALLVAL)) { // Remove redundant point npl[0]=npl[npn-2]; npn--; } } else if (last==2) { sx1=npl[npn-1].x-npl[0].x; sy1=npl[npn-1].y-npl[0].y; sx2=npl[0].x-npl[1].x; sy2=npl[0].y-npl[1].y; // Check if redundant point if ((fabs(sx1)<=SMALLVAL && fabs(sy1)<=SMALLVAL) || (fabs(sx2)<=SMALLVAL && fabs(sy2)<=SMALLVAL) || fabs(sx1*sy2-sx2*sy1)<=(SMALLVAL*SMALLVAL)) { // Remove redundant point npl[0]=npl[npn-1]; npn--; } } // Store polygon points for (i=0;iSMALLANG) { // Get the segment circle point distance if (polyrad<0.0) { slb=(-polyrad); wl=0.0; } else { slb=abspolyrad/tan(smang); // Get the bisector circle center distance wl=abspolyrad/sin(smang); } } // Check if rounding or angle cut possible if ((POLYMODE==0 && fabs(smang)>SMALLANG) || (POLYMODE==1 && fabs(smang)>SMALLANG && x==px && y==py) || (POLYMODE==2 && (fabs(smang-PRECT)<=SMALLANG || fabs(smang-NRECT)<=SMALLANG)) || (POLYMODE==3 && x==px && y==py && (fabs(smang-PRECT)<=SMALLANG || fabs(smang-NRECT)<=SMALLANG))) { // 1st segment shorter than required for rad ? if (slb>sl1) { // Adjust lengths wl*=sl1/slb; slb=sl1; } // 2nd segment shorter than required for rad ? if (slb>sl2) { // Adjust lengths wl*=sl2/slb; slb=sl2; } // Get real angle rang= fmod(atan2(sx2,sy2)-atan2(sx1,sy1)+2.0*PI,2.0*PI); // Store polygon first circle point npl[npn].x=x+slb*sx1; npl[npn].y=y+slb*sy1; npl[npn].typ=0; npn++; // Store polygon circle center point on request if (POLYMODE!=2 && POLYMODE!=3) { if (polyrad<0.0) { npl[npn].x=x; npl[npn].y=y; npl[npn].typ=rang>PI ? 1 : 2; } else { npl[npn].x=x+wl*wx; npl[npn].y=y+wl*wy; npl[npn].typ=rang>PI ? 2 : 1; } npn++; } // Store polygon second circle point npl[npn].x=x+slb*sx2; npl[npn].y=y+slb*sy2; npl[npn].typ=0; npn++; // Adjust point list ppl[i].x=x+slb*sx2; ppl[i].y=y+slb*sy2; } else { // Store polygon point unchanged npl[npn].x=ppl[i].x; npl[npn].y=ppl[i].y; npl[npn].typ=ppl[i].typ; npn++; } } // Store last polygon point if (last==1) { npl[npn].x=ppl[eidx].x; npl[npn].y=ppl[eidx].y; npl[npn].typ=ppl[eidx].typ; npn++; } // Store documentary line wrap around point if (last==2 && sidx!=2) { npl[npn]=npl[0]; npn++; } // Store polygon points for (i=0;i