/* TRACERND (GED) -- Change Trace Corners to Arcs or 45 Degree Segments */ /* TRACERND (GED) -- Leiterbahnecken abschraegen/runden */ /* // Copyright (c) 1993-2012 Oliver Bartels F+E, Muenchen // Author: Manfred Baumeister // 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 (040228) ENHANCEMENT: // Added group polygon arcs remove function. // 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 (010625) RELEASED FOR BAE V5.0. // rl (001208) ENHANCEMENT: // Added trace fix state restore. // rl (000524) RELEASED FOR BAE V4.6. // rl (990625) RELEASED FOR BAE V4.4. // rl (981125) ENHANCEMENT: // Single arc point rounding support introduced. // rl (980929) RELEASED FOR BAE V4.2. // mb (980711) ENHANCEMENT: // Dynamic multi-language support introduced. // rl (971209) ENHANCEMENT // Introduced arc elimination facility. // rl (970929) RELEASED FOR BAE V4.0. // mb (960919) RELEASED FOR BAE V3.4. // mb (951013) CHANGE: // Trace processing mode query placed before radius query. // "Segment Cut Length" string included with radius query prompt. // mb (95) RELEASED FOR BAE V3.2. // mb (94) RELEASED FOR BAE V3.0. // mb (94) ENHANCEMENT: // Introduced trace corner to 45 degree conversion facilities. // mb (93) RELEASED FOR BAE V2.6. // mb (93) ORIGINAL CODING. // // DESCRIPTION // // The tracernd User Language program automatically converts // trace corners of selectable traces of the currently loaded // layout to trace 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 UPRSELPATH = M("Leiterbahn waehlen!","Select Trace!"); string UPRSELPOINT = M("Leiterbahneckpunkt waehlen!", "Select Trace Corner!"); string UPRSELSEGMENT = M("Leiterbahnsegment waehlen!", "Select Trace Segment!"); string UPRSELMODE = M("Leiterbahnecken-Umwandlungsmodus waehlen!", "Select Trace Corner Processing Mode!"); string UPRTRCRND = M("&Kreisboegen","&Arcs"); string UPRTRCSRND = M("Kreisboegen &einzeln","Arcs &Single"); string UPRTRC45 = M("&45 Grad Knicke","&45 Degrees Angles"); string UPRTRCS45 = M("45 G&rad Knicke einzeln", "45 Deg&rees Angles Single"); string UPRTRCNRND = M("%&Boegen entfernen","%&Eliminate Arcs"); string UPRTRCPRND = M("E&inzelboegen entfernen","E&liminate Arcs Single"); string UPRTRCN45 = M("45 Gr&ad Knicke entfernen", "El&iminate 45 Degree Angles"); string UPRTRCP45 = M("Ein&zel 45 Grad Knicke entfernen", "Eli&minate 45 Degree Arcs Single"); string UPRTRCGRND = M("%&Gruppe Kreisboegen","%&Group Arcs"); string UPRTRCG45 = M("Gruppe 4&5 Grad Knicke", "Group 4&5 Degrees Angles"); string UPRTRCGNRND = M("Gru&ppe Boegen entfernen", "Grou&p Eliminate Arcs"); string UPRTRCGN45 = M("Gr&uppe 45 Grad Knicke entfernen", "Gr&oup Eliminate 45 Degree Angles"); string UPRTRCRADM = M("Bahnradius/Segmentknicklaenge [mm] ? ", "Trace Radius/Segment Cut Length [mm] ? "); string UPRTRCRADI = M("Bahnradius/Segmentknicklaenge [Inch] ? ", "Trace Radius/Segment Cut Length [Inch] ? "); string UPRTRCSEGM = M("Segmentknicklaenge [mm] ? ", "Segment Cut Length [mm] ? "); string UPRTRCSEGI = M("Segmentknicklaenge [Inch] ? ", "Segment Cut Length [Inch] ? "); string ERRPATH = M("Fehler beim Erzeugen der neuen Leiterbahn!", "Error creating new trace!"); string ERRDELTRC = M("Fehler beim Loeschen der alten Leiterbahn!", "Error deleting old trace!"); string ERRTRCGLUED = M("Leiterbahn ist verankert!","Trace 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 tracerad /* Trace corner radius */; double tx, ty /* Trace corner coordinates */; int TRCMODE /* Trace 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 DEFTRCRAD = 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_LINE path /* Path index */; string path_dis = "," /* Path items disable */; string g_dis = "," /* Group items disable */; int layer /* Path layer */; double width /* Path width */; int fixed /* Path fixed flag */; int group /* Path group 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()!=DDBCLLAY && bae_planddbclass()!=DDBCLLPRT) error_class(); // Check if element type scan if (bae_iniintval(PAR_METYPSCAN,1)==1 && !bae_peekiact()) { forall (path) { path_dis=""; break; } forall (fig where fig.GROUP && fig.TYP==L_FIGPATH) { g_dis=""; break; } } else { // Enable all menu items path_dis=g_dis=""; } // Select the trace processing mode bae_defmenusel(-1); bae_promptdialog(UPRSELMODE); if ((TRCMODE=bae_askmenu(13,path_dis+UPRTRCRND,path_dis+UPRTRCSRND, path_dis+UPRTRC45,path_dis+UPRTRCS45,path_dis+UPRTRCNRND, path_dis+UPRTRCPRND,path_dis+UPRTRCN45,path_dis+UPRTRCP45, g_dis+UPRTRCGRND,g_dis+UPRTRCG45,g_dis+UPRTRCGNRND,g_dis+UPRTRCGN45, UPRABORT))<0 || TRCMODE>11) error_abort(); // Transform group modes switch (TRCMODE) { // Group round corners case 8 : TRCMODE=0; grpmode=1; break; // Group 45 degree corners case 9 : TRCMODE=2; grpmode=1; break; // Remove group trace corners case 10 : TRCMODE=4; grpmode=1; break; // Remove group 45 degree corners case 11 : TRCMODE=6; grpmode=1; break; default : } // Get the corner radius if (varget(VAR_CORNERR,tracerad)) tracerad=DEFTRCRAD; switch (TRCMODE) { // Round corners case 0 : case 1 : if (askdist( tracerad,lay_defusrunit()?UPRTRCRADI:UPRTRCRADM,0) || tracerad==0.0) // Invalid input value error(ERRINPVAL); break; // 45 degree corners case 2 : case 3 : if (askdist( tracerad,lay_defusrunit()?UPRTRCSEGI:UPRTRCSEGM,0) || tracerad==0.0) // Invalid input value error(ERRINPVAL); break; } if (grpmode) { // Save current state for undo bae_callmenu(MNU_BAESAVESTATE); forall (fig where fig.GROUP && fig.TYP==L_FIGPATH && !(fig.FIXED&2)) { // Clear the internal polygon point list bae_clearpoints(); pointfound=0; // Scan the path data if (lay_scanfelem(fig,0.0,0.0,0.0,1,0, NULL,NULL,trcrndpathfunc,NULL,NULL,NULL,NULL)) error_scan(); // Check if path points found if (pointfound) { // Get polygon data rn= fig.RULEOBJID>=0 ? rs_getfigrules(fig,rl) : 0; layer=fig.LAYER; width=fig.SIZE; fixed=fig.FIXED; // Delete old path dfig=fig; if (ged_delelem(dfig)) error(ERRDELTRC); // Store new path if (ged_storepath(layer,width)) error(ERRPATH); // Check if path 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((TRCMODE&1)==1 ? (TRCMODE==7 ? UPRSELSEGMENT : UPRSELPOINT) : UPRSELPATH); while (ged_pickelem(fig, (TRCMODE&1)==1 && TRCMODE!=7 ? L_FIGPATHC : L_FIGPATH)==0) { if (fig.FIXED&2) error(ERRTRCGLUED); // Save current state for undo if (cflag==0) { bae_callmenu(MNU_BAESAVESTATE); cflag=1; } // Get the pick point bae_wsmouse(tx,ty,0); // Clear the internal polygon point list bae_clearpoints(); pointfound=0; // Scan the path data if (lay_scanfelem(fig,0.0,0.0,0.0,1,0, NULL,NULL,trcrndpathfunc,NULL,NULL,NULL,NULL)) error_scan(); // Check if path points found if (pointfound) { // Get polygon data rn= fig.RULEOBJID>=0 ? rs_getfigrules(fig,rl) : 0; layer=fig.LAYER; width=fig.SIZE; fixed=fig.FIXED; group=fig.GROUP; // Delete old path if (ged_delelem(fig)) error(ERRDELTRC); // Store new path if (ged_storepath(layer,width)) error(ERRPATH); // Check if path should be fixed if (fixed && lay_lastfigelem(nfig)==0) ged_elemfixchg(nfig,fixed); if (group && lay_lastfigelem(nfig)==0) ged_elemgrpchg(nfig,1); // Attach rules if (rn>0 && lay_lastfigelem(nfig)==0 && lay_rulefigatt(nfig,rl)) rs_error(-1); // Make trace visible for further picks bae_postprocess(); } } } // Screen redraw screenredraw(); if (TRCMODE<4) varset(VAR_CORNERR,tracerad); // Done bae_prtdialog(REPDONE); } int trcrndpathfunc(index L_LINE path, int layer,int pathinws,index L_LEVEL level) /* // Path scan function. Generates rounded path point list // Return value : // zero if done or (-1) on error // Parameters : // index L_LINE path : Path data // int layer : Path layer // int pathinws : Path in workspace flag // index L_LEVEL level : Path level */ { struct ppoint { // Path point structure double x,y /* Point coordinates */; int typ /* Point type */; } ppl[],npl[] /* Polygon point list */; int ppn = 0 /* Path 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 tol /* Pick tolerance */; 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 i /* Loop control variable */; // Scan points forall (point of path) { // Add point to the path 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 path 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 path points if (ppn<2) return(0); // Search pick segment if (TRCMODE==7) { for (i=1;iSMALLVAL) continue; // Check the segment range if (ppl[i-1].x>ppl[i].x) { if (txppl[i-1].x) continue; } else { if (txppl[i].x) continue; } if (ppl[i-1].y>ppl[i].y) { if (typpl[i-1].y) continue; } else { if (typpl[i].y) continue; } tx=ppl[i].x; ty=ppl[i].y; break; } } // Search pick corner else if ((TRCMODE&1)==1) { mindist=(-1.0); for (i=0;i=0.0) { tx=x; ty=y; } } 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 (TRCMODE==4 || TRCMODE==5 || TRCMODE==6 || TRCMODE==7) { eidx=ppn; ppl[ppn]=ppl[1]; } else { eidx=ppn-1; } last=1; } // Set path point found flag pointfound=1; // Check if 45 degree segment elimination mode if (TRCMODE==6 || TRCMODE==7) { // Store first path 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 path 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 path 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 path 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 path 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 path polygon points for (i=0;iSMALLANG) { // Get the segment circle point distance slb=tracerad/tan(smang); // Get the bisector circle center distance wl=tracerad/sin(smang); } // Check if rounding or angle cut possible if ((TRCMODE==0 && fabs(smang)>SMALLANG) || (TRCMODE==1 && fabs(smang)>SMALLANG && fabs(x-tx)<=tol && fabs(y-ty)<=tol) || (TRCMODE==2 && (fabs(smang-PRECT)<=SMALLANG || fabs(smang-NRECT)<=SMALLANG)) || (TRCMODE==3 && fabs(x-tx)<=tol && fabs(y-ty)<=tol && (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 path first circle point npl[npn].x=x+slb*sx1; npl[npn].y=y+slb*sy1; npl[npn].typ=0; npn++; // Store path circle center point on request if (TRCMODE!=2 && TRCMODE!=3) { npl[npn].x=x+wl*wx; npl[npn].y=y+wl*wy; npl[npn].typ=rang>PI ? 2 : 1; npn++; } // Store path 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 path point unchanged npl[npn].x=ppl[i].x; npl[npn].y=ppl[i].y; npl[npn].typ=ppl[i].typ; npn++; } } // Store last path point if (last==1) { npl[npn].x=ppl[eidx].x; npl[npn].y=ppl[eidx].y; npl[npn].typ=ppl[eidx].typ; npn++; } // Store line wrap around point if (last==2) { npl[npn]=npl[0]; npn++; } // Store polygon points for (i=0;i