/** \file ccurve.c * CURVE */ /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include "ccurve.h" #include "cjoin.h" #include "cstraigh.h" #include "cundo.h" #include "custom.h" #include "fileio.h" #include "i18n.h" #include "messages.h" #include "param.h" #include "param.h" #include "track.h" #include "utility.h" #include "wlib.h" #include "cbezier.h" /* * STATE INFO */ static struct { STATE_T state; coOrd pos0; coOrd pos1; curveData_t curveData; track_p trk; EPINX_T ep; BOOL_T down; } Da; static long curveMode; EXPORT void DrawArrowHeads( trkSeg_p sp, coOrd pos, ANGLE_T angle, BOOL_T bidirectional, wDrawColor color ) { coOrd p0, p1; DIST_T d, w; int inx; d = mainD.scale*0.25; w = mainD.scale/mainD.dpi*2; for ( inx=0; inx<5; inx++ ) { sp[inx].type = SEG_STRLIN; sp[inx].width = w; sp[inx].color = color; } Translate( &p0, pos, angle, d ); Translate( &p1, pos, angle+180, bidirectional?d:(d/2.0) ); sp[0].u.l.pos[0] = p0; sp[0].u.l.pos[1] = p1; sp[1].u.l.pos[0] = p0; Translate( &sp[1].u.l.pos[1], p0, angle+135, d/2.0 ); sp[2].u.l.pos[0] = p0; Translate( &sp[2].u.l.pos[1], p0, angle-135, d/2.0 ); if (bidirectional) { sp[3].u.l.pos[0] = p1; Translate( &sp[3].u.l.pos[1], p1, angle-45, d/2.0 ); sp[4].u.l.pos[0] = p1; Translate( &sp[4].u.l.pos[1], p1, angle+45, d/2.0 ); } } EXPORT STATUS_T CreateCurve( wAction_t action, coOrd pos, BOOL_T track, wDrawColor color, DIST_T width, long mode, curveMessageProc message ) { track_p t; DIST_T d; ANGLE_T a, angle1, angle2; static coOrd pos0, p; int inx; switch ( action ) { case C_START: DYNARR_SET( trkSeg_t, tempSegs_da, 8 ); Da.down = FALSE; //Not got a valid start yet switch ( curveMode ) { case crvCmdFromEP1: if (track) message(_("Drag from End-Point in direction of curve - Shift locks to track open end-point") ); else message (_("Drag from End-Point in direction of curve") ); break; case crvCmdFromTangent: if (track) message(_("Drag from End-Point to Center - Shift locks to track open end-point") ); else message(_("Drag from End-Point to Center") ); break; case crvCmdFromCenter: message(_("Drag from Center to End-Point") ); break; case crvCmdFromChord: message(_("Drag from one to other end of chord") ); break; } return C_CONTINUE; case C_DOWN: for ( inx=0; inx<8; inx++ ) { tempSegs(inx).color = wDrawColorBlack; tempSegs(inx).width = 0; } tempSegs_da.cnt = 0; p = pos; BOOL_T found = FALSE; Da.trk = NULL; if ((mode == crvCmdFromEP1 || mode == crvCmdFromTangent) && track && (MyGetKeyState() & WKEY_SHIFT) != 0) { if ((t = OnTrack(&p, FALSE, TRUE)) != NULL) { EPINX_T ep = PickUnconnectedEndPointSilent(p, t); if (ep != -1) { Da.trk = t; Da.ep = ep; pos = GetTrkEndPos(t, ep); found = TRUE; } else { Da.pos0=pos; message(_("No unconnected end-point on track - Try again or release Shift and click")); return C_CONTINUE; } } else { Da.pos0=pos; message(_("Not on a track - Try again or release Shift and click")); return C_CONTINUE; } Da.down = TRUE; } Da.down = TRUE; if (!found) SnapPos( &pos ); pos0 = pos; Da.pos0 = pos; switch (mode) { case crvCmdFromEP1: tempSegs(0).type = (track?SEG_STRTRK:SEG_STRLIN); tempSegs(0).color = color; tempSegs(0).width = width; if (Da.trk) message(_("End Locked: Drag out curve start")); else message(_("Drag along curve start") ); break; case crvCmdFromTangent: case crvCmdFromCenter: tempSegs(0).type = SEG_STRLIN; tempSegs(1).type = SEG_CRVLIN; tempSegs(1).u.c.radius = mainD.scale*0.05; tempSegs(1).u.c.a0 = 0; tempSegs(1).u.c.a1 = 360; tempSegs(2).type = SEG_STRLIN; if (Da.trk && mode==crvCmdFromTangent) message(_("End Locked: Drag out to center")); else message( mode==crvCmdFromTangent?_("Drag from End-Point to Center"):_("Drag from Center to End-Point") ); break; case crvCmdFromChord: tempSegs(0).type = (track?SEG_STRTRK:SEG_STRLIN); tempSegs(0).color = color; tempSegs(0).width = width; message( _("Drag to other end of chord") ); break; } tempSegs(0).u.l.pos[0] = pos; return C_CONTINUE; case C_MOVE: if (!Da.down) return C_CONTINUE; if (Da.trk) { angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk, Da.ep)); angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1); if (mode ==crvCmdFromEP1) { if (angle2 > 90.0 && angle2 < 270.0) Translate( &pos, pos0, angle1, -FindDistance( pos0, pos )*cos(D2R(angle2)) ); else pos = pos0; } else { DIST_T dp = -FindDistance(pos0, pos)*sin(D2R(angle2)); if (angle2 > 180.0) Translate( &pos, pos0, angle1+90.0, dp ); else Translate( &pos, pos0, angle1-90.0, dp ); } } else SnapPos(&pos); tempSegs(0).u.l.pos[1] = pos; d = FindDistance( pos0, pos ); a = FindAngle( pos0, pos ); switch ( mode ) { case crvCmdFromEP1: if (Da.trk) message( _("Start Locked: Drag out curve start - Angle=%0.3f"), PutAngle(a)); else message( _("Drag out curve start - Angle=%0.3f"), PutAngle(a) ); tempSegs_da.cnt = 1; break; case crvCmdFromTangent: if (Da.trk) message( _("Tangent Locked: Drag out center - Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); else message( _("Drag out center - Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); tempSegs(1).u.c.center = pos; DrawArrowHeads( &tempSegs(2), pos0, FindAngle(pos0,pos)+90, TRUE, wDrawColorBlack ); tempSegs_da.cnt = 7; break; case crvCmdFromCenter: message( _("Radius=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); tempSegs(1).u.c.center = pos0; DrawArrowHeads( &tempSegs(2), pos, FindAngle(pos,pos0)+90, TRUE, wDrawColorBlack ); tempSegs_da.cnt = 7; break; case crvCmdFromChord: message( _("Length=%s Angle=%0.3f"), FormatDistance(d), PutAngle(a) ); if ( d > mainD.scale*0.25 ) { pos.x = (pos.x+pos0.x)/2.0; pos.y = (pos.y+pos0.y)/2.0; DrawArrowHeads( &tempSegs(1), pos, FindAngle(pos,pos0)+90, TRUE, wDrawColorBlack ); tempSegs_da.cnt = 6; } else { tempSegs_da.cnt = 1; } break; } return C_CONTINUE; case C_UP: if (!Da.down) return C_CONTINUE; if (Da.trk) { angle1 = NormalizeAngle(GetTrkEndAngle(Da.trk, Da.ep)); angle2 = NormalizeAngle(FindAngle(pos, pos0)-angle1); if (mode == crvCmdFromEP1) { if (angle2 > 90.0 && angle2 < 270.0) { Translate( &pos, pos0, angle1, -FindDistance( pos0, pos )*cos(D2R(angle2)) ); Da.pos1 = pos; } else { ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(0.0) ); return C_TERMINATE; } } else { DIST_T dp = -FindDistance(pos0, pos)*sin(D2R(angle2)); if (angle2 > 180.0) Translate( &pos, pos0, angle1+90.0, dp ); else Translate( &pos, pos0, angle1-90.0, dp ); Da.pos1 = pos; } } switch (mode) { case crvCmdFromEP1: DrawArrowHeads( &tempSegs(1), pos, FindAngle(pos,pos0)+90, TRUE, drawColorRed ); tempSegs_da.cnt = 6; break; case crvCmdFromChord: tempSegs(1).color = drawColorRed; case crvCmdFromTangent: case crvCmdFromCenter: tempSegs(2).color = drawColorRed; tempSegs(3).color = drawColorRed; tempSegs(4).color = drawColorRed; tempSegs(5).color = drawColorRed; tempSegs(6).color = drawColorRed; break; } message( _("Drag on Red arrows to adjust curve") ); return C_CONTINUE; default: return C_CONTINUE; } } static STATUS_T CmdCurve( wAction_t action, coOrd pos ) { track_p t; DIST_T d; static int segCnt; STATUS_T rc = C_CONTINUE; switch (action) { case C_START: curveMode = (long)commandContext; Da.state = -1; Da.pos0 = pos; tempSegs_da.cnt = 0; STATUS_T rcode; return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); case C_TEXT: if ( Da.state == 0 ) return CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); else return C_CONTINUE; case C_DOWN: if ( Da.state == -1 ) { //SnapPos( &pos ); Da.pos0 = pos; Da.state = 0; rcode = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); if (!Da.down) Da.state = -1; return rcode; //Da.pos0 = pos; } else { tempSegs_da.cnt = segCnt; return C_CONTINUE; } case C_MOVE: if (Da.state<0) return C_CONTINUE; mainD.funcs->options = wDrawOptTemp; DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); if ( Da.state == 0 ) { Da.pos1 = pos; rc = CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); } else { // SnapPos( &pos ); if (Da.trk) PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, FALSE ); else PlotCurve( curveMode, Da.pos0, Da.pos1, pos, &Da.curveData, TRUE ); if (Da.curveData.type == curveTypeStraight) { tempSegs(0).type = SEG_STRTRK; tempSegs(0).u.l.pos[0] = Da.pos0; tempSegs(0).u.l.pos[1] = Da.curveData.pos1; tempSegs_da.cnt = 1; InfoMessage( _("Straight Track: Length=%s Angle=%0.3f"), FormatDistance(FindDistance( Da.pos0, Da.curveData.pos1 )), PutAngle(FindAngle( Da.pos0, Da.curveData.pos1 )) ); } else if (Da.curveData.type == curveTypeNone) { tempSegs_da.cnt = 0; InfoMessage( _("Back") ); } else if (Da.curveData.type == curveTypeCurve) { tempSegs(0).type = SEG_CRVTRK; tempSegs(0).u.c.center = Da.curveData.curvePos; tempSegs(0).u.c.radius = Da.curveData.curveRadius; tempSegs(0).u.c.a0 = Da.curveData.a0; tempSegs(0).u.c.a1 = Da.curveData.a1; tempSegs_da.cnt = 1; d = D2R(Da.curveData.a1); if (d < 0.0) d = 2*M_PI+d; if ( d*Da.curveData.curveRadius > mapD.size.x+mapD.size.y ) { ErrorMessage( MSG_CURVE_TOO_LARGE ); tempSegs_da.cnt = 0; Da.curveData.type = curveTypeNone; mainD.funcs->options = 0; return C_CONTINUE; } InfoMessage( _("Curved Track: Radius=%s Angle=%0.3f Length=%s"), FormatDistance(Da.curveData.curveRadius), Da.curveData.a1, FormatDistance(Da.curveData.curveRadius*d) ); } } DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); mainD.funcs->options = 0; return rc; case C_UP: if (Da.state<0) return C_CONTINUE; mainD.funcs->options = wDrawOptTemp; DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); if (Da.state == 0) { SnapPos( &pos ); Da.pos1 = pos; Da.state = 1; CreateCurve( action, pos, TRUE, wDrawColorBlack, 0, curveMode, InfoMessage ); DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); mainD.funcs->options = 0; segCnt = tempSegs_da.cnt; InfoMessage( _("Drag on Red arrows to adjust curve") ); return C_CONTINUE; } else { mainD.funcs->options = 0; tempSegs_da.cnt = 0; Da.state = -1; if (Da.curveData.type == curveTypeStraight) { if ((d=FindDistance( Da.pos0, Da.curveData.pos1 )) <= minLength) { ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(fabs(minLength-d)) ); return C_TERMINATE; } UndoStart( _("Create Straight Track"), "newCurve - straight" ); t = NewStraightTrack( Da.pos0, Da.curveData.pos1 ); if (Da.trk) { EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t); if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep); } UndoEnd(); } else if (Da.curveData.type == curveTypeCurve) { if ((d= Da.curveData.curveRadius * Da.curveData.a1 *2.0*M_PI/360.0) <= minLength) { ErrorMessage( MSG_TRK_TOO_SHORT, "Curved ", PutDim(fabs(minLength-d)) ); return C_TERMINATE; } UndoStart( _("Create Curved Track"), "newCurve - curve" ); t = NewCurvedTrack( Da.curveData.curvePos, Da.curveData.curveRadius, Da.curveData.a0, Da.curveData.a1, 0 ); if (Da.trk) { EPINX_T ep = PickUnconnectedEndPoint(Da.pos0, t); if (ep != -1) ConnectTracks(Da.trk, Da.ep, t, ep); } UndoEnd(); } else { return C_ERROR; } DrawNewTrack( t ); return C_TERMINATE; } case C_REDRAW: if ( Da.state >= 0 ) { mainD.funcs->options = wDrawOptTemp; DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); mainD.funcs->options = 0; } return C_CONTINUE; case C_CANCEL: if (Da.state == 1) { mainD.funcs->options = wDrawOptTemp; DrawSegs( &mainD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); mainD.funcs->options = 0; tempSegs_da.cnt = 0; Da.trk = NULL; } Da.state = -1; return C_CONTINUE; } return C_CONTINUE; } static DIST_T circleRadius = 18.0; static long helixTurns = 5; static ANGLE_T helixAngSep = 0.0; static DIST_T helixElev = 0.0; static DIST_T helixRadius = 18.0; static DIST_T helixGrade = 0.0; static DIST_T helixVertSep = 0.0; static DIST_T origVertSep = 0.0; static wWin_p helixW; #define H_ELEV (0) #define H_RADIUS (1) #define H_TURNS (2) #define H_ANGSEP (3) #define H_GRADE (4) #define H_VERTSEP (5) static int h_orders[7]; static int h_clock; EXPORT long circleMode; static void ComputeHelix( paramGroup_p, int, void * ); static paramFloatRange_t r0_360 = { 0, 360 }; static paramFloatRange_t r0_1000000 = { 0, 1000000 }; static paramIntegerRange_t i1_1000000 = { 1, 1000000 }; static paramFloatRange_t r1_10000 = { 1, 10000 }; static paramFloatRange_t r0_100= { 0, 100 }; static paramData_t helixPLs[] = { { PD_FLOAT, &helixElev, "elev", PDO_DIM, &r0_1000000, N_("Elevation Difference") }, { PD_FLOAT, &helixRadius, "radius", PDO_DIM, &r1_10000, N_("Radius") }, { PD_LONG, &helixTurns, "turns", 0, &i1_1000000, N_("Turns") }, { PD_FLOAT, &helixAngSep, "angSep", 0, &r0_360, N_("Angular Separation") }, { PD_FLOAT, &helixGrade, "grade", 0, &r0_100, N_("Grade") }, { PD_FLOAT, &helixVertSep, "vertSep", PDO_DIM, &r0_1000000, N_("Vertical Separation") }, #define I_HELIXMSG (6) { PD_MESSAGE, N_("Total Length"), NULL, PDO_DLGRESETMARGIN, (void*)200 } }; static paramGroup_t helixPG = { "helix", PGO_PREFMISCGROUP, helixPLs, sizeof helixPLs/sizeof helixPLs[0] }; static paramData_t circleRadiusPLs[] = { { PD_FLOAT, &circleRadius, "radius", PDO_DIM, &r1_10000 } }; static paramGroup_t circleRadiusPG = { "circle", 0, circleRadiusPLs, sizeof circleRadiusPLs/sizeof circleRadiusPLs[0] }; static void ComputeHelix( paramGroup_p pg, int h_inx, void * data ) { DIST_T totTurns; DIST_T length; long updates = 0; if ( h_inx < 0 || h_inx >= sizeof h_orders/sizeof h_orders[0] ) return; ParamLoadData( &helixPG ); totTurns = helixTurns + helixAngSep/360.0; length = totTurns * helixRadius * (2 * M_PI); h_orders[h_inx] = ++h_clock; switch ( h_inx ) { case H_ELEV: if (h_orders[H_TURNS] 0.0) { helixTurns = (int)floor(helixElev/origVertSep - helixAngSep/360.0); totTurns = helixTurns + helixAngSep/360.0; updates |= (1< 0) { helixVertSep = helixElev/totTurns; updates |= (1< 0.0) { origVertSep = helixVertSep; helixTurns = (int)floor(helixElev/origVertSep - helixAngSep/360.0); updates |= (1< 0) { helixVertSep = helixElev/totTurns; updates |= (1< 0.0 ) { if ( h_orders[H_RADIUS]>=h_orders[H_GRADE] || (helixGrade==0.0 && totTurns>0 && helixRadius>0) ) { if ( helixRadius > 0.0 ) { helixGrade = helixElev/(totTurns*helixRadius*(2*M_PI))*100.0; updates |= (1< 0.0 ) { helixRadius = helixElev/(totTurns*(helixGrade/100.0)*2.0*M_PI); updates |= (1<>=1 ) { if ( (updates&1) ) ParamLoadControl( &helixPG, h_inx ); } if (length > 0.0) sprintf( message, _("Total Length %s"), FormatDistance(length) ); else strcpy( message, " " ); ParamLoadMessage( &helixPG, I_HELIXMSG, message ); } static void HelixCancel( wWin_p win ) { wHide( helixW ); Reset(); } static void ChangeHelixW( long changes ) { if ( (changes & CHANGE_UNITS) && helixW != NULL && wWinIsVisible(helixW) ) { ParamLoadControls( &helixPG ); ComputeHelix( NULL, 6, NULL ); } } static STATUS_T CmdCircleCommon( wAction_t action, coOrd pos, BOOL_T helix ) { track_p t; static coOrd pos0; wControl_p controls[2]; char * labels[1]; switch (action) { case C_START: if (helix) { if (helixW == NULL) helixW = ParamCreateDialog( &helixPG, MakeWindowTitle(_("Helix")), NULL, NULL, HelixCancel, TRUE, NULL, 0, ComputeHelix ); ParamLoadControls( &helixPG ); ParamGroupRecord( &helixPG ); ComputeHelix( NULL, 6, NULL ); wShow( helixW ); memset( h_orders, 0, sizeof h_orders ); h_clock = 0; } else { ParamLoadControls( &circleRadiusPG ); ParamGroupRecord( &circleRadiusPG ); switch ( circleMode ) { case circleCmdFixedRadius: controls[0] = circleRadiusPLs[0].control; controls[1] = NULL; labels[0] = N_("Circle Radius"); InfoSubstituteControls( controls, labels ); break; case circleCmdFromTangent: InfoSubstituteControls( NULL, NULL ); InfoMessage( _("Click on Circle Edge") ); break; case circleCmdFromCenter: InfoSubstituteControls( NULL, NULL ); InfoMessage( _("Click on Circle Center") ); break; } } tempSegs_da.cnt = 0; return C_CONTINUE; case C_DOWN: DYNARR_SET( trkSeg_t, tempSegs_da, 1 ); tempSegs_da.cnt = 0; if (helix) { if (helixRadius <= 0.0) { ErrorMessage( MSG_RADIUS_GTR_0 ); return C_ERROR; } if (helixTurns <= 0) { ErrorMessage( MSG_HELIX_TURNS_GTR_0 ); return C_ERROR; } ParamLoadData( &helixPG ); } else { ParamLoadData( &circleRadiusPG ); switch( circleMode ) { case circleCmdFixedRadius: if (circleRadius <= 0.0) { ErrorMessage( MSG_RADIUS_GTR_0 ); return C_ERROR; } break; case circleCmdFromTangent: InfoSubstituteControls( NULL, NULL ); InfoMessage( _("Drag to Center") ); break; case circleCmdFromCenter: InfoSubstituteControls( NULL, NULL ); InfoMessage( _("Drag to Edge") ); break; } } SnapPos( &pos ); tempSegs(0).u.c.center = pos0 = pos; tempSegs(0).color = wDrawColorBlack; tempSegs(0).width = 0; return C_CONTINUE; case C_MOVE: DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); SnapPos( &pos ); tempSegs(0).u.c.center = pos; if ( !helix ) { switch ( circleMode ) { case circleCmdFixedRadius: break; case circleCmdFromCenter: tempSegs(0).u.c.center = pos0; circleRadius = FindDistance( tempSegs(0).u.c.center, pos ); InfoMessage( _("Radius=%s"), FormatDistance(circleRadius) ); break; case circleCmdFromTangent: circleRadius = FindDistance( tempSegs(0).u.c.center, pos0 ); InfoMessage( _("Radius=%s"), FormatDistance(circleRadius) ); break; } } tempSegs(0).type = SEG_CRVTRK; tempSegs(0).u.c.radius = helix?helixRadius:circleRadius; tempSegs(0).u.c.a0 = 0.0; tempSegs(0).u.c.a1 = 360.0; tempSegs_da.cnt = 1; DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); return C_CONTINUE; case C_UP: DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); if (helixRadius > mapD.size.x && helixRadius > mapD.size.y) { ErrorMessage( MSG_RADIUS_TOO_BIG ); return C_ERROR; } if (circleRadius > mapD.size.x && circleRadius > mapD.size.y) { ErrorMessage( MSG_RADIUS_TOO_BIG ); return C_ERROR; } if ( helix ) { if (helixRadius > 10000) { ErrorMessage( MSG_RADIUS_GTR_10000 ); return C_ERROR; } UndoStart( _("Create Helix Track"), "newHelix" ); t = NewCurvedTrack( tempSegs(0).u.c.center, helixRadius, 0.0, 0.0, helixTurns ); } else { if ( circleRadius <= 0 ) { ErrorMessage( MSG_RADIUS_GTR_0 ); return C_ERROR; } if ((circleRadius > 100000) || (helixRadius > 10000)) { ErrorMessage( MSG_RADIUS_GTR_10000 ); return C_ERROR; } UndoStart( _("Create Circle Track"), "newCircle" ); t = NewCurvedTrack( tempSegs(0).u.c.center, circleRadius, 0.0, 0.0, 0 ); } UndoEnd(); DrawNewTrack(t); if (helix) wHide( helixW ); else InfoSubstituteControls( NULL, NULL ); tempSegs_da.cnt = 0; return C_TERMINATE; case C_REDRAW: DrawSegs( &tempD, zero, 0.0, &tempSegs(0), tempSegs_da.cnt, trackGauge, wDrawColorBlack ); return C_CONTINUE; case C_CANCEL: if (helix) wHide( helixW ); else InfoSubstituteControls( NULL, NULL ); return C_CONTINUE; default: return C_CONTINUE; } } static STATUS_T CmdCircle( wAction_t action, coOrd pos ) { if ( action == C_START ) { circleMode = (long)commandContext; } return CmdCircleCommon( action, pos, FALSE ); } static STATUS_T CmdHelix( wAction_t action, coOrd pos ) { return CmdCircleCommon( action, pos, TRUE ); } #include "bitmaps/curve1.xpm" #include "bitmaps/curve2.xpm" #include "bitmaps/curve3.xpm" #include "bitmaps/curve4.xpm" #include "bitmaps/bezier.xpm" #include "bitmaps/circle1.xpm" #include "bitmaps/circle2.xpm" #include "bitmaps/circle3.xpm" EXPORT void InitCmdCurve( wMenu_p menu ) { ButtonGroupBegin( _("Curve Track"), "cmdCircleSetCmd", _("Curve Tracks") ); AddMenuButton( menu, CmdCurve, "cmdCurveEndPt", _("Curve from End-Pt"), wIconCreatePixMap( curve1_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE1, (void*)0 ); AddMenuButton( menu, CmdCurve, "cmdCurveTangent", _("Curve from Tangent"), wIconCreatePixMap( curve2_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE2, (void*)1 ); AddMenuButton( menu, CmdCurve, "cmdCurveCenter", _("Curve from Center"), wIconCreatePixMap( curve3_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE3, (void*)2 ); AddMenuButton( menu, CmdCurve, "cmdCurveChord", _("Curve from Chord"), wIconCreatePixMap( curve4_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CURVE4, (void*)3 ); AddMenuButton( menu, CmdBezCurve, "cmdBezier", _("Bezier Curve"), wIconCreatePixMap(bezier_xpm), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_BEZIER, (void*)bezCmdCreateTrack ); ButtonGroupEnd(); ButtonGroupBegin( _("Circle Track"), "cmdCurveSetCmd", _("Circle Tracks") ); AddMenuButton( menu, CmdCircle, "cmdCircleFixedRadius", _("Fixed Radius Circle"), wIconCreatePixMap( circle1_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CIRCLE1, (void*)0 ); AddMenuButton( menu, CmdCircle, "cmdCircleTangent", _("Circle from Tangent"), wIconCreatePixMap( circle2_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CIRCLE2, (void*)1 ); AddMenuButton( menu, CmdCircle, "cmdCircleCenter", _("Circle from Center"), wIconCreatePixMap( circle3_xpm ), LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_CIRCLE3, (void*)2 ); ButtonGroupEnd(); ParamRegister( &circleRadiusPG ); ParamCreateControls( &circleRadiusPG, NULL ); } /** * Append the helix command to the pulldown menu. The helix doesn't use an icon, so it is only * available through the pulldown * * \param varname1 IN pulldown menu * \return */ void InitCmdHelix(wMenu_p menu) { AddMenuButton(menu, CmdHelix, "cmdHelix", _("Helix"), NULL, LEVEL0_50, IC_STICKY|IC_POPUP2, ACCL_HELIX, NULL); ParamRegister(&helixPG); RegisterChangeNotification(ChangeHelixW); }