/** \file cgroup.c * Compound tracks: Group * */ /* XTrkCad - Model Railroad CAD * Copyright (C) 2005 Dave Bullis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cselect.h" #include "compound.h" #include "cundo.h" #include "custom.h" #include "fileio.h" #include "tbezier.h" #include "tcornu.h" #include "common.h" #include "param.h" #include "shrtpath.h" #include "track.h" #include "trkendpt.h" #include "common-ui.h" /* * Note: Bumper support * Currently Ungroup will convert 1 ended Turnouts (Bumpers) to simple 2-ended Straight * Group will remove any Bumpers from the Selected track list * See TODO-BUMPER * * The remaining issue is that the ShortestPath logic aborts if it gets to the end of a * path elem list and can't find the corresponing EP. */ /***************************************************************************** * * Ungroup / Group * */ static int log_group=-1; static dynArr_t pathPtr_da; #define pathPtr(N) DYNARR_N( char, pathPtr_da, N ) static char groupManuf[STR_SIZE]; static char groupDesc[STR_SIZE]; static char groupPartno[STR_SIZE]; static char groupTitle[STR_LONG_SIZE]; static int groupCompoundCount = 0; /***************************************************************************** * * Ungroup * */ typedef struct { int segInx; EPINX_T segEP; int inx; track_p trk; } mergePt_t; static dynArr_t mergePt_da; #define mergePt(N) DYNARR_N( mergePt_t, mergePt_da, N ) static void AddMergePt( int segInx, EPINX_T segEP ) { int inx; mergePt_t * mp; for ( inx=0; inxsegInx == segInx && mp->segEP == segEP ) { return; } } DYNARR_APPEND( mergePt_t, mergePt_da, 10 ); mp = &mergePt(mergePt_da.cnt-1); mp->segInx = segInx; mp->segEP = segEP; mp->inx = mergePt_da.cnt-1; LOG( log_group, 2, ( " MergePt: %d.%d\n", segInx, segEP ) ); } static EPINX_T FindEP( EPINX_T epCnt, trkEndPt_p endPts, coOrd pos ) { DIST_T dist; EPINX_T ep; for ( ep=0; epinx == mpInx ) { mp->inx = map[segInx]; } } } static void GroupCopyTitle( char * title ) { char *mP, *nP, *pP; int mL, nL, pL; ParseCompoundTitle( title, &mP, &mL, &nP, &nL, &pP, &pL ); if ( strncmp( nP, "Ungrouped ", 10 ) == 0 ) { nP += 10; nL -= 10; } if ( ++groupCompoundCount == 1 ) { strncpy( groupManuf, mP, mL ); groupManuf[mL] = '\0'; strncpy( groupDesc, nP, nL ); groupDesc[nL] = '\0'; strncpy( groupPartno, pP, pL ); groupPartno[pL] = '\0'; } else { if ( mL != (int)strlen( groupManuf ) || strncmp( groupManuf, mP, mL ) != 0 ) { groupManuf[0] = '\0'; } if ( nL != (int)strlen( groupDesc ) || strncmp( groupDesc, nP, nL ) != 0 ) { groupDesc[0] = '\0'; } if ( pL != (int)strlen( groupPartno ) || strncmp( groupPartno, pP, pL ) != 0 ) { groupPartno[0] = '\0'; } } } // TODO-BUMPER - handle paths which don't end on an EP // Set GROUP_BUMPER_REFCOUT // to 1 to convert Bumper tracks to Straight tracks with 2 EP // to 2 to create 1 EP Turnout tracks #define GROUP_BUMPER_REFCOUNT (1) EXPORT void UngroupCompound( track_p trk ) { struct extraDataCompound_t *xx = GET_EXTRA_DATA(trk, T_NOTRACK, extraDataCompound_t); struct extraDataCompound_t *xx1; trkSeg_p sp; track_p trk0, trk1; int segCnt, segInx, segInx1; EPINX_T ep, epCnt, epCnt1=0, segEP, segEP1, eps[2]; char * cp; coOrd pos, orig, size; ANGLE_T angle; int inx; int off; mergePt_t * mp; trkEndPt_p epp; segProcData_t segProcData; static dynArr_t refCount_da; #define refCount(N) DYNARR_N( int, refCount_da, N ) typedef struct { track_p trk; EPINX_T ep[2]; } segTrack_t; #define segTrack(N) DYNARR_N( segTrack_t, segTrack_da, N ) static dynArr_t segTrack_da; segTrack_t * stp, * stp1; BOOL_T turnoutChanged; DYNARR_RESET( mergePt_t, mergePt_da ); DYNARR_RESET( int, refCount_da ); DYNARR_RESET( segTrack_t, segTrack_da ); GroupCopyTitle( xtitle(xx) ); #ifdef LATER for ( sp=sq=xx->segs; sp<&xx->segs[xx->segCnt]; sp++ ) { if ( IsSegTrack(sp) ) { *sq = *sp; sq++; } else { trk1 = MakeDrawFromSeg( xx->orig, xx->angle, sp ); if ( trk1 ) { SetTrkBits( trk1, TB_SELECTED ); DrawNewTrack( trk1 ); } } } if ( GetTrkEndPtCnt(trk) <= 0 ) { UndoDelete( trk ); return; } #endif LOG( log_group, 1, ( "Ungroup( T%d )\n", GetTrkIndex(trk) ) ); epCnt = GetTrkEndPtCnt(trk); segCnt = xx->segCnt; int trackCount = 0; for ( sp=xx->segs; sp<&xx->segs[xx->segCnt]; sp++ ) { if (IsSegTrack(sp)) { trackCount++; } } //CHECK( (epCnt==0) == (segCnt==0) ); CHECK( (epCnt==0) == (trackCount==0) ); turnoutChanged = FALSE; if ( epCnt > 0 ) { turnoutChanged = TRUE; /* 1: collect EPs */ TempEndPtsSet( epCnt ); DYNARR_SET( segTrack_t, segTrack_da, segCnt ); memset( &segTrack(0), 0, segCnt * sizeof segTrack(0) ); for ( ep=0; eporig, -xx->angle ); pos.x -= xx->orig.x; pos.y -= xx->orig.y; ANGLE_T angle = GetTrkEndAngle( trk, ep ); track_p trk1 = GetTrkEndTrk( trk, ep ); EPINX_T ep1; ep1 = trk1 ? GetEndPtConnectedToMe( trk1, trk ) : -1 ; SetEndPt( epp, pos, angle ); SetEndPtTrack( epp, trk1 ); // Remember what EP on trk1 was connecting to me SetEndPtEndPt( epp, ep1 ); LOG( log_group, 1, ( " EP%d = [%0.3f %0.3f] A%0.3f T%d.%d\n", ep, pos.x, pos.y, angle, trk1?GetTrkIndex(trk1):-1, ep1 ) ); } /* 3: Count number of times each segment is referenced * If the refcount differs between adjacent segments * add segment with smaller count to mergePts * Treat EndPts as a phantom segment with inx above segCnt * Path ends that don't map onto a real EndPt (bumpers) get a virtual EP */ DYNARR_SET( int, refCount_da, segCnt+epCnt ); memset( &refCount(0), 0, refCount_da.cnt * sizeof *(int*)0 ); cp = (char *)GetPaths( trk ); while ( cp[0] ) { cp += strlen(cp)+1; while ( cp[0] ) { // Process 1st seg in sub-path GetSegInxEP( cp[0], &segInx, &segEP ); // Find EP its connected to pos = GetSegEndPt( xx->segs+segInx, segEP, FALSE, NULL ); segInx1 = FindEP( TempEndPtsCount(), TempEndPt(0), pos ); if ( segInx1 >= 0 ) { // Found existing EP, incr it's refCount segInx1 += segCnt; if ( segInx1 >= refCount_da.cnt ) { InputError( "Invalid segInx1 %d", TRUE, segInx1 ); return; } refCount(segInx1)++; } else { // No existing EP: must be a bumper, add virtual EP epp = TempEndPtsAppend(); DYNARR_APPEND( int, refCount_da, 10 ); SetEndPt( epp, pos, 0 ); segInx1 = refCount_da.cnt-1; refCount(segInx1) = GROUP_BUMPER_REFCOUNT; } segEP1 = 0; while ( cp[0] ) { // Process remaining segs GetSegInxEP( cp[0], &segInx, &segEP ); if ( segInx1 >= refCount_da.cnt ) { InputError( "Invalid segInx1 %d", TRUE, segInx1 ); return; } // Incr it's refCoount refCount(segInx)++; // Is my refCount > then previous seg/EP? if ( refCount(segInx) > refCount(segInx1) ) { AddMergePt( segInx, segEP ); } // Is previous seg/EP refCount > my refCount if ( refCount(segInx1) > refCount(segInx) ) { AddMergePt( segInx1, segEP1 ); } // Advance to next seg segInx1 = segInx; segEP1 = 1-segEP; cp++; } // Process last seg in sub-path GetSegInxEP( cp[-1], &segInx, &segEP ); // Find EP its connected to pos = GetSegEndPt( xx->segs+segInx, 1-segEP, FALSE, NULL ); segInx = FindEP( TempEndPtsCount(), TempEndPt(0), pos ); if ( segInx >= 0 ) { // Found EP, incr refCount segInx += segCnt; refCount(segInx)++; } else { // No existing EP: must be a bumper, add virtual EP epp = TempEndPtsAppend(); DYNARR_APPEND( int, refCount_da, 10 ); SetEndPt( epp, pos, 0 ); segInx = refCount_da.cnt-1; refCount(segInx) = GROUP_BUMPER_REFCOUNT; } if ( refCount(segInx) > refCount(segInx1) ) { AddMergePt( segInx, 0 ); } cp++; } cp++; } /* 4: For each path element, map segment to a mergePt if the adjacent segment * and EP is a mergePt * If segment is already mapped then merge mergePts */ DYNARR_SET( int, refCount_da, segCnt ); memset( &refCount(0), -1, segCnt * sizeof *(int*)0 ); cp = (char *)GetPaths( trk ); while ( cp[0] ) { cp += strlen(cp)+1; while ( cp[0] ) { GetSegInxEP( cp[0], &segInx, &segEP ); pos = GetSegEndPt( xx->segs+segInx, segEP, FALSE, NULL ); /*REORIGIN1( pos, xx->angle, xx->orig );*/ segInx1 = FindEP( TempEndPtsCount(), TempEndPt(0), pos ); if ( segInx1 >= 0 ) { segInx1 += segCnt; } segEP1 = 0; while ( cp[0] ) { GetSegInxEP( cp[0], &segInx, &segEP ); if ( segInx1 >= 0 ) { for ( inx=0; inxsegInx == segInx1 && mp->segEP == segEP1 ) { SegOnMP( segInx, mp->inx, segCnt, &refCount(0) ); } if ( mp->segInx == segInx && mp->segEP == segEP ) { SegOnMP( segInx1, mp->inx, segCnt, &refCount(0) ); } } } segInx1 = segInx; segEP1 = 1-segEP; cp++; } GetSegInxEP( cp[-1], &segInx, &segEP ); pos = GetSegEndPt( xx->segs+segInx, 1-segEP, FALSE, NULL ); /*REORIGIN1( pos, xx->angle, xx->orig );*/ segInx = FindEP( TempEndPtsCount(), TempEndPt(0), pos ); if ( segInx >= 0 ) { segInx += segCnt; for ( inx=0; inxsegInx == segInx && mp->segEP == 0 ) { SegOnMP( segInx1, mp->inx, segCnt, &refCount(0) ); } } } cp++; } cp++; } /* 5: Check is all segments are on the same mergePt, which means there is nothing to do */ if ( mergePt_da.cnt > 0 ) { for ( segInx=0; segInxsegCnt ) { /* no non-track segments to remove */ return; } } } } /* 6: disconnect, undraw, remove non-track segs, return if there is nothing else to do */ wDrawDelayUpdate( mainD.d, TRUE ); if ( turnoutChanged ) { for ( ep=0; epsegs; sp<&xx->segs[xx->segCnt]; sp++ ) { if ( ! IsSegTrack(sp) ) { trk1 = MakeDrawFromSeg( xx->orig, xx->angle, sp ); if ( trk1 ) { SetTrkBits( trk1, TB_SELECTED ); DrawNewTrack( trk1 ); } } } if ( !turnoutChanged ) { if ( epCnt <= 0 ) { trackCount--; UndoDelete( trk ); } else { UndoModify( trk ); xx->segCnt = segCnt; DrawNewTrack( trk ); } wDrawDelayUpdate( mainD.d, FALSE ); return; } /* 7: for each valid mergePt, create a new turnout */ for ( inx=0; inxinx != inx ) { continue; } DYNARR_RESET( trkSeg_t, tempSegs_da ); DYNARR_RESET( char, pathPtr_da ); // Mark start of virtual EPs for this MergePt epCnt1 = TempEndPtsCount(); for ( segInx=0; segInxsegs[segInx]; sprintf( message, "P%d", segInx ); off = pathPtr_da.cnt; DYNARR_SET( char, pathPtr_da, off+(int)strlen(message)+4 ); strcpy( &pathPtr(off), message ); off = pathPtr_da.cnt-3; pathPtr(off+0) = (char)tempSegs_da.cnt; pathPtr(off+1) = '\0'; pathPtr(off+2) = '\0'; for ( ep=0; ep<2; ep++ ) { pos = GetSegEndPt( xx->segs+segInx, ep, FALSE, &angle ); segEP = FindEP( epCnt1, TempEndPt(0), pos ); if ( segEP >= 0 && segEP >= epCnt && segEP < epCnt1 ) { /* was a bumper: no EP */ eps[ep] = -1; // TODO-BUMPER To support Bumpers remove this continue continue; } REORIGIN1( pos, xx->angle, xx->orig ); angle = NormalizeAngle( xx->angle+angle ); eps[ep] = -1; if ( TempEndPtsCount()-epCnt1 > 0 ) { eps[ep] = FindEP( TempEndPtsCount()-epCnt1, TempEndPt(epCnt1), pos ); } if ( eps[ep] < 0 ) { epp = TempEndPtsAppend(); eps[ep] = TempEndPtsCount()-1-epCnt1; SetEndPt( epp, pos, angle ); } } segTrack(segInx).ep[0] = eps[0]; segTrack(segInx).ep[1] = eps[1]; } } DYNARR_SET( char, pathPtr_da, pathPtr_da.cnt+1 ); pathPtr(pathPtr_da.cnt-1) = '\0'; CHECK ( tempSegs_da.cnt != 0 ); GetSegBounds( zero, 0, tempSegs_da.cnt, &tempSegs(0), &orig, &size ); orig.x = -orig.x; orig.y = -orig.y; MoveSegs( tempSegs_da.cnt, &tempSegs(0), orig ); Rotate( &orig, zero, xx->angle ); orig.x = xx->orig.x - orig.x; orig.y = xx->orig.y - orig.y; trk1 = NewCompound( T_TURNOUT, 0, orig, xx->angle, xx->title, TempEndPtsCount()-epCnt1, TempEndPt(epCnt1), (PATHPTR_T)&pathPtr(0), tempSegs_da.cnt, &tempSegs(0) ); xx1 = GET_EXTRA_DATA(trk1, T_TURNOUT, extraDataCompound_t); xx1->ungrouped = TRUE; xx1->pathOverRide = xx->pathOverRide; xx1->pathNoCombine = xx->pathNoCombine; SetTrkVisible( trk1, TRUE ); SetTrkNoTies( trk1, FALSE ); SetTrkBits( trk1, TB_SELECTED ); for ( segInx=0; segInxtrk = trk1; } /* 8: for remaining segments, create simple tracks */ for ( segInx=0; segInx= 0 ) { continue; } if ( ! IsSegTrack( xx->segs+segInx ) ) { continue; } SegProc( SEGPROC_NEWTRACK, xx->segs+segInx, &segProcData ); SetTrkScale( segProcData.newTrack.trk, GetTrkScale(trk) ); SetTrkBits( segProcData.newTrack.trk, TB_SELECTED ); MoveTrack( segProcData.newTrack.trk, xx->orig ); RotateTrack( segProcData.newTrack.trk, xx->orig, xx->angle ); segTrack(segInx).trk = segProcData.newTrack.trk; segTrack(segInx).ep[0] = segProcData.newTrack.ep[0]; segTrack(segInx).ep[1] = segProcData.newTrack.ep[1]; } /* 9: reconnect tracks */ cp = (char *)GetPaths( trk ); while ( cp[0] ) { cp += strlen(cp)+1; while ( cp[0] ) { /* joint EP to this segment */ GetSegInxEP( cp[0], &segInx, &segEP ); stp = &segTrack(segInx); ep = FindEP( epCnt, TempEndPt(0), GetSegEndPt( xx->segs+segInx, segEP, FALSE, NULL ) ); if ( ep >= 0 ) { epp = TempEndPt(ep); track_p trk1 = GetEndPtTrack(epp); if ( trk1 ) { EPINX_T ep1 = GetEndPtEndPt(epp); ConnectTracks( stp->trk, stp->ep[segEP], trk1, ep1 ); DrawEndPt( &mainD, trk1, ep1, GetTrkColor(trk1, &mainD) ); SetEndPtTrack( epp, NULL ); // Finished with this EP } } stp1 = stp; segEP1 = 1-segEP; cp++; while ( cp[0] ) { GetSegInxEP( cp[0], &segInx, &segEP ); stp = &segTrack(segInx); // Check EPs are not virtual (Bumpers) // TODO-BUMPER May not be necessary CHECK( stp->ep[segEP] >= 0 ); CHECK( stp1->ep[segEP1] >= 0 ); trk0 = GetTrkEndTrk( stp->trk, stp->ep[segEP] ); trk1 = GetTrkEndTrk( stp1->trk, stp1->ep[segEP1] ); if ( trk0 == NULL ) { CHECK ( trk1 == NULL ); ConnectTracks( stp->trk, stp->ep[segEP], stp1->trk, stp1->ep[segEP1] ); } else { CHECK( trk1 == stp->trk ); CHECK( stp1->trk == trk0 ); // ungroup: last seg not connected to curr } stp1 = stp; segEP1 = 1-segEP; cp++; } /* joint EP to last segment */ ep = FindEP( epCnt, TempEndPt(0), GetSegEndPt( xx->segs+segInx, segEP1, FALSE, NULL ) ); if ( ep > 0 ) { epp = TempEndPt(ep); track_p trk1 = GetEndPtTrack( epp ); if ( trk1 ) { EPINX_T ep1 = GetEndPtEndPt( epp ); ConnectTracks( stp1->trk, stp1->ep[segEP1], trk1, ep1 ); DrawEndPt( &mainD, trk1, ep1, wDrawColorWhite ); SetEndPtTrack( epp, NULL ); // Finished with this EP } } cp++; } cp++; } /* 10: cleanup: delete old track, draw new tracks */ UndoDelete( trk ); trackCount--; for ( segInx=0; segInx= 0 ) { mp = &mergePt( refCount(segInx) ); if ( mp->trk ) { DrawNewTrack( mp->trk ); mp->trk = NULL; } } else { if ( segTrack(segInx).trk ) { DrawNewTrack( segTrack(segInx).trk ); } } } wDrawDelayUpdate( mainD.d, FALSE ); } EXPORT void DoUngroup( void * unused ) { track_p trk = NULL; int ungroupCnt; int oldTrackCount; TRKINX_T lastTrackIndex; if ( log_group < 0 ) { log_group = LogFindIndex( "group" ); } groupManuf[0] = 0; groupDesc[0] = 0; groupPartno[0] = 0; ungroupCnt = 0; oldTrackCount = trackCount; UndoStart( _("Ungroup Object"), "Ungroup Objects" ); lastTrackIndex = max_index; groupCompoundCount = 0; while ( TrackIterate( &trk ) ) { if ( GetTrkSelected( trk ) && GetTrkIndex(trk) <= lastTrackIndex ) { oldTrackCount = trackCount; UngroupTrack( trk ); if ( oldTrackCount != trackCount ) { ungroupCnt++; } } } if ( ungroupCnt ) { InfoMessage( _("%d objects ungrouped"), ungroupCnt ); } else { InfoMessage( _("No objects ungrouped") ); } } static drawCmd_t groupD = { NULL, &tempSegDrawFuncs, DC_SEGTRACK, 1, 0.0, {0.0, 0.0}, {0.0, 0.0}, Pix2CoOrd, CoOrd2Pix }; static long groupSegCnt; static long groupReplace; static long groupNoCombine; static double groupOriginX; static double groupOriginY; char * groupReplaceLabels[] = { N_("Replace with new group?"), NULL }; char * groupNoCombineLabels[] = { N_("Turntable/TransferTable/DblSlipSwith?"), NULL }; static wWin_p groupW; static paramIntegerRange_t r0_999999 = { 0, 999999 }; static paramFloatRange_t r_1000_1000 = { -1000.0, 1000.0, 80 }; static paramData_t groupPLs[] = { /*0*/ { PD_STRING, groupManuf, "manuf", PDO_NOPREF | PDO_NOTBLANK, I2VP(350), N_("Manufacturer"), 0, 0, sizeof(groupManuf)}, /*1*/ { PD_STRING, groupDesc, "desc", PDO_NOPREF | PDO_NOTBLANK, I2VP(230), N_("Description"), 0, 0, sizeof(groupDesc)}, /*2*/ { PD_STRING, groupPartno, "partno", PDO_NOPREF|PDO_DLGHORZ|PDO_DLGIGNORELABELWIDTH|PDO_NOTBLANK, I2VP(100), N_("#"), 0, 0, sizeof(groupPartno)}, /*3*/ { PD_LONG, &groupSegCnt, "segcnt", PDO_NOPREF, &r0_999999, N_("# Segments"), BO_READONLY }, #define I_GROUP_ORIGIN_OFFSET 4 /* Need to change if add above */ /*4*/ { PD_FLOAT, &groupOriginX, "orig", PDO_DIM, &r_1000_1000, N_("Offset X,Y:")}, /*5*/ { PD_FLOAT, &groupOriginY, "origy",PDO_DIM | PDO_DLGHORZ, &r_1000_1000, ""}, /*6*/ { PD_TOGGLE, &groupNoCombine, "noCombine", 0, groupNoCombineLabels, "", BC_HORZ|BC_NOBORDER }, /*7*/ { PD_TOGGLE, &groupReplace, "replace", 0, groupReplaceLabels, "", BC_HORZ|BC_NOBORDER } }; static paramGroup_t groupPG = { "group", 0, groupPLs, COUNT( groupPLs ) }; typedef struct { track_p trk; int segStart; int segEnd; int totalSegStart; //Where we are overall int totalSegEnd; } groupTrk_t, * groupTrk_p; static dynArr_t groupTrk_da; #define groupTrk(N) DYNARR_N( groupTrk_t, groupTrk_da, N ) typedef struct { int groupInx; EPINX_T ep1, ep2; PATHPTR_T path; BOOL_T flip; } pathElem_t, *pathElem_p; typedef struct { int pathElemStart; int pathElemEnd; EPINX_T ep1, ep2; int conflicts; BOOL_T inGroup; BOOL_T done; } path_t, *path_p; static dynArr_t path_da; #define path(N) DYNARR_N( path_t, path_da, N ) static dynArr_t pathElem_da; #define pathElem(N) DYNARR_N( pathElem_t, pathElem_da, N ) static int pathElemStart; /* * Find sub-path that connects the 2 EPs for the given track * * \param trk IN Track * \param ep1, ep2 IN EndPt index * \param BOOL_T *flip OUT whether path is flipped * \return sub-path that connects the 2 EPs */ static char * FindPathBtwEP( track_p trk, EPINX_T ep1, EPINX_T ep2, BOOL_T * flip ) { char * cp; coOrd trkPos[2]; LOG( log_group, 3, (" FindPathBtwEP: T%d .%d .%d = ", trk?GetTrkIndex(trk):-1, ep1, ep2 )); if ( GetTrkType(trk) != T_TURNOUT ) { CHECK( ep1+ep2 == 1 ); *flip = ( ep1 == 1 ); if (GetTrkType(trk) == T_CORNU ) { // Cornu doesn't have a path but lots of segs! cp = CreateSegPathList(trk); // Make path LOG( log_group, 2, ( " Group: Cornu path:%s \n", cp ) ) } else { cp = "\1\0\0"; } //One segment (but could be a Bezier) LOG( log_group, 3, (" Flip:%s Path= Seg=%d-\n", *flip?"T":"F", *cp ) ); return cp; } struct extraDataCompound_t * xx = GET_EXTRA_DATA( trk, T_TURNOUT, extraDataCompound_t ); cp = (char *)GetPaths( trk ); trkPos[0] = GetTrkEndPos(trk,ep1); Rotate( &trkPos[0], xx->orig, -xx->angle ); trkPos[0].x -= xx->orig.x; trkPos[0].y -= xx->orig.y; trkPos[1] = GetTrkEndPos(trk,ep2); Rotate( &trkPos[1], xx->orig, -xx->angle ); trkPos[1].x -= xx->orig.x; trkPos[1].y -= xx->orig.y; DIST_T dist = 1000.0; char * path = NULL; char * pName = "Not Found"; while ( cp[0] ) { char * pName1 = cp; // Save path name cp += strlen(cp)+1; while ( cp[0] ) { int segInx; int segEP; coOrd segPos[2]; // Check if this sub-path endpts match the requested endpts char * path1 = cp; // get the seg indices for the start and end GetSegInxEP( cp[0], &segInx, &segEP ); segPos[0] = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL ); cp += strlen(cp); GetSegInxEP( cp[-1], &segInx, &segEP ); segPos[1] = GetSegEndPt( &xx->segs[segInx], 1-segEP, FALSE, NULL ); // Find the closest seg end for ( int inx = 0; inx<2; inx++ ) { // Check 1st end DIST_T dist1 = FindDistance( trkPos[0], segPos[inx] ); if ( dist1 < connectDistance && dist1 < dist ) { // Closest so far, Check 2nd end DIST_T dist2 = FindDistance( trkPos[1], segPos[1-inx] ); if ( dist2 > dist1 ) // 2nd end is further away { dist1 = dist2; } if ( dist1 < connectDistance && dist1 < dist ) { // both ends are closest dist = dist1; path = path1; pName = pName1; *flip = (inx==1); } } } cp++; } cp++; } LOG( log_group, 3, (" %s: %d..%d Flip:%s\n", pName, path?path[0]:-1, path?path[strlen(path)-1]:-1, *flip?"T":"F" ) ); return path; } static int GroupShortestPathFunc( SPTF_CMD cmd, track_p trk, EPINX_T ep1, EPINX_T ep2, DIST_T dist, void * data ) { track_p trk1; path_t *pp; pathElem_t *ppp; BOOL_T flip; int inx; EPINX_T ep; coOrd pos1, pos2; ANGLE_T angle, ang1, ang2; switch ( cmd ) { case SPTC_MATCH: if ( !GetTrkSelected(trk) ) { return 0; } // TODO-BUMPER may not be necessary if ( GetTrkEndPtCnt(trk) < 2 && ep1 >= 1 ) { return 1; } trk1 = GetTrkEndTrk(trk,ep1); if ( trk1 == NULL ) { return 1; } if ( !GetTrkSelected(trk1) ) { return 1; } return 0; case SPTC_MATCHANY: return -1; case SPTC_ADD_TRK: LOG( log_group, 4, ( " Add T%d[%d]\n", GetTrkIndex(trk), ep2 ) ) DYNARR_APPEND( pathElem_t, pathElem_da, 10 ); ppp = &pathElem(pathElem_da.cnt-1); for ( inx=0; inxgroupInx = inx; ppp->ep1 = ep1; ppp->ep2 = ep2; ppp->path = (PATHPTR_T)FindPathBtwEP( trk, ep1, ep2, &ppp->flip ); return 0; } } CHECKMSG( FALSE, ( "GroupShortestPathFunc(SPTC_ADD_TRK, T%d) - track not in group", GetTrkIndex(trk) ) ); case SPTC_TERMINATE: ppp = &pathElem(pathElemStart); trk = groupTrk(ppp->groupInx).trk; pos1 = GetTrkEndPos( trk, ppp->ep2 ); ang1 = GetTrkEndAngle( trk, ppp->ep2 ); ppp = &pathElem(pathElem_da.cnt-1); trk = groupTrk(ppp->groupInx).trk; pos2 = GetTrkEndPos( trk, ppp->ep1 ); ang2 = GetTrkEndAngle( trk, ppp->ep1 ); ep1 = ep2 = -1; for ( ep=0; epep1 == ep1 || pp->ep2 == ep1 ) ) && ( ep2 < 0 || ( pp->ep1 == ep2 || pp->ep2 == ep2 ) ) ) { LOG( log_group, 4, ( " Remove: duplicate path P%d\n", inx ) ) DYNARR_SET( pathElem_t, pathElem_da, pathElemStart ); return 0; } } DYNARR_APPEND( path_t, path_da, 10 ); pp = &path(path_da.cnt-1); memset( pp, 0, sizeof *pp ); pp->pathElemStart = pathElemStart; pp->pathElemEnd = pathElem_da.cnt-1; pp->ep1 = ep1; pp->ep2 = ep2; pathElemStart = pathElem_da.cnt; LOG( log_group, 4, ( " Keep\n" ) ) return 0; case SPTC_IGNNXTTRK: if ( !GetTrkSelected(trk) ) { return 1; } if ( ep1 == ep2 ) { return 1; } if ( GetTrkEndPtCnt(trk) == 2 ) { return 0; } CHECKMSG( GetTrkType(trk) == T_TURNOUT, ( "GroupShortestPathFunc(IGNNXTTRK,T%d:%d,%d)", GetTrkIndex(trk), ep1, ep2 ) ); return FindPathBtwEP( trk, ep2, ep1, &flip ) == NULL; case SPTC_VALID: return 1; } return 0; } static int CmpGroupOrder( const void * ptr1, const void * ptr2 ) { int inx1 = *(int*)ptr1; int inx2 = *(int*)ptr2; return path(inx1).conflicts-path(inx2).conflicts; } static coOrd endPtOrig; static ANGLE_T endPtAngle; static int CmpEndPtAngle( const void * ptr1, const void * ptr2 ) { ANGLE_T angle; trkEndPt_p epp1 = (trkEndPt_p)ptr1; trkEndPt_p epp2 = (trkEndPt_p)ptr2; angle = NormalizeAngle(FindAngle(endPtOrig, GetEndPtPos(epp1))-endPtAngle) - NormalizeAngle(FindAngle(endPtOrig, GetEndPtPos(epp2))-endPtAngle); return (int)angle; } static int ConflictPaths( path_p path0, path_p path1 ) { if ( groupNoCombine != 0 ) { // No grouping return TRUE; } /* do these paths share an EP? */ if ( path0->ep1 == path1->ep1 ) { return TRUE; } if ( path0->ep1 == path1->ep2 ) { return TRUE; } if ( path0->ep2 == path1->ep1 ) { return TRUE; } if ( path0->ep2 == path1->ep2 ) { return TRUE; } return FALSE; } static BOOL_T CheckPathEndPt( track_p trk, char cc, EPINX_T ep ) { struct extraDataCompound_t *xx = GET_EXTRA_DATA(trk, T_TURNOUT, extraDataCompound_t); wIndex_t segInx; EPINX_T segEP, epCnt; DIST_T d; coOrd pos; GetSegInxEP( cc, &segInx, &segEP ); if ( ep ) { segEP = 1-segEP; } pos = GetSegEndPt( &xx->segs[segInx], segEP, FALSE, NULL ); REORIGIN1( pos, xx->angle, xx->orig ); epCnt = GetTrkEndPtCnt(trk); for ( ep=0; ep\n" ); return; } LogPrintf( "%c: ", segP->type ); switch ( segP->type ) { case SEG_STRTRK: case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: LogPrintf( "[ %0.3f %0.3f ] [ %0.3f %0.3f ]\n", segP->u.l.pos[0].x, segP->u.l.pos[0].y, segP->u.l.pos[1].x, segP->u.l.pos[1].y ); break; case SEG_CRVLIN: case SEG_CRVTRK: LogPrintf( "R:%0.3f [ %0.3f %0.3f } A0:%0.3f A1:%0.3f\n", segP->u.c.radius, segP->u.c.center.x, segP->u.c.center.y, segP->u.c.a0, segP->u.c.a1 ); break; default: LogPrintf( "%c:\n", segP->type ); } } /* * GroupOk: create a TURNOUT or STRUCTURE from the selected objects * 1 - Add selected tracks to groupTrk[] * - Add each group trk's segments to trackSeg[] * - Add all segs to segInMap[] * - if no track segments goto step 9 * 2 - Collect boundary endPts and sort them in tempEndPts[] * 3 - Find shortest path between all endPts (if it exists) * - For each track we add to the shortest path tree * capture the sub-path elements (FindPathBtwEP) in pathElem[] * 4 - Flip tracks so sub-path elements match up * 5 - Create conflict map * 6 - Flip paths to minimize the number of flipped segments * 7 - Build the path ('P') string (new-P) * 8 - Build segment list, adjust endPts in tempEndPts[] * 9 - create new TURNOUT/STRUCTURE definition * 10 - write defn to xtrkcad.cus * 11 - optionally replace grouped tracks with new defn */ static void GroupOk( void * unused ) { struct extraDataCompound_t *xx = NULL; turnoutInfo_t * to; int inx; EPINX_T ep, epCnt, epN; coOrd orig, size; FILE * f = NULL; BOOL_T rc = TRUE; track_p trk, trk1; path_t * pp, *ppN; pathElem_p ppp; groupTrk_p groupP; BOOL_T flip, flip1, allDone; DIST_T dist; ANGLE_T angle, angleN; pathElem_t pathElemTemp; char * cp=NULL; trkSeg_p segPtr; int segCnt; static dynArr_t conflictMap_da; #define conflictMap( I, J ) DYNARR_N( int, conflictMap_da, (I)*(path_da.cnt)+(J) ) #define segFlip( N ) DYNARR_N( int, conflictMap_da, (N) ) static dynArr_t groupOrder_da; #define groupOrder( N ) DYNARR_N( int, groupOrder_da, N ) static dynArr_t groupMap_da; #define groupMap( I, J ) DYNARR_N( int, groupMap_da, (I)*(path_da.cnt+1)+(J) ) int groupCnt; int pinx, pinx2, ginx, ginx2, gpinx2; trkEndPt_p endPtP; signed char pathChar; DYNARR_RESET( trkSeg_t, trackSegs_da ); DYNARR_RESET( trkSeg_t, tempSegs_da ); DYNARR_RESET( groupTrk_t, groupTrk_da ); DYNARR_RESET( path_t, path_da ); DYNARR_RESET( pathElem_t, pathElem_da ); TempEndPtsReset(); DYNARR_RESET( char, pathPtr_da ); ParamUpdate( &groupPG ); if ( groupManuf[0]==0 || groupDesc[0]==0 || groupPartno[0]==0 ) { NoticeMessage2( 0, MSG_GROUP_NONBLANK, _("Ok"), NULL ); return; } sprintf( message, "%s\t%s\t%s", groupManuf, groupDesc, groupPartno ); if ( strcmp( message, groupTitle ) != 0 ) { if ( FindCompound( FIND_TURNOUT|FIND_STRUCT, curScaleName, message ) ) if ( !NoticeMessage2( 1, MSG_TODSGN_REPLACE, _("Yes"), _("No") ) ) { return; } strcpy( groupTitle, message ); } wDrawDelayUpdate( mainD.d, TRUE ); /* * 1: Collect tracks */ trk = NULL; // int InInx = -1; BOOL_T hasTracks = FALSE; wIndex_t nTrkSeg = 0; wIndex_t nSeg = 0; wIndex_t iLastTrkSeg = 0; while ( TrackIterate( &trk ) ) { if ( GetTrkSelected( trk ) ) { DYNARR_APPEND( groupTrk_t, groupTrk_da, 10 ); groupP = &groupTrk(groupTrk_da.cnt-1); groupP->trk = trk; groupP->segStart = trackSegs_da.cnt; groupP->totalSegStart = tempSegs_da.cnt+trackSegs_da.cnt; if (IsTrack(trk)) { hasTracks = TRUE; } if ( GetTrkType(trk) == T_TURNOUT || GetTrkType(trk) == T_STRUCTURE) { xx = GET_EXTRA_DATA(trk, T_NOTRACK, extraDataCompound_t); for ( pinx=0; pinxsegCnt; pinx++ ) { segPtr = &xx->segs[pinx]; if ( IsSegTrack(segPtr) ) { DYNARR_APPEND( trkSeg_t, trackSegs_da, 10 ); trackSegs(trackSegs_da.cnt-1) = *segPtr; hasTracks = TRUE; RotateSegs( 1, &trackSegs(trackSegs_da.cnt-1), zero, xx->angle ); MoveSegs( 1, &trackSegs(trackSegs_da.cnt-1), xx->orig ); } else { DYNARR_APPEND( trkSeg_t, trackSegs_da, 10 ); trackSegs(trackSegs_da.cnt-1) = *segPtr; RotateSegs( 1, &trackSegs(trackSegs_da.cnt-1), zero, xx->angle ); MoveSegs( 1, &trackSegs(trackSegs_da.cnt-1), xx->orig ); } } } else if (GetTrkType(trk) == T_BEZIER || GetTrkType(trk) == T_BZRLIN ) { DYNARR_APPEND(trkSeg_t, trackSegs_da, 10); segPtr = &trackSegs(trackSegs_da.cnt-1); GetBezierSegmentFromTrack(trk,segPtr); } else if (GetTrkType(trk) == T_CORNU) { // int start = trackSegs_da.cnt; GetBezierSegmentsFromCornu(trk,&trackSegs_da, TRUE); //Only give back Bezier - cant be undone } else { if (IsTrack(trk)) { hasTracks=TRUE; } segCnt = tempSegs_da.cnt; DrawTrack( trk, &groupD, wDrawColorBlack ); for ( ; segCnt < tempSegs_da.cnt; segCnt++ ) { // Copy drawn segments DYNARR_APPEND( trkSeg_t, trackSegs_da, 10 ); segPtr = &trackSegs(trackSegs_da.cnt-1); *segPtr = tempSegs( segCnt ); } } // Count number of track segs and if any appear after seg 127 for ( ; nSeg < trackSegs_da.cnt; nSeg++ ) { if ( IsSegTrack( &trackSegs( nSeg ) ) ) { nTrkSeg++; iLastTrkSeg = nSeg; } } groupP->segEnd = trackSegs_da.cnt-1; } } if ( log_group >= 1 && logTable(log_group).level >= 4 ) { LogPrintf( "Track Segs:\n"); for ( int inx = 0; inx < trackSegs_da.cnt; inx++ ) { if (IsSegTrack(&trackSegs(inx))) { LogPrintf( " %d: ", inx+1 ); LogSeg( &trackSegs(inx) ); } } LogPrintf( "Other Segs:\n"); for ( int inx = 0; inx < trackSegs_da.cnt; inx++ ) { if (!IsSegTrack(&trackSegs(inx))) { LogPrintf( " %d: ", inx+1 ); LogSeg( &tempSegs(inx) ); } } } if ( nTrkSeg > MAX_PATH_SEGS ) { // Too many track segs NoticeMessage( MSG_TOOMANYSEGSINGROUP, _("Ok"), NULL ); wDrawDelayUpdate( mainD.d, FALSE ); wHide( groupW ); return; } if ( iLastTrkSeg > MAX_PATH_SEGS ) { // track segs beyond threshold NoticeMessage( MSG_TOOMANYSEGSINGROUP2, _("Ok"), NULL ); wDrawDelayUpdate( mainD.d, FALSE ); wHide( groupW ); return; } if ( groupTrk_da.cnt>0 && hasTracks) { /* * Collect EndPts and find paths */ pathElemStart = 0; endPtOrig = zero; for ( inx=0; inx=TempEndPtsCount() ) { endPtP = TempEndPtsAppend(); SetEndPt( endPtP, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep)); SetEndPtTrack( endPtP, trk1 ); // Remember what EP on trk1 was connecting to me SetEndPtEndPt( endPtP, trk1?GetEndPtConnectedToMe(trk1,trk):-1 ); endPtOrig.x += GetEndPtPos(endPtP).x; endPtOrig.y += GetEndPtPos(endPtP).y; } } } } if ( log_group >= 1 && logTable(log_group).level >= 4 ) { LogPrintf( "EndPts:\n" ); for ( int inx=0; inx NormalizeAngle( GetEndPtAngle(TempEndPt(1)) - GetEndPtAngle(TempEndPt(0)) ) ) { for ( ep=1; ep<(TempEndPtsCount()+1)/2; ep++ ) { SwapEndPts( TempEndPt(0), ep, TempEndPtsCount()-ep ); } } if ( log_group >= 1 && logTable(log_group).level >= 3 ) { LogPrintf( "Sorted EndPts:\n" ); for ( int inx=0; inx= 1 && logTable(log_group).level >= 3 ) { LogPrintf( "Shortest path:\n Group Tracks\n" ); for ( int inx=0; inxtrk ), gtp->segStart+1, gtp->segEnd+1 ); } LogPrintf( " Path Elem\n" ); for ( int inx=0; inxgroupInx, ppp->ep1, ppp->ep2, ppp->flip?"T":"F" ); if ( ppp->path == NULL ) { LogPrintf( "No Paths!\n" ); } else { for ( PATHPTR_T cp = ppp->path; cp[0] || cp[1]; cp++ ) { LogPrintf( " %d", *cp ); } } LogPrintf( " 0\n" ); } LogPrintf( " Path\n" ); for ( int inx=0; inxpathElemStart, pp->pathElemEnd, pp->ep1, pp->ep2, pp->conflicts, pp->inGroup?"T":"F", pp->done?"T":"F" ); } } /* * 4: Flip paths so they align */ if ( path_da.cnt == 0 ) { NoticeMessage( MSG_GROUP_NO_PATHS, _("Ok"), NULL ); wDrawDelayUpdate( mainD.d, FALSE ); wHide( groupW ); return; } allDone = FALSE; path(0).done = TRUE; while ( !allDone ) { allDone = TRUE; inx = -1; for ( pinx=0; pinxdone ) { continue; } for ( pinx2=0; pinx2ep1 == ppN->ep1 || pp->ep2 == ppN->ep2 ) { pp->done = TRUE; allDone = FALSE; LOG( log_group, 1, ( "P%d aligns with P%d\n", pinx, pinx2 ) ); break; } if ( pp->ep1 == ppN->ep2 || pp->ep2 == ppN->ep1 ) { pp->done = TRUE; allDone = FALSE; LOG( log_group, 1, ( "P%d aligns flipped with P%d\n", pinx, pinx2 ) ); inx = (pp->pathElemStart+pp->pathElemEnd-1)/2; for ( ginx=pp->pathElemStart,ginx2=pp->pathElemEnd; ginx<=inx; ginx++,ginx2-- ) { pathElemTemp = pathElem(ginx); pathElem(ginx) = pathElem(ginx2); pathElem(ginx2) = pathElemTemp; } for ( ginx=pp->pathElemStart; ginx<=pp->pathElemEnd; ginx++ ) { ppp = &pathElem(ginx); ep = ppp->ep1; ppp->ep1 = ppp->ep2; ppp->ep2 = ep; ppp->flip = !ppp->flip; } ep = pp->ep1; pp->ep1 = pp->ep2; pp->ep2 = ep; break; } } if ( inx<0 && !pp->done ) { inx = pinx; } } if ( allDone && inx>=0 ) { allDone = FALSE; path(inx).done = TRUE; } } if ( log_group >= 1 && logTable(log_group).level >= 1 ) { LogPrintf( "Group Paths\n" ); for ( pinx=0; pinxep1, pp->ep2 ); for ( pinx2=pp->pathElemEnd; pinx2>=pp->pathElemStart; pinx2-- ) { ppp = &pathElem(pinx2); LogPrintf( " %sT%d:%d.%d", ppp->flip?"-":"", GetTrkIndex(groupTrk(ppp->groupInx).trk), ppp->ep1, ppp->ep2 ); } LogPrintf( "\n" ); } } /* * 5: Create Conflict Map */ DYNARR_SET( int, conflictMap_da, path_da.cnt*path_da.cnt ); memset( &conflictMap(0,0), 0, conflictMap_da.cnt * sizeof conflictMap(0,0) ); for ( pinx=0; pinxinGroup ) { continue; } pp->inGroup = TRUE; groupCnt++; groupMap( groupCnt-1, 0 ) = groupOrder(pinx); ginx = 1; for ( pinx2=pinx+1; pinx2=0; ginx++ ); for ( pinx2=0; pinx2= 1 && logTable(log_group).level >= 3 ) { LogPrintf( "Group Map\n"); for ( pinx=0; pinx= 0; ginx++ ) { LogPrintf( " %d: %d", ginx, groupMap(pinx,ginx) ); } LogPrintf( "\n" ); } } /* * 6: Count number of times each segment is used as flipped */ DYNARR_SET( int, conflictMap_da, trackSegs_da.cnt ); memset( &segFlip(0), 0, trackSegs_da.cnt * sizeof segFlip(0) ); for ( pinx=0; pinxpath; pPaths && *pPaths; pPaths++ ) { inx = *pPaths; if ( inx<0 ) { inx = - inx; } CHECK( inx <= trackSegs_da.cnt ); flip = *pPaths<0; if ( ppp->flip ) { flip = !flip; } inx += groupTrk(ppp->groupInx).segStart - 1; if ( !flip ) { segFlip(inx)++; } else { segFlip(inx)--; } } } /* * Flip each segment that is used as flipped more than not */ LOG( log_group, 3, ( "Flipping Segments:" ) ); for ( pinx=0; pinx= 0; ginx++ ) { pp = &path(groupMap(pinx,ginx)); LOG( log_group, 3, (" Group Map(%d, %d): elem %d-%d, EP %d %d, Conflicts %d, inGrp %d, Done: %s\n", pinx, ginx, pp->pathElemStart, pp->pathElemEnd, pp->ep1, pp->ep2, pp->conflicts, pp->inGroup, pp->done?"T":"F" ) ); for ( pinx2=pp->pathElemEnd; pinx2>=pp->pathElemStart; pinx2-- ) { ppp = &pathElem( pinx2 ); LOG( log_group, 3, (" PE %d: GI %d, EP %d %d, Flip %d =", pinx2, ppp->groupInx, ppp->ep1, ppp->ep2, ppp->flip )); groupP = &groupTrk( ppp->groupInx ); PATHPTR_T pPaths = ppp->path; flip = ppp->flip; if ( pPaths == NULL ) { ErrorMessage( MSG_GROUP_NO_PATHS, _("Ok"), NULL ); wDrawDelayUpdate( mainD.d, FALSE ); wHide( groupW ); return; } if ( flip ) { pPaths += strlen((char *)pPaths)-1; } while ( *pPaths && (pPaths >= ppp->path) ) { //Add Guard for flip backwards DYNARR_APPEND( char, pathPtr_da, 10 ); pathChar = *pPaths; flip1 = flip; if ( pathChar < 0 ) { flip1 = !flip; pathChar = - pathChar; } pathChar = groupP->segStart+pathChar; if ( segFlip(pathChar-1)<0 ) { flip1 = ! flip1; } if ( flip1 ) { pathChar = - pathChar; } pathPtr(pathPtr_da.cnt-1) = pathChar; pPaths += (flip?-1:1); LOG( log_group, 3, (" %d", pathChar ) ); } LOG( log_group, 3, ("\n") ); } DYNARR_APPEND( char, pathPtr_da, 10 ); pathPtr(pathPtr_da.cnt-1) = 0; } DYNARR_APPEND( char, pathPtr_da, 10 ); pathPtr(pathPtr_da.cnt-1) = 0; } DYNARR_APPEND( char, pathPtr_da, 10 ); pathPtr(pathPtr_da.cnt-1) = 0; /* * 8: Copy and Reorigin Segments - Start by putting them out in the original order */ DYNARR_RESET(trkSeg_t, outputSegs_da); for (int i=0; ititle), options )>0; rc &= WriteCompoundPathsEndPtsSegs( f, pPaths, outputSegs_da.cnt, &outputSegs(0), TempEndPtsCount(), TempEndPt(0) ); SetUserLocale(); } if ( groupReplace ) { /* * 11: Replace defn */ UndoStart( _("Group Tracks"), "group" ); orig.x = - orig.x; orig.y = - orig.y; for ( ep=0; eptitle, TempEndPtsCount(), TempEndPt(0), pPaths, outputSegs_da.cnt, &outputSegs(0) ); struct extraDataCompound_t *xx = GET_EXTRA_DATA(trk, T_TURNOUT, extraDataCompound_t); xx->pathOverRide = FALSE; xx->pathNoCombine = groupNoCombine; SetTrkVisible( trk, TRUE ); for ( ep=0; ep0; rc &= WriteSegs( f, trackSegs_da.cnt, &trackSegs(0) ); SetUserLocale(); } if ( groupReplace ) { UndoStart( _("Group Tracks"), "group" ); trk = NULL; while ( TrackIterate( &trk ) ) { if ( GetTrkSelected( trk ) ) { DrawTrack( trk, &mainD, wDrawColorWhite ); UndoDelete( trk ); trackCount--; } } SelectRecount(); orig.x = - orig.x; orig.y = - orig.y; trk = NewCompound( T_STRUCTURE, 0, orig, 0.0, groupTitle, 0, NULL, NULL, trackSegs_da.cnt, &trackSegs(0) ); SetTrkVisible( trk, TRUE ); DrawNewTrack( trk ); EnableCommands(); } } if (f) { fclose(f); } DoChangeNotification( CHANGE_PARAMS ); wHide( groupW ); wDrawDelayUpdate( mainD.d, FALSE ); groupDesc[0] = '\0'; groupPartno[0] = '\0'; } EXPORT void DoGroup( void * unused ) { track_p trk = NULL; struct extraDataCompound_t *xx; TRKTYP_T trkType; xx = NULL; groupSegCnt = 0; groupCompoundCount = 0; groupOriginX = 0.0; groupOriginY = 0.0; BOOL_T isTurnout = FALSE; groupNoCombine = FALSE; while ( TrackIterate( &trk ) ) { if ( GetTrkSelected( trk ) ) { trkType = GetTrkType(trk); if ( IsTrack(trk) ) { isTurnout = TRUE; } if ( trkType == T_TURNOUT || trkType == T_STRUCTURE ) { xx = GET_EXTRA_DATA(trk, trkType, extraDataCompound_t); groupSegCnt += xx->segCnt; GroupCopyTitle( xtitle(xx) ); if ( trkType == T_TURNOUT && GetTrkEndPtCnt(trk) > 2 && xx->pathNoCombine != 0 ) { groupNoCombine = TRUE; } } else { groupSegCnt += 1; } } } if ( groupSegCnt <= 0 ) { ErrorMessage( MSG_NO_SELECTED_TRK ); return; } sprintf( groupTitle, "%s\t%s\t%s", groupManuf, groupDesc, groupPartno ); if ( log_group < 0 ) { log_group = LogFindIndex( "group" ); } if ( !groupW ) { ParamRegister( &groupPG ); groupW = ParamCreateDialog( &groupPG, MakeWindowTitle(_("Group Objects")), _("Ok"), GroupOk, wHide, TRUE, NULL, F_BLOCK, NULL ); groupD.dpi = mainD.dpi; } if (isTurnout) { groupPLs[4].option |= PDO_DLGIGNORE; wControlShow( groupPLs[4].control, FALSE ); groupPLs[5].option |= PDO_DLGIGNORE; wControlShow( groupPLs[5].control, FALSE ); } else { groupPLs[4].option &= ~PDO_DLGIGNORE; wControlShow( groupPLs[4].control, TRUE ); groupPLs[5].option &= ~PDO_DLGIGNORE; wControlShow( groupPLs[5].control, TRUE ); } ParamLoadControls( &groupPG ); wShow( groupW ); }