diff options
Diffstat (limited to 'app/bin/tease.c')
-rw-r--r-- | app/bin/tease.c | 1950 |
1 files changed, 1950 insertions, 0 deletions
diff --git a/app/bin/tease.c b/app/bin/tease.c new file mode 100644 index 0000000..3667fe1 --- /dev/null +++ b/app/bin/tease.c @@ -0,0 +1,1950 @@ +/* + * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/tease.c,v 1.2 2008-01-20 23:29:15 mni77 Exp $ + * + * TRANSISTION-CURVES (JOINTS) + * + */ + +/* 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. + */ + +/* + +Transistion-curves (aka joints or spirals) connect curves with different +radius (including straights which have infinite radius, indicated by radius=0). +The curve is described by 2 control parameters: R and L. +L is the length along the tangent of the curve and +R is the radius of an arc at the end of the curve. +At any point (l) along the tangent the arc at that point has radius +r=(R*L)/l. +The transition curve offset (x) is the closest distance between the arc +and the tangent. +The center of any arc is at (l/2, r+x). +See 'ComputeJointPos()' for details on this. + +Warning crude ascii graphics! + +a aa + aaa aaa * + aaaa aaaa * + aaaaa aaaaa *** + aaaaaaa aaaaaa **** + aaaaaaa aaaaaaa ***** + aaaaaaaaaaaaaaaaaaaa ****** + ^ ******* + x ******** + *******v* +0*****************************TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT + L/2 L + +'R' and 'L' are curve control parameters. +'0' is curve origin. +'**..TT' is tangent line. +'a' is arc with radius 'R'. +'*' is the transisition curve. +'x' is transisition curve offset. + +For a better representation of this, build 'testjoin' and +do 'testjoin psplot 10 10 40 1 | lpr -Ppostscript' +*/ + + +#include "track.h" +#include "ccurve.h" +#include "cstraigh.h" +#include "cjoin.h" +#include "i18n.h" + +static TRKTYP_T T_EASEMENT = -1; + +static ANGLE_T JOINT_ANGLE_INCR = 2.0; + +struct extraData { + DIST_T l0, l1; /* curve start and end parameter */ + DIST_T R, L; /* curve control parameters */ + BOOL_T flip; /* T: endPt[1] - is l0 */ + BOOL_T negate; /* T: curves to the left */ + BOOL_T Scurve; /* T: is an S-curve */ + coOrd pos; /* Pos of origin */ + ANGLE_T angle; /* Angle of curve tangent */ + }; + +#define xl0 extraData->l0 +#define xl1 extraData->l1 +#define xR extraData->R +#define xL extraData->L +#define xflip extraData->flip +#define xnegate extraData->negate +#define xScurve extraData->Scurve +#define xpos extraData->pos +#define xangle extraData->angle + +#define EASE_MIN_X (0.01) + +static int log_ease; +static int log_traverseJoint; + + +static DIST_T FindL( + DIST_T r, + DIST_T R, + DIST_T L ) +/* + * Given a radius (r) return control value (l). + * This function is it's inverse! + */ +{ + return (r==0.0)?L:(R*L)/r; +} + + +static void GetLandD( + DIST_T *RL, + DIST_T *RD, + coOrd q, + coOrd p, + ANGLE_T a, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T Scurve ) +{ + DIST_T l, d, x; + + q.x -= p.x; + q.y -= p.y; + Rotate( &q, zero, -a ); + l = q.y; + x = (l*l*l)/(6*R*L); + if (!negate) { + d = q.x - x; + } else { + d = q.x + x; + } + if (RL) + *RL = l; + if (RD) + *RD = d; +} + + +int OLDEASE = 0; +static void ComputeJoinPos( + DIST_T l, + DIST_T R, + DIST_T L, + DIST_T *RR, + ANGLE_T *RA, + coOrd *P, + coOrd *PC ) +/* + * Compute position along transition-curve. + * Also compute angle and position of tangent circle's center. + */ +{ + ANGLE_T a; + DIST_T r; + coOrd pp, pc; + if (l==0.0) + r = 100000.0; + else + r = (R*L)/l; + pp.y = l; + pc.y = l/2.0; + a = asin( l/2.0 / r ); +if (OLDEASE){ + pc.x = l*l / (24*r) + r; + pp.x = pc.x - r*cos(a); +}else{ + pp.x = (l*l*l)/(6*R*L); + pc.x = pp.x + r*cos(a); +} +/*lprintf( "A %0.3f %0.3f %0.3f [%0.3f %0.3f]\n", a, aa, aaa, q.x, q.y );*/ + if (P) + *P = pp; + if (PC) + *PC = pc; + if (RR) + *RR = r; + if (RA) + *RA = R2D(a); +} + +static DIST_T JoinD( + DIST_T l, + DIST_T R, + DIST_T L ) +/* + * Compute distance from transition-curve origin to specified point. + * Distance is approximately equal to length of arc from origin + * to specified point with radius = 2.0 * radius at point. + * This is a very good approximation (< 0.1%). + */ +{ + DIST_T rr1, d1; + ANGLE_T a1; + coOrd p0; + DIST_T sign = 1; + if ( l < 0 ) { + sign = -1; + l = -l; + } + ComputeJoinPos( l, R, L, &rr1, NULL, &p0, NULL ); + rr1 *= 2.0; + a1=asin(sqrt(p0.x*p0.x + p0.y*p0.y)/2.0/rr1); + d1 = rr1 * a1 * 2.0; + return d1*sign; +} + + +static DIST_T GetLfromD( + DIST_T D, + DIST_T R, + DIST_T L ) +{ + DIST_T deltaD, d, l, deltaL; + l = L/2.0; + deltaL = L/4.0; + while ( deltaL>0.0001 ) { + d = JoinD(l,R,L); + if ( d < D ) { + deltaD = D-d; + } else { + deltaD = d-D; + } + if ( deltaD < 0.000001 ) + return l; + if ( d < D ) + l += deltaL; + else + l -= deltaL; + deltaL /= 2.0; + } +/*printf( "GetLfromD( %0.3f %0.3f %0.3f ) = %0.3f\n", D, R, L, l );*/ + return l; +} + + +#ifdef LATER +static void JoinDistance( + DIST_T r, + DIST_T R, + DIST_T X, + DIST_T L, + DIST_T *dr, + DIST_T *xr, + DIST_T *lr ) +{ + DIST_T l, d, rr; + coOrd p, pc; + if (r == 0.0) { + *dr = 0.0; + *lr = *xr = 0.0; + return; + } + l = FindL( r, R, L ); + d = JoinD( l, R, L ); + ComputeJoinPos( l, R, L, NULL, NULL, &p, NULL ); +LOG( log_ease, 2, ( "joinDistance r=%0.3f rr=%0.3f\n", r, rr ) ) + *xr = pc.x - rr; + *dr = d; + *lr = pc.y; +} +#endif + +EXPORT STATUS_T ComputeJoint( + DIST_T r0, + DIST_T r1, + easementData_t * e ) +/* + * Compute joint data given radius of the 2 curves being joined. + * Radius is =0 for straight tracks and <0 for left-handed curves. + * S-curves are handled by treating them as 2 transition-curves joined + * origin to origin. + */ +{ + DIST_T t, l0, l1, d0, d1, rr0, rr1, xx; + ANGLE_T a, a0, a1; + coOrd rp0, rpc0, rp1, rpc1; + +LOG( log_ease, 4, ( "ComputeJoint( %0.3f, %0.3f )\n", r0, r1 ) ) + + if (easementVal <= 0.1) { + e->d0 = e->d1 = e->x = 0.0; + return E_NOTREQ; + } + if (r0 != 0.0 && fabs(r0) < easeR) { + ErrorMessage( MSG_RADIUS_LSS_EASE_MIN, + FormatDistance(fabs(r0)), FormatDistance(easeR) ); + e->d0 = e->d1 = e->x = 0.0; + return E_ERROR; + } + if (r1 != 0.0 && fabs(r1) < easeR) { + ErrorMessage( MSG_RADIUS_LSS_EASE_MIN, FormatDistance(fabs(r1)), FormatDistance(easeR) ); + e->d0 = e->d1 = e->x = 0.0; + return E_ERROR; + } + if (r0 == 0.0 && r1 == 0.0) { + /* ASSERT( FALSE ); CHECKME */ + e->d0 = e->d1 = e->x = 0.0; + return E_NOTREQ; + } + e->r0 = r0; + e->r1 = r1; + e->Scurve = FALSE; + if ( ! ( (r0 >= 0 && r1 >= 0) || (r0 <= 0 && r1 <= 0) ) ) { + /* S-curve */ + e->Scurve = TRUE; + e->flip = FALSE; + e->negate = (r0 > 0.0); + l0 = FindL( fabs(r0), easeR, easeL ); + ComputeJoinPos( l0, easeR, easeL, &rr0, NULL, &rp0, &rpc0 ); + l1 = FindL( fabs(r1), easeR, easeL ); + ComputeJoinPos( l1, easeR, easeL, &rr1, NULL, &rp1, &rpc1 ); + rp1.x = - rp1.x; + rp1.y = - rp1.y; + rpc1.x = - rpc1.x; + rpc1.y = - rpc1.y; + xx = FindDistance(rpc0, rpc1) - rr0 - rr1; + a0 = NormalizeAngle( FindAngle(rpc0, rp0) - FindAngle(rpc0, rpc1) ); + a1 = NormalizeAngle( FindAngle(rpc1, rp1) - FindAngle(rpc1, rpc0) ); + d0 = fabs( rr0 * D2R(a0) ); + d1 = fabs( rr1 * D2R(a1) ); + } else { + /* ! S-curve */ + e->negate = ( (r0==0.0||r1==0.0)? r0>r1 : r0<r1 ); + r0 = fabs(r0); + r1 = fabs(r1); + e->flip = FALSE; + if ( r1 == 0 || (r0 != 0 && r1 > r0 ) ) { + e->flip = TRUE; + t=r0; r0=r1; r1=t; + } + if (r0 == 0) { + if (r1 == 0) { + xx = l0 = l1 = d0 = d1 = 0.0; + } else { + l0 = 0.0; + l1 = FindL( r1, easeR, easeL ); + ComputeJoinPos( l1, easeR, easeL, &rr1, NULL, &rp1, &rpc1 ); + d0 = rpc1.y; + a1 = FindAngle(rpc1, rp1) - 270.0; + d1 = rr1 * D2R(a1); + xx = rpc1.x - rr1; + } + } else { + l0 = FindL( r0, easeR, easeL ); + ComputeJoinPos( l0, easeR, easeL, &rr0, NULL, &rp0, &rpc0 ); + l1 = FindL( r1, easeR, easeL ); + ComputeJoinPos( l1, easeR, easeL, &rr1, NULL, &rp1, &rpc1 ); + a = FindAngle( rpc0, rpc1 ); + a0 = a - FindAngle(rpc0, rp0);/*???*/ + a1 = FindAngle(rpc1, rp1) - a; + xx = rr0 - ( rr1 + FindDistance(rpc0, rpc1) ); + d0 = rr0 * D2R(a0); + d1 = rr1 * D2R(a1); + } + } +LOG( log_ease, 2, ( "CJoint(%0.3f %0.3f) l0=%0.3f d0=%0.3f l1=%0.3f d1=%0.3f x=%0.3f S%d F%d N%d\n", + e->r0, e->r1, l0, d0, l1, d1, xx, e->Scurve, e->flip, e->negate ) ) + if (xx < EASE_MIN_X || d0+d1<=minLength) { + e->d0 = e->d1 = e->x = 0.0; + return E_NOTREQ; + } else { + if (!e->flip) { + e->d0 = d0; + e->d1 = d1; + e->l0 = l0; + e->l1 = l1; + } else { + e->d0 = d1; + e->d1 = d0; + e->l0 = l1; + e->l1 = l0; + } + e->x = xx; + return E_REQ; + } +} + +static track_p NewJoint( + coOrd pos0, + ANGLE_T angle0, + coOrd pos1, + ANGLE_T angle1, + DIST_T trackGauge, + DIST_T R, + DIST_T L, + easementData_t * e ) +/* + * Allocate a joint track segment. + * Allocate a track, save relevant data from (*e), + * and compute origin and angle of transition-curve. + * Position is determined relative to endPoints. + */ +{ + track_p trk; + struct extraData *xx; + coOrd p, p0, p1, q0, q1; + static coOrd qZero = { 0.0, 0.0 }; + ANGLE_T az0, a01, b, b01, b1, d, d1; + trk = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + SetTrkScale( trk, curScaleInx ); + xx = GetTrkExtraData( trk ); + SetTrkEndPoint( trk, 0, pos0, NormalizeAngle(angle0+180.0) ); + SetTrkEndPoint( trk, 1, pos1, NormalizeAngle(angle1+180.0) ); + xx->R = R; + xx->L = L; + xx->flip = e->flip; + xx->negate = e->negate; + xx->Scurve = e->Scurve; + if (!e->flip) { + xx->l0 = e->l0; + xx->l1 = e->l1; + p0 = pos0; + p1 = pos1; + } else { + xx->l0 = e->l1; + xx->l1 = e->l0; + p0 = pos1; + p1 = pos0; + } + ComputeJoinPos( xx->l0, R, L, NULL, NULL, &q0, NULL ); + ComputeJoinPos( xx->l1, R, L, NULL, NULL, &q1, NULL ); + if (e->negate) { + q0.x = -q0.x; + q1.x = -q1.x; + } + b01 = FindAngle( p0, p1 ); + if (!e->Scurve) { + az0 = FindAngle( qZero, q0 ); + a01 = FindAngle( q0, q1 ); + b1 = NormalizeAngle( b01 - (a01+az0) ); + b = NormalizeAngle( b01 - a01 ); + } else { + q1.x = -q1.x; + q1.y = -q1.y; + az0 = FindAngle( qZero, q0 ); + a01 = FindAngle( q0, q1 ); + b = NormalizeAngle( b01 - a01 ); + } + /*a = NormalizeAngle(a0+a1-90.0);*/ + p = q0; + Rotate( &p, qZero, b ); + xx->pos.x = p0.x - p.x; + xx->pos.y = p0.y - p.y; + xx->angle = b; + ComputeBoundingBox( trk ); + d = FindDistance( p0, p1 ); + d1 = FindDistance( q0, q1 ); +LOG( log_ease, 1, ( "NewJoint( [%0.3f %0.3f] A%0.3f, [%0.3f %0.3f] A%0.3f\n B01=%0.3f AZ0=%0.3f A01=%0.3f B=%0.3f D0=%0.3f D1=%0.3f\n", + pos0.x, pos0.y, angle0, pos1.x, pos1.y, angle1, + b01, az0, a01, b, d, d1 ) ) + CheckTrackLength( trk ); + return trk; +} + +/**************************************** + * + * GENERIC FUNCTIONS + * + */ + +static DIST_T GetLengthJoint( track_p trk ) +{ + struct extraData *xx; + DIST_T d0, d1; + xx = GetTrkExtraData(trk); + d0 = JoinD( xx->l0, xx->R, xx->L ); + d1 = JoinD( xx->l1, xx->R, xx->L ); + if (xx->Scurve) + return d0+d1; + else + return fabs( d0-d1 ); +} + + +static struct { + coOrd endPt[2]; + DIST_T elev[2]; + FLOAT_T length; + coOrd orig; + ANGLE_T angle; + DIST_T r; + DIST_T l; + DIST_T l0; + DIST_T l1; + FLOAT_T grade; + descPivot_t pivot; + LAYER_T layerNumber; + } jointData; +typedef enum { E0, Z0, E1, Z1, OR, AL, RR, LL, L0, L1, GR, PV, LY } jointDesc_e; +static descData_t jointDesc[] = { +/*E0*/ { DESC_POS, N_("End Pt 1: X"), &jointData.endPt[0] }, +/*Z0*/ { DESC_DIM, N_("Z"), &jointData.elev[0] }, +/*E1*/ { DESC_POS, N_("End Pt 2: X"), &jointData.endPt[1] }, +/*Z1*/ { DESC_DIM, N_("Z"), &jointData.elev[1] }, +/*OR*/ { DESC_POS, N_("Origin: X"), &jointData.orig }, +/*AL*/ { DESC_ANGLE, N_("Angle"), &jointData.angle }, +/*RR*/ { DESC_DIM, N_("R"), &jointData.r }, +/*LL*/ { DESC_DIM, N_("L"), &jointData.l }, +/*L0*/ { DESC_DIM, N_("l0"), &jointData.l0 }, +/*L1*/ { DESC_DIM, N_("l1"), &jointData.l1 }, +/*GR*/ { DESC_FLOAT, N_("Grade"), &jointData.grade }, +/*PV*/ { DESC_PIVOT, N_("Pivot"), &jointData.pivot }, +/*LY*/ { DESC_LAYER, N_("Layer"), &jointData.layerNumber }, + { DESC_NULL } }; + +static void UpdateJoint( track_p trk, int inx, descData_p descUpd, BOOL_T final ) +{ + EPINX_T ep; + switch (inx) { + case Z0: + case Z1: + ep = (inx==Z0?0:1); + UpdateTrkEndElev( trk, ep, GetTrkEndElevUnmaskedMode(trk,ep), jointData.elev[ep], NULL ); + ComputeElev( trk, 1-ep, FALSE, &jointData.elev[1-ep], NULL ); + if ( jointData.length > minLength ) + jointData.grade = fabs( (jointData.elev[0]-jointData.elev[1])/jointData.length )*100.0; + else + jointData.grade = 0.0; + jointDesc[GR].mode |= DESC_CHANGE; + jointDesc[inx==Z0?Z1:Z0].mode |= DESC_CHANGE; + return; + case LY: + SetTrkLayer( trk, jointData.layerNumber ); + break; + default: + return; + } +} + + +static void DescribeJoint( + track_p trk, + char * str, + CSIZE_T len ) +/* + * Print some interesting info about the track. + */ +{ + struct extraData *xx = GetTrkExtraData(trk); + int fix0, fix1; + + sprintf( str, _("Joint Track(%d): Layer=%d Length=%0.3f EP=[%0.3f,%0.3f A%0.3f] [%0.3f,%0.3f A%0.3f]"), GetTrkIndex(trk), + GetTrkLayer(trk)+1, + GetLengthJoint( trk ), + GetTrkEndPosXY(trk,0), GetTrkEndAngle(trk,0), + GetTrkEndPosXY(trk,1), GetTrkEndAngle(trk,1) ); + + fix0 = GetTrkEndTrk(trk,0)!=NULL; + fix1 = GetTrkEndTrk(trk,1)!=NULL; + + jointData.endPt[0] = GetTrkEndPos(trk,0); + jointData.endPt[1] = GetTrkEndPos(trk,1); + jointData.length = GetLengthJoint(trk); + jointData.orig = xx->pos; + jointData.angle = xx->angle; + jointData.r = xx->R; + jointData.l = xx->L; + jointData.l0 = xx->l0; + jointData.l1 = xx->l1; + jointData.layerNumber = GetTrkLayer(trk); + ComputeElev( trk, 0, FALSE, &jointData.elev[0], NULL ); + ComputeElev( trk, 1, FALSE, &jointData.elev[1], NULL ); + if ( jointData.length > minLength ) + jointData.grade = fabs( (jointData.elev[0]-jointData.elev[1])/jointData.length )*100.0; + else + jointData.grade = 0.0; + + jointDesc[E0].mode = + jointDesc[E1].mode = + jointDesc[OR].mode = + jointDesc[AL].mode = + jointDesc[RR].mode = + jointDesc[LL].mode = + jointDesc[L0].mode = + jointDesc[L1].mode = + DESC_RO; + jointDesc[Z0].mode = (EndPtIsDefinedElev(trk,0)?0:DESC_RO)|DESC_NOREDRAW; + jointDesc[Z1].mode = (EndPtIsDefinedElev(trk,1)?0:DESC_RO)|DESC_NOREDRAW; + jointDesc[GR].mode = DESC_RO; + jointDesc[PV].mode = (fix0|fix1)?DESC_IGNORE:0; + jointDesc[LY].mode = DESC_NOREDRAW; + jointData.pivot = (fix0&fix1)?DESC_PIVOT_NONE: + fix0?DESC_PIVOT_FIRST: + fix1?DESC_PIVOT_SECOND: + DESC_PIVOT_MID; + + DoDescribe( _("Easement Track"), trk, jointDesc, UpdateJoint ); +} + +static void GetJointPos( + coOrd * RP, + ANGLE_T * RA, + DIST_T l, + DIST_T R, + DIST_T L, + coOrd P, + ANGLE_T A, + BOOL_T N ) +/* + * Compute position of point on transition-curve. + */ +{ + coOrd p1; + static coOrd pZero = {0.0,0.0}; + ComputeJoinPos( l, R, L, NULL, RA, &p1, NULL ); + if (N) + p1.x = -p1.x; + Rotate( &p1, pZero, A ); + if (RP) { + RP->x = P.x + p1.x; + RP->y = P.y + p1.y; + } + if (RA) + *RA = NormalizeAngle( A + (N?-*RA:*RA) ); +} + + +EXPORT DIST_T JointDistance( + coOrd * q, + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T Scurve ) +{ + DIST_T d, l; + coOrd p0 = *q; + GetLandD( &l, &d, p0, pos, angle, R, L, negate, Scurve ); + if (Scurve) { + if ( l < -l1 ) + l = -l1; + else if ( l > l0 ) + l = l0; + } else { + if ( l < l0 ) + l = l0; + else if ( l > l1 ) + l = l1; + } + GetJointPos( q, NULL, l, R, L, pos, angle, negate ); + d = FindDistance( p0, *q ); + return d; +} + + +static DIST_T DistanceJoint( + track_p trk, + coOrd * p ) +/* + * Determine how close (p) is to (t). + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + return JointDistance( p, xx->pos, xx->angle, xx->l0, xx->l1, xx->R, xx->L, xx->negate, xx->Scurve ); +} + + +#ifdef LATER +static void DrawJointSegment1( + drawCmd_p d, + wIndex_t cnt, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + coOrd P, + ANGLE_T A, + BOOL_T N, + track_p trk, + DIST_T trackGauge, + wDrawColor color ) +/* + * Draw a transition-curve from (l0) to (l1), + * at angle (A) from origin (P). + */ +{ + DIST_T l, lincr; + wIndex_t i; + coOrd p0, p1; + long widthOptions = DTS_RIGHT|DTS_LEFT|DTS_TIES; + + if (GetTrkWidth(trk) == 2) + widthOptions |= DTS_THICK2; + if (GetTrkWidth(trk) == 3) + widthOptions |= DTS_THICK3; + + l = l0; + lincr = (l1-l0)/cnt; + GetJointPos( &p0, NULL, l0, R, L, P, A, N ); + for (i=1; i<=cnt; i++) { + l += lincr; + GetJointPos( &p1, NULL, l, R, L, P, A, N ); + DrawStraightTrack( d, p0, p1, + FindAngle( p1, p0 ), trk, trackGauge, color, widthOptions ); + p0 = p1; + } +} +#endif + +static void DrawJointSegment( + drawCmd_p d, + wIndex_t cnt, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + coOrd P, + ANGLE_T A, + BOOL_T N, + DIST_T trackGauge, + wDrawColor color, + long widthOptions, + track_p trk ) +/* + * Draw a transition-curve from (l0) to (l1), + * at angle (A) from origin (P). + */ +{ + DIST_T ll; + wIndex_t i; + coOrd p0, p1; + ANGLE_T a0, a1; + int cnt1; + + ComputeJoinPos( l0, R, L, NULL, &a0, NULL, NULL ); + ComputeJoinPos( l1, R, L, NULL, &a1, NULL, NULL ); + a1 = a1-a0; + if ( (d->options&DC_QUICK) ) { + cnt1 = 1; + } else { + cnt1 = (int)floor(a1/JOINT_ANGLE_INCR) + 1; + a1 /= cnt1; + } + + widthOptions |= DTS_RIGHT|DTS_LEFT|DTS_TIES; + GetJointPos( &p0, NULL, l0, R, L, P, A, N ); + for (i=1; i<=cnt1; i++) { + a0 += a1; + ll = sqrt( sin(D2R(a0)) * 2 * R * L ); + GetJointPos( &p1, NULL, ll, R, L, P, A, N ); + DrawStraightTrack( d, p0, p1, FindAngle( p1, p0 ), trk, trackGauge, + color, widthOptions ); + p0 = p1; + } +} + + +EXPORT coOrd GetJointSegEndPos( + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T flip, + BOOL_T Scurve, + EPINX_T ep, + ANGLE_T * angleR ) +{ + coOrd p1; + DIST_T ll; + if ( flip ) ep = 1-ep; + ll = (ep==0?l0:l1); + if ( Scurve ) { + if ( ep==1 ) + angle += 180; + } + GetJointPos( &p1, &angle, ll, R, L, pos, angle, negate ); + if ( angleR ) { + if ( (!Scurve) && ep==0 ) + angle = NormalizeAngle(angle+180); + *angleR = angle; + } + return p1; +} + + +EXPORT void DrawJointTrack( + drawCmd_p d, + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T flip, + BOOL_T Scurve, + track_p trk, + EPINX_T ep0, + EPINX_T ep1, + DIST_T trackGauge, + wDrawColor color, + long options ) +{ + wIndex_t cnt; + DIST_T len; + trkSeg_p segPtr; + + if ( (d->options&DC_SEGTRACK) ) { + DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); + segPtr = &tempSegs(tempSegs_da.cnt-1); + segPtr->type = SEG_JNTTRK; + segPtr->width = 0; + segPtr->color = wDrawColorBlack; + segPtr->u.j.pos = pos; + segPtr->u.j.angle = angle; + segPtr->u.j.l0 = l0; + segPtr->u.j.l1 = l1; + segPtr->u.j.R = R; + segPtr->u.j.L = L; + segPtr->u.j.negate = negate; + segPtr->u.j.flip = flip; + segPtr->u.j.Scurve = Scurve; + return; + } +LOG( log_ease, 4, ( "DJT( (X%0.3f Y%0.3f A%0.3f) \n", pos.x, pos.y, angle ) ) +#ifdef LATER + scale2rail = (d->options&DC_PRINT)?(twoRailScale*2+1):twoRailScale; + + if (options&DTS_THICK2) + width = 2; + if (options&DTS_THICK3) + width = 3; +#ifdef WINDOWS + width *= (wDrawWidth)(d->dpi/mainD.dpi); +#else + if (d->options&DC_PRINT) + width *= 300/75; +#endif +#endif + if (color == wDrawColorBlack) + color = normalColor; + if (!Scurve) { + /* print segments about 0.20" long */ + len = (l0-l1)/(0.20*d->scale); + cnt = (int)ceil(fabs(len)); + if (cnt == 0 || (d->options&DC_QUICK)) cnt = 1; + DrawJointSegment( d, cnt, l0, l1, R, L, pos, + angle, negate, trackGauge, color, options, trk ); + } else { + /* print segments about 0.20" long */ + cnt = (int)ceil((l0)/(0.20*d->scale)); + if (cnt == 0 || (d->options&DC_QUICK)) cnt = 1; + DrawJointSegment( d, cnt, 0, l0, R, L, pos, + angle, negate, trackGauge, color, options, trk ); + cnt = (int)ceil((l1)/(0.20*d->scale)); + if (cnt == 0 || (d->options&DC_QUICK)) cnt = 1; + DrawJointSegment( d, cnt, 0, l1, R, L, pos, + angle+180, negate, trackGauge, color, options, trk ); + } + if ( (d->funcs->options & wDrawOptTemp) == 0 && (d->options&DC_QUICK) == 0 ) { + DrawEndPt( d, trk, ep0, color ); + DrawEndPt( d, trk, ep1, color ); + } +} + + +static void DrawJoint( + track_p trk, + drawCmd_p d, + wDrawColor color ) +/* + * Draw a transition-curve. + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + long widthOptions = 0; + + if (GetTrkWidth(trk) == 2) + widthOptions = DTS_THICK2; + if (GetTrkWidth(trk) == 3) + widthOptions = DTS_THICK3; + DrawJointTrack( d, xx->pos, xx->angle, xx->l0, xx->l1, xx->R, xx->L, xx->negate, xx->flip, xx->Scurve, trk, 0, 1, GetTrkGauge(trk), color, widthOptions ); +} + + +static void DeleteJoint( + track_p t ) +/* Delete track - nothing to do */ +{ +} + +static BOOL_T WriteJoint( + track_p t, + FILE * f ) +/* + * Write track data to a file (f). + */ +{ + struct extraData * xx = GetTrkExtraData(t); + BOOL_T rc = TRUE; + rc &= fprintf(f, "JOINT %d %d %ld 0 0 %s %d %0.6f %0.6f %0.6f %0.6f %d %d %d %0.6f %0.6f 0 %0.6f\n", + GetTrkIndex(t), GetTrkLayer(t), (long)GetTrkWidth(t), + GetTrkScaleName(t), GetTrkVisible(t), xx->l0, xx->l1, xx->R, xx->L, + xx->flip, xx->negate, xx->Scurve, xx->pos.x, xx->pos.y, xx->angle )>0; + rc &= WriteEndPt( f, t, 0 ); + rc &= WriteEndPt( f, t, 1 ); + rc &= fprintf(f, "\tEND\n" )>0; + return rc; +} + +static void ReadJoint( + char * line ) +/* + * Read track data from a file (f). + */ +{ + track_p trk; + TRKINX_T index; + BOOL_T visible; + struct extraData e, *xx; + char scale[10]; + wIndex_t layer; + long options; + DIST_T elev; + + if ( !GetArgs( line+6, paramVersion<3?"dXZsdffffdddpYf":paramVersion<9?"dLl00sdffffdddpYf":"dLl00sdffffdddpff", + &index, &layer, &options, scale, &visible, &e.l0, &e.l1, &e.R, &e.L, + &e.flip, &e.negate, &e.Scurve, &e.pos, &elev, &e.angle) ) + return; + trk = NewTrack( index, T_EASEMENT, 0, sizeof e ); + xx = GetTrkExtraData(trk); + SetTrkVisible(trk, visible); + SetTrkScale(trk, LookupScale(scale)); + SetTrkLayer(trk, layer); + SetTrkWidth(trk, (int)(options&3)); + *xx = e; + ReadSegs(); + SetEndPts( trk, 2 ); + ComputeBoundingBox( trk ); +} + +static void MoveJoint( + track_p trk, + coOrd orig ) +/* + * Move a track. + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + xx->pos.x += orig.x; + xx->pos.y += orig.y; + ComputeBoundingBox( trk ); +} + +static void RotateJoint( + track_p trk, + coOrd orig, + ANGLE_T angle ) +/* + * Rotate a track. + */ +{ + struct extraData * xx = GetTrkExtraData(trk); + Rotate( &xx->pos, orig, angle ); + xx->angle = NormalizeAngle( xx->angle+angle ); + ComputeBoundingBox( trk ); +} + + +static void RescaleJoint( track_p trk, FLOAT_T ratio ) +{ + struct extraData *xx = GetTrkExtraData(trk); + xx->pos.x *= ratio; + xx->pos.y *= ratio; + xx->R *= ratio; + xx->L *= ratio; + xx->l0 *= ratio; + xx->l1 *= ratio; +} + + +static ANGLE_T GetAngleJoint( track_p trk, coOrd pos, EPINX_T * ep0, EPINX_T * ep1 ) +{ + struct extraData * xx = GetTrkExtraData(trk); + DIST_T l; + ANGLE_T a; + if ( ep0 && ep1 ) { + if (xx->flip) { + *ep0 = 1; + *ep1 = 0; + } else { + *ep0 = 0; + *ep1 = 1; + } + } + GetLandD( &l, NULL, pos, xx->pos, xx->angle, xx->R, xx->L, xx->negate, xx->Scurve ); + if (small(l)) { + a = xx->angle; + } else { +/* if (xx->Scurve && NormalizeAngle(FindAngle(xx->pos,pos)-xx->angle+90.0) > 180.0)*/ + if (xx->Scurve && l < 0.0) { + GetJointPos( NULL, &a, -l, xx->R, xx->L, xx->pos, xx->angle+180.0, xx->negate ); + a = NormalizeAngle( a-180.0 ); + } else { + GetJointPos( NULL, &a, l, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + } + } + return NormalizeAngle(a+180.0); +} + + +static void SplitJointA( + coOrd * posR, + EPINX_T ep, + struct extraData * xx, + struct extraData * xx1, + ANGLE_T * aR ) +{ + struct extraData * xx0; + BOOL_T flip; + DIST_T l; + + *xx1 = *xx; + if ( (ep==1) == (!xx->flip) ) { + xx0 = xx; + flip = FALSE; + } else { + xx0 = xx1; + xx1 = xx; + flip = TRUE; + } + GetLandD( &l, NULL, *posR, xx->pos, xx->angle, xx->R, xx->L, xx->negate, xx->Scurve ); + + if (!xx->Scurve) { + if (l < xx->l0 || l > xx->l1) { + NoticeMessage2( 0, "splitJoint: ! %0.3f <= %0.3f <= %0.3f", _("Ok"), NULL, xx->l0, l, xx->l1 ); + if ( l < xx->l0 ) l = xx->l0; + else if ( l > xx->l1 ) l = xx->l1; + } + GetJointPos( posR, aR, l, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + xx0->l1 = xx1->l0 = l; + } else if (small(l)){ + xx0->Scurve = xx1->Scurve = 0; + xx0->l1 = xx0->l0; + xx0->flip = !xx0->flip; + xx1->angle = NormalizeAngle(xx1->angle+180.0); + xx0->l0 = xx1->l0 = 0; + *posR = xx->pos; + *aR = xx1->angle; + } else { + GetJointPos( posR, aR, l, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + if (l > 0) { + xx0->Scurve = 0; + xx0->l1 = xx0->l0; + xx0->flip = !xx0->flip; + xx0->l0 = l; + xx1->l0 = l; + } else { + xx1->Scurve = 0; + xx1->l0 = -l; + xx1->angle = NormalizeAngle( xx1->angle+180.0 ); + xx0->l1 = -l; + } + *aR = NormalizeAngle( *aR+180.0 ); + } + if (flip) + *aR = NormalizeAngle( *aR + 180.0 ); +} + + +static BOOL_T SplitJoint( track_p trk, coOrd pos, EPINX_T ep, track_p * leftover, EPINX_T *ep0, EPINX_T *ep1 ) +{ + struct extraData *xx, *xx1; + track_p trk1; + ANGLE_T a; + + xx = GetTrkExtraData(trk); + trk1 = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + xx1 = GetTrkExtraData(trk1); + *xx1 = *xx; + SetTrkEndPoint( trk1, ep, GetTrkEndPos(trk,ep), GetTrkEndAngle(trk,ep) ); + *leftover = trk1; + *ep0 = 1-ep; + *ep1 = ep; + SplitJointA( &pos, ep, xx, xx1, &a ); + SetTrkEndPoint( trk, ep, pos, a ); + SetTrkEndPoint( trk1, 1-ep, pos, NormalizeAngle(a+180.0) ); + + ComputeBoundingBox( trk ); + ComputeBoundingBox( trk1 ); + return TRUE; +} + + +static BOOL_T TraverseJoint( + coOrd * posR, + ANGLE_T *angleR, + DIST_T *distR, + coOrd pos, + ANGLE_T angle, + DIST_T l0, + DIST_T l1, + DIST_T R, + DIST_T L, + BOOL_T negate, + BOOL_T flip, + BOOL_T Scurve ) +{ + + DIST_T l, lx, d, dx, ll0, ll1, d0, d1; + BOOL_T from_tangent, flip_angle; + + GetLandD( &l, &d, *posR, pos, angle, R, L, negate, Scurve ); + +LOG( log_traverseJoint, 2, ( "TJ: [%0.3f %0.3f] D%0.3f l0:%0.3f l1:%0.3f [%0.3f %0.3f] A%0.3f N%d F%d S%d = l:%0.3f ", + posR->x, posR->y, *distR, l0, l1, pos.x, pos.y, angle, negate, flip, Scurve, l ) ) + + if ( (!Scurve) ) { + if ( l < l0 ) l = l0; + else if ( l > l1 ) l = l1; + } else { + if ( l > l0 ) l = l0; + else if ( l < -l1 ) l = -l1; + } + + lx = l; + from_tangent = !flip; + flip_angle = from_tangent; + if ( !Scurve ) { + ll0 = l0; + ll1 = l1; + } else if ( l > 0 ) { + ll1 = l0; + ll0 = 0; + } else { + ll0 = 0; + ll1 = l1; + lx = -l; + from_tangent = !from_tangent; + } + dx = JoinD( lx, R, L ); + d0 = JoinD( ll0, R, L ); + d1 = JoinD( ll1, R, L ); + if ( from_tangent ) + d = d1 - dx; + else + d = dx - d0; + if ( *distR < d ) { + if ( from_tangent ) { + d = dx + *distR; + } else { + d = dx - *distR; + } + lx = GetLfromD( d, R, L ); + if ( l < 0 ) + lx = - lx; + /* compute posR and angleR */ + GetJointPos( posR, angleR, lx, R, L, pos, angle, negate ); + if ( ! flip_angle ) + *angleR = NormalizeAngle( *angleR + 180.0 ); + *distR = 0; + goto doreturn; + } + *distR -= d; + if ( Scurve && (!from_tangent) ) { + /* skip over midpoint */ + if ( l > 0 ) + d = JoinD( l1, R, L ); + else + d = JoinD( l0, R, L ); + if ( *distR < d ) { + lx = GetLfromD( *distR, R, L ); + if ( l > 0 ) + lx = - lx; + GetJointPos( posR, angleR, lx, R, L, pos, angle, negate ); + if ( ! flip_angle ) + *angleR = NormalizeAngle( *angleR + 180.0 ); + *distR = 0; + goto doreturn; + } + *distR -= d; + } +doreturn: +LOG( log_traverseJoint, 2, ( " [%0.3f %0.3f] A%0.3f D%0.3f\n", posR->x, posR->y, *angleR, *distR ) ) + return TRUE; +} + + +static BOOL_T TraverseJointTrack( + traverseTrack_p trvTrk, + DIST_T * distR ) +{ + track_p trk = trvTrk->trk; + struct extraData * xx = GetTrkExtraData(trk); + BOOL_T rc; + EPINX_T ep; + ANGLE_T angle; + BOOL_T flip; + + angle = NormalizeAngle( xx->angle-trvTrk->angle ); + flip = ( angle < 270 && angle > 90 ); + rc = TraverseJoint( &trvTrk->pos, &trvTrk->angle, distR, xx->pos, xx->angle, xx->l0, xx->l1, xx->R, xx->L, xx->negate, flip, xx->Scurve ); + if ( *distR > 0 ) { + ep = (flip?0:1); + if ( xx->flip ) + ep = 1-ep; + if ( xx->Scurve ) + ep = 1-ep; + trvTrk->pos = GetTrkEndPos( trk, ep ); + trvTrk->angle = GetTrkEndAngle( trk, ep ); + trvTrk->trk = GetTrkEndTrk( trk, ep ); + } + return rc; +} + + +static BOOL_T EnumerateJoint( track_p trk ) +{ + if (trk != NULL) { + ScaleLengthIncrement( GetTrkScale(trk), GetLengthJoint(trk) ); + } + return TRUE; +} + +static BOOL_T TrimJoint( track_p trk, EPINX_T ep, DIST_T maxX ) +{ + DeleteTrack( trk, FALSE ); + return TRUE; +} + + +static BOOL_T MergeJoint( + track_p trk0, + EPINX_T ep0, + track_p trk1, + EPINX_T ep1 ) +{ + track_p trk2; + EPINX_T ep2=-1; + coOrd pos; + ANGLE_T a; + struct extraData *xx0 = GetTrkExtraData(trk0); + struct extraData *xx1 = GetTrkExtraData(trk1); + + if ( ep0 == ep1 ) + return FALSE; + if ( xx0->R != xx1->R || + xx0->L != xx1->L || + xx0->flip != xx1->flip || + xx0->negate != xx1->negate || + xx0->angle != xx1->angle || + xx0->Scurve || + xx1->Scurve || + FindDistance( xx0->pos, xx1->pos ) > connectDistance ) + return FALSE; + + UndoStart( _("Merge Easements"), "MergeJoint( T%d[%d] T%d[%d] )", GetTrkIndex(trk0), ep0, GetTrkIndex(trk1), ep1 ); + UndoModify( trk0 ); + UndrawNewTrack( trk0 ); + trk2 = GetTrkEndTrk( trk1, 1-ep1 ); + if (trk2) { + ep2 = GetEndPtConnectedToMe( trk2, trk1 ); + DisconnectTracks( trk1, 1-ep1, trk2, ep2 ); + } + + if (ep0 == 0) { + xx0->l0 = xx1->l0; + } else { + xx0->l1 = xx1->l1; + } + + pos = GetTrkEndPos( trk1, 1-ep1 ); + a = GetTrkEndAngle( trk1, 1-ep1 ); + SetTrkEndPoint( trk0, ep0, pos, a ); + ComputeBoundingBox( trk0 ); + + DeleteTrack( trk1, TRUE ); + if (trk2) { + ConnectTracks( trk0, ep0, trk2, ep2 ); + } + DrawNewTrack( trk0 ); + return TRUE; +} + + +static BOOL_T GetParamsJoint( int inx, track_p trk, coOrd pos, trackParams_t * params ) +{ + params->type = curveTypeStraight; + params->ep = PickUnconnectedEndPoint( pos, trk ); + if (params->ep == -1) + return FALSE; + params->lineOrig = GetTrkEndPos(trk,params->ep); + params->lineEnd = params->lineOrig; + params->angle = GetTrkEndAngle(trk,params->ep); + params->len = 0.0; + params->arcR = 0.0; + return TRUE; +} + + +static BOOL_T MoveEndPtJoint( track_p *trk, EPINX_T *ep, coOrd pos, DIST_T d ) +{ + return FALSE; +} + + +static BOOL_T QueryJoint( track_p trk, int query ) +{ + struct extraData * xx = GetTrkExtraData(trk); + track_p trk1; + + switch ( query ) { + case Q_CANNOT_BE_ON_END: + case Q_IGNORE_EASEMENT_ON_EXTEND: + case Q_ISTRACK: + return TRUE; + case Q_CAN_PARALLEL: + if ( xx->Scurve ) { + if ( FindDistance( xx->pos, GetTrkEndPos(trk,0) ) <= minLength || + FindDistance( xx->pos, GetTrkEndPos(trk,1) ) <= minLength ) + return FALSE; + UndoStart( _("Split Easement Curve"), "queryJoint T%d Scurve", GetTrkIndex(trk) ); + SplitTrack( trk, xx->pos, 0, &trk1, FALSE ); + } + return TRUE; + default: + return FALSE; + } +} + + +static void FlipJoint( + track_p trk, + coOrd orig, + ANGLE_T angle ) +{ + struct extraData * xx = GetTrkExtraData(trk); + FlipPoint( &xx->pos, orig, angle ); + xx->angle = NormalizeAngle( 2*angle - xx->angle ); + xx->negate = !xx->negate; + ComputeBoundingBox( trk ); +} + + +static BOOL_T MakeParallelJoint( + track_p trk, + coOrd pos, + DIST_T sep, + track_p * newTrkR, + coOrd * p0R, + coOrd * p1R ) +{ + struct extraData * xx = GetTrkExtraData(trk), *xx1; + ANGLE_T angle, A; + coOrd p0, p1, P, q1, r1; + DIST_T d, d0; + DIST_T R, L, l0, l1, len, dl; + int cnt, inx; + + if ( xx->Scurve ) + return FALSE; + GetLandD( NULL, &d, pos, xx->pos, xx->angle, xx->R, xx->L, xx->negate, FALSE ); + angle = 90.0; + if ( (d < 0) == xx->negate ) + sep = -sep; + if ( xx->negate ) + angle = -90.0; + if ( xx->flip ) + angle = -angle; + p0 = GetTrkEndPos(trk,0); + p1 = GetTrkEndPos(trk,1); + d0 = FindDistance( p0, p1 ); + Translate( &p0, p0, GetTrkEndAngle(trk,0)+angle, sep ); + Translate( &p1, p1, GetTrkEndAngle(trk,1)-angle, sep ); + d = FindDistance( p0, p1 ); + angle = R2D(asin(xx->L/2/xx->R)); + A = xx->angle; + R = xx->R + sep*sin(D2R(angle)); + + dl = JoinD( xx->l1, xx->R, xx->L ) - JoinD( xx->l0, xx->R, xx->L ); +/*printf( "D = %0.3f %0.3f\n", d, dl );*/ + d /= d0; + R = xx->R * d; + L = xx->L * d; + l0 = xx->l0 * d; + l1 = xx->l1 * d; +/*printf( " R=%0.3f, L=%0.3f, l0=%0.3f, l1=%0.3f\n", R, L, l0, l1 );*/ + Translate( &P, xx->pos, xx->angle+(xx->negate?90:-90), sep ); + ComputeJoinPos( l1, R, L, NULL, NULL, &q1, NULL ); + r1 = (xx->flip?p0:p1); + r1.x -= P.x; + r1.y -= P.y; + Rotate( &r1, zero, -A ); + if ( xx->negate ) + r1.x = -r1.x; + if ( r1.x > 0 && q1.x > 0 ) { +/*printf( " %0.3f %0.3f, R=%0.3f ", q1.x, r1.x, R );*/ + R *= q1.x/r1.x; +/*printf( " %0.3f\n", R );*/ + } + + if ( newTrkR ) { + *newTrkR = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + xx1 = GetTrkExtraData( *newTrkR ); + *xx1 = *xx; + xx1->angle = A; + xx1->R = R; + xx1->L = L; + xx1->l0 = l0; + xx1->l1 = l1; + xx1->pos = P; + SetTrkEndPoint( *newTrkR, 0, p0, GetTrkEndAngle(trk,0) ); + SetTrkEndPoint( *newTrkR, 1, p1, GetTrkEndAngle(trk,1) ); + ComputeBoundingBox( *newTrkR ); + } else { + /* print segments about 0.20" long */ + dl = fabs(l0-l1); + len = dl/(0.20*mainD.scale); + cnt = (int)ceil(len); + if (cnt == 0 || (mainD.options&DC_QUICK)) cnt = 1; + dl /= cnt; + DYNARR_SET( trkSeg_t, tempSegs_da, cnt ); + for ( inx=0; inx<cnt; inx++ ) { + tempSegs(inx).color = wDrawColorBlack; + tempSegs(inx).width = 0; + tempSegs(inx).type = SEG_STRTRK; + if ( inx == 0 ) { + GetJointPos( &tempSegs(inx).u.l.pos[0], NULL, l0, R, L, P, A, xx->negate ); + } else { + tempSegs(inx).u.l.pos[0] = tempSegs(inx-1).u.l.pos[1]; + } + l0 += dl; + GetJointPos( &tempSegs(inx).u.l.pos[1], NULL, l0, R, L, P, A, xx->negate ); + } + tempSegs_da.cnt = cnt; + } + if ( p0R ) *p0R = p0; + if ( p1R ) *p1R = p1; + return TRUE; +} + + +static trackCmd_t easementCmds = { + "JOINT", + DrawJoint, + DistanceJoint, + DescribeJoint, + DeleteJoint, + WriteJoint, + ReadJoint, + MoveJoint, + RotateJoint, + RescaleJoint, + NULL, /* audit */ + GetAngleJoint, + SplitJoint, + TraverseJointTrack, + EnumerateJoint, + NULL, /* redraw */ + TrimJoint, + MergeJoint, + ExtendStraightFromOrig, + GetLengthJoint, + GetParamsJoint, + MoveEndPtJoint, + QueryJoint, + NULL, /* ungroup */ + FlipJoint, + NULL, + NULL, + NULL, + MakeParallelJoint }; + + +EXPORT void JointSegProc( + segProc_e cmd, + trkSeg_p segPtr, + segProcData_p data ) +{ + DIST_T l; + ANGLE_T a; + BOOL_T flip; + struct extraData * xx, xxx[2]; + coOrd p; + int inx; + EPINX_T ep0; + + switch (cmd) { + + case SEGPROC_TRAVERSE1: + GetLandD( &l, NULL, data->traverse1.pos, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.Scurve ); + if (small(l)) { + a = segPtr->u.j.angle; + } else { + if (segPtr->u.j.Scurve && l < 0.0) { + GetJointPos( NULL, &a, -l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle+180.0, segPtr->u.j.negate ); + a = NormalizeAngle( a-180.0 ); + } else { + GetJointPos( NULL, &a, l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.negate ); + } + } + a = NormalizeAngle( data->traverse1.angle+a ); + data->traverse1.backwards = (a < 270 && a > 90 ); + if ( !segPtr->u.j.Scurve ) { + if ( data->traverse1.backwards==0 ) + data->traverse1.dist = JoinD( l, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ); + else + data->traverse1.dist = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( l, segPtr->u.j.R, segPtr->u.j.L ); + } else { + data->traverse1.backwards = !data->traverse1.backwards; + if ( data->traverse1.backwards==0 ) + data->traverse1.dist = JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( l, segPtr->u.j.R, segPtr->u.j.L ); + else + data->traverse1.dist = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) + JoinD( l, segPtr->u.j.R, segPtr->u.j.L ); + } + if ( segPtr->u.j.flip ) + data->traverse1.backwards = !data->traverse1.backwards; +LOG( log_traverseJoint, 1, ( "TJ0: ?[%0.3f %0.3f] A=%0.3f l=%0.3f J[%0.3f %0.3f] A=%0.3f l0=%0.3f l1=%0.3f R=%0.3f L=%0.3f N:%d F:%d S:%d = a=%0.3f D=%0.3f B=%d\n", + data->traverse1.pos.x, data->traverse1.pos.y, data->traverse1.angle, + l, + segPtr->u.j.pos.x, segPtr->u.j.pos.y, segPtr->u.j.angle, + segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, + segPtr->u.j.negate, segPtr->u.j.flip, segPtr->u.j.Scurve, + a, data->traverse1.dist, data->traverse1.backwards ) ); + break; + + case SEGPROC_TRAVERSE2: + flip = segPtr->u.j.flip; + if (data->traverse2.segDir!=0) + flip = !flip; + if (segPtr->u.j.Scurve) + flip = !flip; + data->traverse2.pos = GetSegEndPt( segPtr, data->traverse2.segDir, FALSE, NULL ); + TraverseJoint( &data->traverse2.pos, &data->traverse2.angle, &data->traverse2.dist, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, flip, segPtr->u.j.Scurve ); + break; + + case SEGPROC_DRAWROADBEDSIDE: + /* TODO: JointSegProc( SEGPROC_DRAWROADBEDSIDE, ... */ + break; + + case SEGPROC_DISTANCE: + data->distance.dd = JointDistance( &data->distance.pos1, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.l0, segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.Scurve ); + break; + + case SEGPROC_FLIP: + segPtr->u.j.flip = !segPtr->u.j.flip; + break; + + case SEGPROC_NEWTRACK: + data->newTrack.trk = NewTrack( 0, T_EASEMENT, 2, sizeof *xx ); + xx = GetTrkExtraData(data->newTrack.trk); + xx->pos = segPtr->u.j.pos; + xx->angle = segPtr->u.j.angle; + xx->l0 = segPtr->u.j.l0; + xx->l1 = segPtr->u.j.l1; + xx->R = segPtr->u.j.R; + xx->L = segPtr->u.j.L; + xx->negate = segPtr->u.j.negate; + xx->flip = segPtr->u.j.flip; + xx->Scurve = segPtr->u.j.Scurve; + ep0 = 0; + if ( xx->flip ) + ep0 = 1-ep0; + if ( xx->Scurve ) + ep0 = 1-ep0; + GetJointPos( &p, &a, xx->l0, xx->R, xx->L, xx->pos, xx->angle, xx->negate ); + if ( !xx->Scurve ) + a = NormalizeAngle(a+180.0); + SetTrkEndPoint( data->newTrack.trk, ep0, p, a ); + a = xx->angle; + if ( xx->Scurve ) + a = NormalizeAngle(a+180.0); + GetJointPos( &p, &a, xx->l1, xx->R, xx->L, xx->pos, a, xx->negate ); + if ( xx->Scurve ) + a = NormalizeAngle(a+180.0); + SetTrkEndPoint( data->newTrack.trk, 1-ep0, p, a ); + ComputeBoundingBox( data->newTrack.trk ); + data->newTrack.ep[0] = 0; + data->newTrack.ep[1] = 1; + break; + + case SEGPROC_LENGTH: + if ( !segPtr->u.j.Scurve ) + data->length.length = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) - JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ); + else + data->length.length = JoinD( segPtr->u.j.l1, segPtr->u.j.R, segPtr->u.j.L ) + JoinD( segPtr->u.j.l0, segPtr->u.j.R, segPtr->u.j.L ); + break; + + case SEGPROC_SPLIT: + xxx[0].pos = segPtr->u.j.pos; + xxx[0].angle = segPtr->u.j.angle; + xxx[0].l0 = segPtr->u.j.l0; + xxx[0].l1 = segPtr->u.j.l1; + xxx[0].R = segPtr->u.j.R; + xxx[0].L = segPtr->u.j.L; + xxx[0].negate = segPtr->u.j.negate; + xxx[0].flip = segPtr->u.j.flip; + xxx[0].Scurve = segPtr->u.j.Scurve; + SplitJointA( &data->split.pos, 0, &xxx[0], &xxx[1], &a ); + for ( inx=0; inx<2; inx++ ) { + xx = &xxx[(!segPtr->u.j.flip)?1-inx:inx]; + data->split.newSeg[inx] = *segPtr; + data->split.newSeg[inx].u.j.pos = xx->pos; + data->split.newSeg[inx].u.j.angle = xx->angle; + data->split.newSeg[inx].u.j.l0 = xx->l0; + data->split.newSeg[inx].u.j.l1 = xx->l1; + data->split.newSeg[inx].u.j.R = xx->R; + data->split.newSeg[inx].u.j.L = xx->L; + data->split.newSeg[inx].u.j.negate = xx->negate; + data->split.newSeg[inx].u.j.flip = xx->flip; + data->split.newSeg[inx].u.j.Scurve = xx->Scurve; + if ( !xx->Scurve ) + data->split.length[inx] = JoinD( xx->l1, xx->R, xx->L ) - JoinD( xx->l0, xx->R, xx->L ); + else + data->split.length[inx] = JoinD( xx->l1, xx->R, xx->L ) + JoinD( xx->l0, xx->R, xx->L ); + } + break; + + case SEGPROC_GETANGLE: + GetLandD( &l, NULL, data->getAngle.pos, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.negate, segPtr->u.j.Scurve ); + if (small(l)) { + a = segPtr->u.j.angle; + } else { + if (segPtr->u.j.Scurve && l < 0.0) { + GetJointPos( NULL, &a, -l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle+180.0, segPtr->u.j.negate ); + a = NormalizeAngle( a-180.0 ); + } else { + GetJointPos( NULL, &a, l, segPtr->u.j.R, segPtr->u.j.L, segPtr->u.j.pos, segPtr->u.j.angle, segPtr->u.j.negate ); + } + } + data->getAngle.angle = a; + break; + } +} + + + +#ifndef TEST +BOOL_T JoinTracks( + track_p trk0, + EPINX_T ep0, + coOrd pos0, + track_p trk1, + EPINX_T ep1, + coOrd pos1, + easementData_t * e ) +/* + * Join 2 tracks with joint described in (e). + * (pos0) and (pos1) are points that would be connected if there was no + * transition-curve. + * If there is then: + * (pos0) and (pos1) have been moved (x) apart. + * Adjust the endPoints by moving (pos0) and (pos1) by (e->d0) and (e->d1) + * along the track. + * Connect the tracks. + */ +{ + track_p joint; + +LOG( log_ease, 1, ( "join T%d[%d] @[%0.3f %0.3f], T%d[%d] @[%0.3f %0.3f]\n", + GetTrkIndex(trk0), ep0, pos0.x, pos0.y, GetTrkIndex(trk1), ep1, pos1.x, pos1.y ) ) + + if ( GetTrkType(trk0) == T_EASEMENT ) { + DIST_T d; + ANGLE_T aa; + d = FindDistance( GetTrkEndPos(trk0,ep0), GetTrkEndPos(trk1,ep1) ); + aa = NormalizeAngle( GetTrkEndAngle(trk0,ep0) - GetTrkEndAngle(trk1,ep1) + 180.0 + connectAngle/2.0 ); + if ( d <= connectDistance && aa <= connectAngle ) { + ConnectTracks( trk0, ep0, trk1, ep1 ); + } + return TRUE; + } + + /* Move the endPoint for (trk0) */ + if (!MoveEndPt( &trk0, &ep0, pos0, e->d0 )) + return FALSE; + + /* Move the endPoint for (trk1) */ + if (!MoveEndPt( &trk1, &ep1, pos1, e->d1 )) + return FALSE; + +LOG( log_ease, 1, ( " EASE R%0.3f..%0.3f L%0.3f..%0.3f\n", + e->r0, e->r1, e->d0, e->d1 ) ) + + /* Connect the tracks */ + if (e->x == 0.0) { + /* No transition-curve */ + ConnectTracks( trk0, ep0, trk1, ep1 ); + } else { + /* Connect with transition-curve */ + joint = NewJoint( GetTrkEndPos(trk0,ep0), GetTrkEndAngle(trk0,ep0), + GetTrkEndPos(trk1,ep1), GetTrkEndAngle(trk1,ep1), + GetTrkGauge(trk0), easeR, easeL, e ); + CopyAttributes( trk0, joint ); + ConnectTracks( trk1, ep1, joint, 1 ); + ConnectTracks( trk0, ep0, joint, 0 ); + DrawNewTrack( joint ); + } + return TRUE; +} + + +EXPORT void UndoJoint( + track_p trk, + EPINX_T ep, + track_p trk1, + EPINX_T ep1 ) +{ + struct extraData * xx; + DIST_T d; + + if ( GetTrkType(trk1) != T_EASEMENT ) + return; + xx = GetTrkExtraData(trk1); + if ( ep1 == 0 ) + d = xx->L/2.0 - xx->l0; + else + d = xx->l1 - xx->L/2.0; + if ( d < 0.01 ) + return; + UndrawNewTrack( trk ); + MoveEndPt( &trk, &ep, GetTrkEndPos(trk,ep), -d ); + DrawNewTrack( trk ); +} +#endif + +/***************************************************************************** + * + * INITIALIZATION + * + */ + + + +void InitTrkEase( void ) +{ + T_EASEMENT = InitObject( &easementCmds ); + log_ease = LogFindIndex( "ease" ); + log_traverseJoint = LogFindIndex( "traverseJoint" ); +} + + +/***************************************************************************** + * + * TEST + * + */ + +#ifdef TEST + + +void ErrorMessage( char * msg, ... ) +{ + lprintf( "%s\n", msg ); +} + +void InfoMessage( char * msg, ... ) +{ + lprintf( "%s\n", msg ); +} + +scaleInfo_p curScale; + +track_p NewTrack( TRKINX_T a, TRKTYP_T b, EPINX_T c, TRKTYP_T d ) +{ + return NULL; +} + +void DrawStraightTrack( drawCmd_p a, coOrd b, coOrd c, ANGLE_T d, + DIST_T trackGauge, wDrawColor color, int opts ) +{ +} + +void DrawNewTrack( track_p t ) +{ +} + +static DIST_T JoinDalt( + DIST_T x, + DIST_T R, + DIST_T L ) +/* + * Alternative distance computation, integrate over the curve. + */ +{ +#define DCNT (1000) + DIST_T d; + wIndex_t i; + coOrd p0, p1; + d = 0.0; + p0.x = p0.y = 0.0; + for ( i=1;i<=DCNT; i++) { + ComputeJoinPos( x*((DIST_T)i)/((DIST_T)DCNT), R, L, NULL, NULL, &p1, NULL ); + d += FindDistance( p0, p1 ); + p0 = p1; + } + return d; +} + + +test_plot( INT_T argc, char * argv[] ) +{ + DIST_T l, X, L, rr, ra, d, d1, R; + coOrd p, pc, p1; + INT_T i, C; + if (argc != 4) { + lprintf("%s R L C\n", argv[0]); + Exit(1); + } + argv++; + R = atof( *argv++ ); + L = atof( *argv++ ); + C = atol( *argv++ ); + X = L*L/(24*R); + lprintf("R=%0.3f X=%0.3f L=%0.3f\n", R, X, L ); + + for (i=0;i<=C;i++) { + l = L*((DIST_T)i)/((DIST_T)C); + d = JoinD( l, R, L ); + d1 = JoinDalt( l, R, L ); + ComputeJoinPos( l, R, L, &rr, &ra, &p, &pc ); + lprintf("d: [%0.3f %0.3f] [%0.3f %03f] R=%0.3f A=%0.3f D=%0.3f D1=%0.3f X=%0.4f\n", + i, p.x, p.y, pc.x, pc.y, rr, ra, d, d1, pc.x-rr ); + } +} + +test_psplot( INT_T argc, char * argv[] ) +{ + DIST_T l, L, rr, ra, d, d1, R, S, X; + coOrd p, q, pc, p1; + INT_T i, C; + if (argc != 5) { + lprintf("%s R L C S\n", argv[0]); + Exit(1); + } + argv++; + easeR = R = atof( *argv++ ); + easeL = L = atof( *argv++ ); + C = atol( *argv++ ); + S = atof( *argv++ ); + X = L*L/(24*R); + +lprintf("%%! kvjfv\nsave\n0 setlinewidth\n"); +lprintf("/Times-BoldItalic findfont 16 scalefont setfont\n"); +lprintf("36 36 moveto (R=%0.3f X=%0.3f L=%0.3f S=%0.3f) show\n", easeR, X, L, S ); +/*lprintf("24 768 translate -90 rotate\n");*/ +lprintf("gsave\n72 72 translate\n"); +lprintf("%0.3f %0.3f scale\n", 72.0/S, 72.0/S ); +lprintf("%0.3f %0.3f moveto %0.3f %0.3f lineto stroke\n", 0.0, 0.0, L, 0.0 ); +lprintf("%0.3f %0.3f %0.3f 270.0 90.0 arc stroke\n", L/2.0, easeR+X, easeR ); +lprintf("%0.3f %0.3f %0.3f 0.0 360.0 arc stroke\n", 0.0, 0.0, 0.25 ); + q.x = q.y = 0.0; + for (i=0;i<=C;i++) { + l = L*((DIST_T)i)/((DIST_T)C); + ComputeJoinPos( l, R, L, &rr, &ra, &p, &pc ); +lprintf("%0.3f %0.3f moveto %0.3f %0.3f lineto stroke\n", q.x, q.y, p.x, p.y ); + q = p; + } +lprintf("%0.3f %0.3f %0.3f 0.0 360.0 arc stroke\n", p.x, p.y, 0.25 ); +lprintf("grestore\nrestore\nshowpage\n%%Trailer\n%%Pages: 1\n"); +} + +void Test_compute( INT_T argc, char * argv[] ) +{ + DIST_T r0, r1, x, l0, l1, R, X, d; + coOrd q0, q1, qc0, qc1; + easementData_t e; + if (argc != 5) { + lprintf("compute R0 R1 R L\n"); + Exit(1); + } + /*debugEase = 5;*/ + argv++; + r0 = atof( *argv++); + r1 = atof( *argv++); + easementVal = 1.0; + easeR = atof( *argv++); + easeL = atof( *argv++); + ComputeJoint( r0, r1, &e ); + ComputeJoinPos( e.l0, easeR, easeL, NULL, NULL, &q0, &qc0 ); + ComputeJoinPos( e.l1, easeR, easeL, NULL, NULL, &q1, &qc1 ); + if (e.Scurve) { + q1.x = - q1.x; q1.y = - q1.y; + qc1.x = - qc1.x; qc1.y = - qc1.y; + } + d = FindDistance( q0, q1 ); + lprintf("ENDPT [%0.3f %0.3f] [%0.3f %0.3f]\n", q0.x, q0.y, q1.x, q1.y ); + lprintf("CENTER [%0.3f %0.3f] [%0.3f %0.3f]\n", qc0.x, qc0.y, qc1.x, qc1.y ); + lprintf("ComputeJoint( %0.3f %0.3f) { %0.3f %0.3f %0.3f } D0=%0.5f D1=%0.5f, D=%0.3f\n", + r0, r1, easeR, easeL, e.x, e.d0, e.d1, d ); +} + +void Test_findL( INT_T argc, char * argv[] ) +{ + DIST_T l, r, R, L; + if (argc != 5) { + lprintf("findL r R L\n"); + Exit(1); + } + /*debugEase = 5;*/ + argv++; + r = atof( *argv++ ); + R = atof( *argv++ ); + L = atof( *argv++ ); + l = FindL( r, R, L ); + lprintf("FindL( %0.3f %0.3f %0.3f ) = %0.3f\n", r, R, L, l ); +} + + +main( INT_T argc, char * argv[] ) +{ +INT_T flagX = 0; +INT_T flagV = 0; + if (argc<1) { + lprintf("plot|compute\n"); + Exit(1); + } + argv++; argc--; + while (argv[0][0] == '-') { + switch (argv[0][1]) { + case 'x': + flagX++; + argc--;argv++; + break; + case 'v': + flagV++; + argc--;argv++; + break; + default: + lprintf("Huh: %s\n", *argv ); + argc--;argv++; + break; + } + } + if (strcmp(argv[0],"plot")==0) { + Test_plot( argc, argv ); + } else if (strcmp(argv[0],"psplot")==0) { + Test_psplot( argc, argv ); + } else if (strcmp(argv[0],"compute")==0) { + Test_compute( argc, argv ); + } else if (strcmp(argv[0],"findL")==0) { + Test_findL( argc, argv ); + } else { + lprintf("unknown cmd %s\n", argv[0] ); + } +} +#endif |