/* * $Header: /home/dmarkle/xtrkcad-fork-cvs/xtrkcad/app/bin/trkseg.c,v 1.2 2006-05-30 16:11:55 m_fischer Exp $ */ /* 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 "common.h" #include "fileio.h" #include "param.h" #include "track.h" #include "misc.h" #include "cbezier.h" #include "tbezier.h" #include "cjoin.h" /***************************************************************************** * * TRACK SEGMENTS * * Notes: Segments are used * 1. as temporary elements during editing operations * 2. as a means of grouping primitives for compounds * 3. as the way of drawing and operating on Bezier curves * * They are stored as dynamic arrays which can be displayed and operated on as sets. * */ /* * Build a Segment that has a radius and passes through two points. This uses the knowledge * that the center of curve is always on an orthogonal line through the bisection of a chord. */ EXPORT void ComputeCurvedSeg( trkSeg_p s, DIST_T radius, coOrd p0, coOrd p1 ) { DIST_T d; ANGLE_T a, aa, aaa; s->u.c.radius = radius; d = FindDistance( p0, p1 )/2.0; a = FindAngle( p0, p1 ); if (radius > 0) { aa = R2D(asin( d/radius )); aaa = a + (90.0 - aa); Translate( &s->u.c.center, p0, aaa, radius ); s->u.c.a0 = NormalizeAngle( aaa + 180.0 ); s->u.c.a1 = aa*2.0; } else { aa = R2D(asin( d/(-radius) )); aaa = a - (90.0 - aa); Translate( &s->u.c.center, p0, aaa, -radius ); s->u.c.a0 = NormalizeAngle( aaa + 180.0 - aa *2.0 ); s->u.c.a1 = aa*2.0; } } EXPORT coOrd GetSegEndPt( trkSeg_p segPtr, EPINX_T ep, BOOL_T bounds, ANGLE_T * angleR ) { coOrd pos; ANGLE_T angle, a, a0, a1 = 0.0; DIST_T r; POS_T x0, y0, x1, y1; switch (segPtr->type) { case SEG_STRTRK: case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: pos = segPtr->u.l.pos[ep]; angle = FindAngle( segPtr->u.l.pos[1-ep], segPtr->u.l.pos[ep] ); break; case SEG_CRVLIN: case SEG_CRVTRK: a0 = segPtr->u.c.a0; a1 = segPtr->u.c.a1; r = fabs( segPtr->u.c.radius ); a = a0; if ( (ep==1) == (segPtr->u.c.radius>0) ) { a += a1; angle = NormalizeAngle( a+90 ); } else { angle = NormalizeAngle( a-90 ); } if (bounds) { x0 = r * sin(D2R(a0)); x1 = r * sin(D2R(a0+a1)); y0 = r * cos(D2R(a0)); y1 = r * cos(D2R(a0+a1)); if (ep == 0) { pos.x = segPtr->u.c.center.x + (((a0<=270.0)&&(a0+a1>=270.0)) ? (-r) : min(x0,x1)); pos.y = segPtr->u.c.center.y + (((a0<=180.0)&&(a0+a1>=180.0)) ? (-r) : min(y0,y1)); } else { pos.x = segPtr->u.c.center.x + (((a0<= 90.0)&&(a0+a1>= 90.0)) ? (r) : max(x0,x1)); pos.y = segPtr->u.c.center.y + ((a0+a1>=360.0) ? (r) : max(y0,y1)); } } else { PointOnCircle( &pos, segPtr->u.c.center, fabs(segPtr->u.c.radius), a ); } break; case SEG_JNTTRK: pos = GetJointSegEndPos( 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.flip, segPtr->u.j.Scurve, ep, &angle ); break; case SEG_BEZTRK: case SEG_BEZLIN: if (ep ==1) { pos = segPtr->u.b.pos[3]; //For Bezier, use the End Points of the overall curve angle = FindAngle(segPtr->u.b.pos[2],segPtr->u.b.pos[3]); } else { pos = segPtr->u.b.pos[0]; angle = FindAngle(segPtr->u.b.pos[1],segPtr->u.b.pos[0]); } break; default: AbortProg("GetSegCntPt(%c)", segPtr->type ); } if ( angleR ) *angleR = angle; return pos; } /** * Calculate the bounding box for a string. * * \param coOrd IN position of text * \param angle IN text angle * \param str IN the string * \param fs IN size of font * \param loR OUT bottom left corner * \param hiR OUT top right corner */ EXPORT void GetTextBounds( coOrd pos, ANGLE_T angle, char * str, FONTSIZE_T fs, coOrd * loR, coOrd * hiR ) { coOrd size, size2; POS_T descent = 0.0, ascent = 0.0; coOrd lo, hi; coOrd p[4]; coOrd lastL; int i; DrawMultiLineTextSize(&mainD, str, NULL, fs, FALSE, &size, &lastL); // set up the corners of the rectangle p[0].x = p[3].x = 0.0; p[1].x = p[2].x = size.x; DrawTextSize2(&mainD, "A", NULL, fs, FALSE, &size2, &descent, &ascent); p[0].y = p[1].y = lastL.y - descent; p[2].y = p[3].y = size2.y; lo = hi = zero; // rotate each point for (i=0; i<4; i++) { Rotate(&p[i], zero, angle); if (p[i].x < lo.x) { lo.x = p[i].x; } if (p[i].y < lo.y) { lo.y = p[i].y; } if (p[i].x > hi.x) { hi.x = p[i].x; } if (p[i].y > hi.y) { hi.y = p[i].y; } } // now recaclulate the corners loR->x = pos.x + lo.x; loR->y = pos.y + lo.y; hiR->x = pos.x + hi.x; hiR->y = pos.y + hi.y; } static void Get1SegBounds( trkSeg_p segPtr, coOrd xlat, ANGLE_T angle, coOrd *lo, coOrd *hi ) { int inx; coOrd p0, p1, pBez[4], pc; ANGLE_T a0, a1; coOrd width; DIST_T radius; width = zero; switch ( segPtr->type ) { case ' ': return; case SEG_STRTRK: case SEG_CRVTRK: case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: case SEG_CRVLIN: case SEG_JNTTRK: REORIGIN( p0, GetSegEndPt( segPtr, 0, FALSE, NULL ), angle, xlat ) REORIGIN( p1, GetSegEndPt( segPtr, 1, FALSE, NULL ), angle, xlat ) if (p0.x < p1.x) { lo->x = p0.x; hi->x = p1.x; } else { lo->x = p1.x; hi->x = p0.x; } if (p0.y < p1.y) { lo->y = p0.y; hi->y = p1.y; } else { lo->y = p1.y; hi->y = p0.y; } if ( (segPtr->type == SEG_CRVTRK) || (segPtr->type == SEG_CRVLIN) ) { /* TODO: be more precise about curved line width */ width.x = width.y = fabs(segPtr->width)/2.0; REORIGIN( pc, segPtr->u.c.center, angle, xlat ); a0 = NormalizeAngle( segPtr->u.c.a0 + angle ); a1 = segPtr->u.c.a1; radius = fabs(segPtr->u.c.radius); if ( a1 >= 360.0 ) { lo->x = pc.x - radius; lo->y = pc.y - radius; hi->x = pc.x + radius; hi->y = pc.y + radius; break; } if ( a0 + a1 >= 360.0 ) hi->y = pc.y + radius; if ( a0 < 90.0 && a0+a1 >= 90.0 ) hi->x = pc.x + radius; if ( a0 > 90.0 && a0+a1 >= 450.0 ) hi->x = pc.x + radius; if ( a0 < 180.0 && a0+a1 >= 180.0 ) lo->y = pc.y - radius; if (a0 > 180.0 && a0+a1 >= 540.0 ) lo->y = pc.y - radius; if ( a0 < 270.0 && a0+a1 >= 270.0 ) lo->x = pc.x - radius; if ( a0 > 270.0 && a0+a1 >= 630.0 ) lo->x = pc.x - radius; } if ( segPtr->type == SEG_STRLIN ) { width.x = fabs(segPtr->width) * fabs(cos( D2R( FindAngle(p0, p1) ) ) ) / 2.0; width.y = fabs(segPtr->width) * fabs(sin( D2R( FindAngle(p0, p1) ) ) ) / 2.0; } else if ( segPtr->type == SEG_BENCH ) { width.x = BenchGetWidth( segPtr->u.l.option ) * fabs(cos( D2R( FindAngle(p0, p1) ) ) ) / 2.0; width.y = BenchGetWidth( segPtr->u.l.option ) * fabs(sin( D2R( FindAngle(p0, p1) ) ) ) / 2.0; } break; case SEG_POLY: /* TODO: be more precise about poly line width */ width.x = width.y = fabs(segPtr->width)/2.0; case SEG_FILPOLY: for (inx=0; inxu.p.cnt; inx++ ) { REORIGIN( p0, segPtr->u.p.pts[inx].pt, angle, xlat ) if (inx==0) { *lo = *hi = p0; } else { if (p0.x < lo->x) lo->x = p0.x; if (p0.y < lo->y) lo->y = p0.y; if (p0.x > hi->x) hi->x = p0.x; if (p0.y > hi->y) hi->y = p0.y; } } break; case SEG_FILCRCL: REORIGIN( p0, segPtr->u.c.center, angle, xlat ) lo->x = p0.x - fabs(segPtr->u.c.radius); hi->x = p0.x + fabs(segPtr->u.c.radius); lo->y = p0.y - fabs(segPtr->u.c.radius); hi->y = p0.y + fabs(segPtr->u.c.radius); break; case SEG_TEXT: REORIGIN( p0, segPtr->u.t.pos, angle, xlat ) GetTextBounds( p0, angle+segPtr->u.t.angle, segPtr->u.t.string, segPtr->u.t.fontSize, lo, hi ); break; case SEG_BEZLIN: case SEG_BEZTRK: //Bezier control arms form a "tent" around the curve REORIGIN( pBez[0], segPtr->u.b.pos[0], angle, xlat ) REORIGIN( pBez[1], segPtr->u.b.pos[1], angle, xlat ) REORIGIN( pBez[2], segPtr->u.b.pos[2], angle, xlat ) REORIGIN( pBez[3], segPtr->u.b.pos[3], angle, xlat ) lo->x = hi->x = pBez[0].x; lo->y = hi->y = pBez[0].y; for (int i=1;i<4;i++) { lo->x = lo->x>pBez[i].x?pBez[i].x:lo->x; lo->y = lo->y>pBez[i].y?pBez[i].y:lo->y; hi->x = hi->xx; hi->y = hi->yy; } width.x = width.y = fabs(segPtr->width)/2.0; break; default: ; } lo->x -= width.x; lo->y -= width.y; hi->x += width.x; hi->y += width.y; } EXPORT void GetSegBounds( coOrd xlat, ANGLE_T angle, wIndex_t segCnt, trkSeg_p segs, coOrd * orig_ret, coOrd * size_ret ) { trkSeg_p s; coOrd lo, hi, tmpLo, tmpHi; BOOL_T first; first = TRUE; for (s=segs; s<&segs[segCnt]; s++) { if (s->type == ' ') continue; if (first) { Get1SegBounds( s, xlat, angle, &lo, &hi ); first = FALSE; } else { Get1SegBounds( s, xlat, angle, &tmpLo, &tmpHi ); if (tmpLo.x < lo.x) lo.x = tmpLo.x; if (tmpLo.y < lo.y) lo.y = tmpLo.y; if (tmpHi.x > hi.x) hi.x = tmpHi.x; if (tmpHi.y > hi.y) hi.y = tmpHi.y; } } if (first) { *orig_ret = xlat; *size_ret = zero; return; } if (lo.x < hi.x) { orig_ret->x = lo.x; size_ret->x = hi.x-lo.x; } else { orig_ret->x = hi.x; size_ret->x = lo.x-hi.x; } if (lo.y < hi.y) { orig_ret->y = lo.y; size_ret->y = hi.y-lo.y; } else { orig_ret->y = hi.y; size_ret->y = lo.y-hi.y; } } EXPORT void MoveSegs( wIndex_t segCnt, trkSeg_p segs, coOrd orig ) { trkSeg_p s; int inx; for (s=segs; s<&segs[segCnt]; s++) { switch (s->type) { case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: case SEG_STRTRK: s->u.l.pos[0].x += orig.x; s->u.l.pos[0].y += orig.y; s->u.l.pos[1].x += orig.x; s->u.l.pos[1].y += orig.y; break; case SEG_CRVLIN: case SEG_CRVTRK: case SEG_FILCRCL: s->u.c.center.x += orig.x; s->u.c.center.y += orig.y; break; case SEG_TEXT: s->u.t.pos.x += orig.x; s->u.t.pos.y += orig.y; break; case SEG_POLY: case SEG_FILPOLY: for (inx=0; inxu.p.cnt; inx++) { s->u.p.pts[inx].pt.x += orig.x; s->u.p.pts[inx].pt.y += orig.y; } break; case SEG_JNTTRK: s->u.j.pos.x += orig.x; s->u.j.pos.y += orig.y; break; case SEG_BEZTRK: case SEG_BEZLIN: for (inx=0;inx<4;inx++) { s->u.b.pos[inx].x +=orig.x; s->u.b.pos[inx].y +=orig.y; } FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK); break; } } } EXPORT void RotateSegs( wIndex_t segCnt, trkSeg_p segs, coOrd orig, ANGLE_T angle ) { trkSeg_p s; int inx; for (s=segs; s<&segs[segCnt]; s++) { switch (s->type) { case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: case SEG_STRTRK: Rotate( &s->u.l.pos[0], orig, angle ); Rotate( &s->u.l.pos[1], orig, angle ); break; case SEG_CRVLIN: case SEG_CRVTRK: case SEG_FILCRCL: Rotate( &s->u.c.center, orig, angle ); s->u.c.a0 = NormalizeAngle( s->u.c.a0+angle ); break; case SEG_TEXT: Rotate( &s->u.t.pos, orig, angle ); s->u.t.angle = NormalizeAngle( s->u.t.angle+angle ); break; case SEG_POLY: case SEG_FILPOLY: for (inx=0; inxu.p.cnt; inx++) { Rotate( &s->u.p.pts[inx].pt, orig, angle ); } break; case SEG_JNTTRK: Rotate( &s->u.j.pos, orig, angle ); s->u.j.angle = NormalizeAngle( s->u.j.angle+angle ); break; case SEG_BEZLIN: case SEG_BEZTRK: Rotate( &s->u.b.pos[0], orig, angle ); Rotate( &s->u.b.pos[1], orig, angle ); Rotate( &s->u.b.pos[2], orig, angle ); Rotate( &s->u.b.pos[3], orig, angle ); FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK); break; } } } EXPORT void FlipSegs( wIndex_t segCnt, trkSeg_p segs, coOrd orig, ANGLE_T angle ) { trkSeg_p s; int inx; pts_t * pts; for (s=segs; s<&segs[segCnt]; s++) { switch (s->type) { case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: case SEG_STRTRK: s->u.l.pos[0].y = -s->u.l.pos[0].y; s->u.l.pos[1].y = -s->u.l.pos[1].y; break; case SEG_CRVTRK: s->u.c.radius = - s->u.c.radius; case SEG_CRVLIN: case SEG_FILCRCL: s->u.c.center.y = -s->u.c.center.y; if ( s->u.c.a1 < 360.0 ) s->u.c.a0 = NormalizeAngle( 180.0 - s->u.c.a0 - s->u.c.a1 ); break; case SEG_TEXT: s->u.t.pos.y = -s->u.t.pos.y; /* TODO flip text angle */ break; case SEG_POLY: case SEG_FILPOLY: pts = (pts_t*)MyMalloc( s->u.p.cnt * sizeof (pts_t) ); memcpy( pts, s->u.p.pts, s->u.p.cnt * sizeof (pts_t) ); s->u.p.pts = pts; for (inx=0; inxu.p.cnt; inx++) { s->u.p.pts[inx].pt.y = -s->u.p.pts[inx].pt.y; } /* Don't Free - we only just got! ALso can't free other copy as that may be a template */ //MyFree(pts); break; case SEG_JNTTRK: s->u.j.pos.y = - s->u.j.pos.y; s->u.j.angle = NormalizeAngle( 180.0 - s->u.j.angle ); s->u.j.negate = ! s->u.j.negate; break; case SEG_BEZTRK: case SEG_BEZLIN: s->u.b.pos[0].y = -s->u.b.pos[0].y; s->u.b.pos[1].y = -s->u.b.pos[1].y; s->u.b.pos[2].y = -s->u.b.pos[2].y; s->u.b.pos[3].y = -s->u.b.pos[3].y; FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK); break; } } } EXPORT void RescaleSegs( wIndex_t segCnt, trkSeg_p segs, DIST_T scale_x, DIST_T scale_y, DIST_T scale_w ) { trkSeg_p s; int inx; for (s=segs; s<&segs[segCnt]; s++) { if (s->width>0) s->width *= scale_w; switch (s->type) { case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: case SEG_STRTRK: s->u.l.pos[0].y *= scale_y; s->u.l.pos[0].x *= scale_x; s->u.l.pos[1].x *= scale_x; s->u.l.pos[1].y *= scale_y; break; case SEG_CRVTRK: case SEG_CRVLIN: case SEG_FILCRCL: s->u.c.center.x *= scale_x; s->u.c.center.y *= scale_y; s->u.c.radius *= scale_w; break; case SEG_TEXT: s->u.t.pos.x *= scale_x; s->u.t.pos.y *= scale_y; s->u.t.fontSize *= scale_w; break; case SEG_POLY: case SEG_FILPOLY: for (inx=0; inxu.p.cnt; inx++) { s->u.p.pts[inx].pt.x *= scale_x; s->u.p.pts[inx].pt.y *= scale_y; } break; case SEG_JNTTRK: s->u.j.pos.x *= scale_x; s->u.j.pos.y *= scale_y; s->u.j.R *= scale_w; s->u.j.L *= scale_w; s->u.j.l0 *= scale_w; s->u.j.l1 *= scale_w; break; case SEG_BEZTRK: case SEG_BEZLIN: s->u.b.pos[0].y *= scale_y; s->u.b.pos[0].x *= scale_x; s->u.b.pos[1].x *= scale_x; s->u.b.pos[1].y *= scale_y; s->u.b.pos[2].y *= scale_y; s->u.b.pos[2].x *= scale_x; s->u.b.pos[3].x *= scale_x; s->u.b.pos[3].y *= scale_y; FixUpBezierSeg(s->u.b.pos,s,s->type == SEG_BEZTRK); break; } } } EXPORT void CloneFilledDraw( int segCnt, trkSeg_p segs, BOOL_T reorigin ) { pts_t * newPts; trkSeg_p sp; wIndex_t inx; for ( sp=segs; sp<&segs[segCnt]; sp++ ) { switch (sp->type) { case SEG_POLY: case SEG_FILPOLY: newPts = (pts_t*)MyMalloc( sp->u.p.cnt * sizeof (pts_t) ); memcpy( newPts, sp->u.p.pts, sp->u.p.cnt * sizeof (pts_t) ); if ( reorigin ) { for ( inx = 0; inxu.p.cnt; inx++ ) REORIGIN( newPts[inx].pt, sp->u.p.pts[inx].pt, sp->u.p.angle, sp->u.p.orig ); sp->u.p.angle = 0; sp->u.p.orig = zero; } //if (sp->u.p.pts) Can't do this a pts could be pointing at static // free(sp->u.p.pts); sp->u.p.pts = newPts; break; case SEG_TEXT: sp->u.t.string = MyStrdup( sp->u.t.string); break; case SEG_BEZTRK: case SEG_BEZLIN: sp->bezSegs.cnt = 0; //if (sp->bezSegs.ptr) MyFree(sp->bezSegs.ptr); Make sure no update to static sp->bezSegs.ptr = NULL; sp->bezSegs.max = 0; FixUpBezierSeg(sp->u.b.pos,sp,sp->type == SEG_BEZTRK); break; default: break; } } } EXPORT void FreeFilledDraw( int segCnt, trkSeg_p segs ) { trkSeg_p sp; for ( sp=segs; sp<&segs[segCnt]; sp++ ) { switch (sp->type) { case SEG_POLY: case SEG_FILPOLY: if ( sp->u.p.pts ) MyFree( sp->u.p.pts ); sp->u.p.pts = NULL; break; case SEG_TEXT: if (sp->u.t.string) MyFree(sp->u.t.string); sp->u.t.string = NULL; break; default: break; } } } /* * DistanceSegs * * Find the closest point on the Segs to the point pos. * Return the distance to the point, the point on the curve and the index of the segment that contains it. * */ EXPORT DIST_T DistanceSegs( coOrd orig, ANGLE_T angle, wIndex_t segCnt, trkSeg_p segPtr, coOrd * pos, wIndex_t * inx_ret ) { DIST_T d, dd = DIST_INF, ddd; coOrd p0, p1, p2, pt, lo, hi; BOOL_T found = FALSE; wIndex_t inx, lin; segProcData_t segProcData2; p0 = *pos; Rotate( &p0, orig, -angle ); p0.x -= orig.x; p0.y -= orig.y; d = dd; for ( inx=0; segCnt>0; segPtr++,segCnt--,inx++) { p1 = p0; switch (segPtr->type) { case SEG_STRTRK: case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: dd = LineDistance( &p1, segPtr->u.l.pos[0], segPtr->u.l.pos[1] ); if ( segPtr->type == SEG_BENCH ) { if ( dd < BenchGetWidth( segPtr->u.l.option )/2.0 ) dd = 0.0; } break; case SEG_CRVTRK: case SEG_CRVLIN: case SEG_FILCRCL: dd = CircleDistance( &p1, segPtr->u.c.center, fabs(segPtr->u.c.radius), segPtr->u.c.a0, segPtr->u.c.a1 ); break; case SEG_POLY: case SEG_FILPOLY: ddd = DIST_INF; for (lin=0;linu.p.cnt;lin++) { pt = p0; if (lin < segPtr->u.p.cnt-1 ) ddd = LineDistance( &pt, segPtr->u.p.pts[lin].pt, segPtr->u.p.pts[lin+1].pt ); else if (segPtr->u.p.polyType != POLYLINE) ddd = LineDistance( &pt, segPtr->u.p.pts[lin].pt, segPtr->u.p.pts[0].pt ); if ( ddd < dd ) { dd = ddd; p1 = pt; } } break; case SEG_BEZTRK: case SEG_BEZLIN: dd = DIST_INF; pt = p0; for (int i = 0;ibezSegs.cnt;i++) { segProcData2.distance.pos1 = pt; SegProc(SEGPROC_DISTANCE,&DYNARR_N(trkSeg_t,segPtr->bezSegs,i),&segProcData2); if (segProcData2.distance.ddu.t.pos, angle+segPtr->u.t.angle, segPtr->u.t.string, segPtr->u.t.fontSize, &lo, &hi );*/ GetTextBounds( zero, 0, segPtr->u.t.string, segPtr->u.t.fontSize, &lo, &hi ); //lo and hi are relative to seg origin p0.x -= segPtr->u.t.pos.x; p0.y -= segPtr->u.t.pos.y; Rotate( &p0, zero, -segPtr->u.t.angle ); if (p0.x > lo.x && p0.x < hi.x && p0.y >lo.y && p0.y < hi.y) { //Within rectangle - therefore small dist hi.x /= 2.0; hi.y /= 2.0; dd = 0.1*FindDistance(hi, p0)/FindDistance(lo,hi); // Proportion to mean that the closer we to the center or the smaller the target in overlapping cases, the more likely we pick it break; } hi.x /= 2.0; // rough center of rectangle hi.y /= 2.0; if (fabs((p0.x-hi.x)/hi.x) hi.x) dd = (p0.x - hi.x); else dd = fabs(p0.x-hi.x); } else { // Closer to y if (p0.y > hi.y) dd = (p0.y - hi.y); else dd = fabs(p0.y-hi.y); } /*printf( "dx=%0.4f dy=%0.4f dd=%0.3f\n", dx, dy, dd );*/ /* if ( p0.x >= lo.x && p0.x <= hi.x && p0.y >= lo.y && p0.y <= hi.y ) { p1.x = (lo.x+hi.x)/2.0; p1.y = (lo.y+hi.y)/2.0; dd = FindDistance( p0, p1 ); } */ break; case SEG_JNTTRK: dd = JointDistance( &p1, 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; default: dd = DIST_INF; } if (dd < d) { d = dd; p2 = p1; if (inx_ret) *inx_ret = inx; found = TRUE; } } if (found) { p2.x += orig.x; p2.y += orig.y; Rotate( &p2, orig, angle ); *pos = p2; } return d; } /* * Get the angle at a point on the segments closest to pos1 * Optionally return the index of the segment and the distance to that point * */ EXPORT ANGLE_T GetAngleSegs( wIndex_t segCnt, trkSeg_p segPtr, coOrd * pos1, // Now IN/OUT OUT = wIndex_t * segInxR, DIST_T * dist, BOOL_T * seg_backwards, wIndex_t * subSegInxR, BOOL_T * negative_radius) { wIndex_t inx; ANGLE_T angle = 0.0; coOrd p0,p1; DIST_T d, dd; segProcData_t segProcData; coOrd pos2 = * pos1; BOOL_T negative = FALSE; BOOL_T backwards = FALSE; if (subSegInxR) *subSegInxR = -1; d = DistanceSegs( zero, 0.0, segCnt, segPtr, &pos2, &inx ); // if (dist) * dist = d; segPtr += inx; segProcData.getAngle.pos = pos2; switch ( segPtr->type ) { case SEG_STRTRK: case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: StraightSegProc( SEGPROC_GETANGLE, segPtr, &segProcData ); angle = segProcData.getAngle.angle; break; case SEG_CRVTRK: case SEG_CRVLIN: case SEG_FILCRCL: CurveSegProc( SEGPROC_GETANGLE, segPtr, &segProcData ); angle = segProcData.getAngle.angle; negative = segProcData.getAngle.negative_radius; backwards = segProcData.getAngle.backwards; break; case SEG_JNTTRK: JointSegProc( SEGPROC_GETANGLE, segPtr, &segProcData ); angle = segProcData.getAngle.angle; break; case SEG_BEZTRK: case SEG_BEZLIN: BezierSegProc( SEGPROC_GETANGLE, segPtr, &segProcData ); angle = segProcData.getAngle.angle; negative = segProcData.getAngle.negative_radius; backwards = segProcData.getAngle.backwards; if (subSegInxR) *subSegInxR = segProcData.getAngle.bezSegInx; break; case SEG_POLY: case SEG_FILPOLY: p0 = p1 = pos2; dd = LineDistance( &p0, segPtr->u.p.pts[segPtr->u.p.cnt-1].pt, segPtr->u.p.pts[0].pt ); angle = FindAngle( segPtr->u.p.pts[segPtr->u.p.cnt-1].pt, segPtr->u.p.pts[0].pt ); for ( inx=0; inxu.p.cnt-1; inx++ ) { p0 = p1; d = LineDistance( &p0, segPtr->u.p.pts[inx].pt, segPtr->u.p.pts[inx+1].pt ); if ( d < dd ) { dd = d; angle = FindAngle( segPtr->u.p.pts[inx].pt, segPtr->u.p.pts[inx+1].pt ); if (subSegInxR) *subSegInxR = inx; pos2 = p0; } } break; case SEG_TEXT: angle = segPtr->u.t.angle; break; default: AbortProg( "GetAngleSegs(%d)", segPtr->type ); } if ( segInxR ) *segInxR = inx; if (seg_backwards) *seg_backwards = backwards; if (negative_radius) *negative_radius = negative; * pos1 = pos2; return angle; } /**************************************************************************** * * Color * ****************************************************************************/ typedef struct { FLOAT_T h, s, v; } hsv_t; static FLOAT_T max_s; static FLOAT_T max_v; static dynArr_t hsv_da; #define hsv(N) DYNARR_N( hsv_t, hsv_da, N ) static void Hsv2rgb( hsv_t hsv, long *rgb ) { int i; FLOAT_T f, w, q, t, r=0, g=0, b=0; if (hsv.s == 0.0) hsv.s = 0.000001; if (hsv.h == -1.0) { r = hsv.v; g = hsv.v; b = hsv.v; } else { if (hsv.h == 360.0) hsv.h = 0.0; hsv.h = hsv.h / 60.0; i = (int) hsv.h; f = hsv.h - i; w = hsv.v * (1.0 - hsv.s); q = hsv.v * (1.0 - (hsv.s * f)); t = hsv.v * (1.0 - (hsv.s * (1.0 - f))); switch (i) { case 0: r = hsv.v; g = t; b = w; break; case 1: r = q; g = hsv.v; b = w; break; case 2: r = w; g = hsv.v; b = t; break; case 3: r = w; g = q; b = hsv.v; break; case 4: r = t; g = w; b = hsv.v; break; case 5: r = hsv.v; g = w; b = q; break; } } *rgb = wRGB( (int)(r*255), (int)(g*255), (int)(b*255) ); } static void Rgb2hsv( long rgb, hsv_t *hsv ) { FLOAT_T r, g, b; FLOAT_T max, min, delta; r = ((rgb>>16)&0xFF)/255.0; g = ((rgb>>8)&0xFF)/255.0; b = ((rgb)&0xFF)/255.0; max = r; if (g > max) max = g; if (b > max) max = b; min = r; if (g < min) min = g; if (b < min) min = b; hsv->v = max; if (max != 0.0) hsv->s = (max - min) / max; else hsv->s = 0.0; if (hsv->s == 0.0) hsv->h = -1.0; else { delta = max - min; if (r == max) hsv->h = (g - b) / delta; else if (g == max) hsv->h = 2.0 + (b - r) / delta; else if (b == max) hsv->h = 4.0 + (r - g) / delta; hsv->h = hsv->h * 60.0; if (hsv->h < 0.0) hsv->h = hsv->h + 360; } } static void Fill_hsv( wIndex_t segCnt, trkSeg_p segPtr, hsv_t * hsvP ) { int inx; max_s = 0.0; max_v = 0.0; for ( inx=0; inx= 0 ) { if ( max_s < hsvP[inx].s ) max_s = hsvP[inx].s; if ( max_v < hsvP[inx].v ) max_v = hsvP[inx].v; } } } EXPORT void RecolorSegs( wIndex_t cnt, trkSeg_p segs, wDrawColor color ) { long rgb; wIndex_t inx; hsv_t hsv0; FLOAT_T h, s, v; DYNARR_SET( hsv_t, hsv_da, cnt ); Fill_hsv( cnt, segs, &hsv(0) ); rgb = wDrawGetRGB( color ); Rgb2hsv( rgb, &hsv0 ); h = hsv0.h; if ( max_s > 0.25 ) s = hsv0.s/max_s; else s = 1.0; if ( max_v > 0.25 ) v = hsv0.v/max_v; else v = 1.0; for ( inx=0; inxcolor = wDrawFindColor( rgb ); } } /**************************************************************************** * * Input/Output * ****************************************************************************/ static void AppendPath( signed char c ) { if (pathPtr == NULL) { pathMax = 100; pathPtr = (signed char*)MyMalloc( pathMax ); } else if (pathCnt >= pathMax) { pathMax += 100; pathPtr = (signed char*)MyRealloc( pathPtr, pathMax ); } pathPtr[pathCnt++] = c; } EXPORT BOOL_T ReadSegs( void ) { char *cp, *cpp; BOOL_T rc=TRUE; trkSeg_p s; trkEndPt_p e; long rgb; int i; DIST_T elev0, elev1; BOOL_T hasElev; BOOL_T isPolyV1, isPolyV2; BOOL_T improvedEnds; FLOAT_T ignoreFloat; char type; char * plain_text; long option, option2; BOOL_T subsegs = FALSE; descriptionOff = zero; tempSpecial[0] = '\0'; tempCustom[0] = '\0'; DYNARR_RESET( trkSeg_t, tempSegs_da ); DYNARR_RESET( trkEndPt_t, tempEndPts_da ); pathCnt = 0; AppendPath(0); // End of all paths while ( rc && ((cp = GetNextLine()) != NULL) ) { while (isspace(*cp)) cp++; hasElev = FALSE; improvedEnds = FALSE; if ( IsEND( END_SEGS ) ) { return TRUE; } if ( strncmp(cp, "SUBSEGS", 7) == 0) { subsegs = TRUE; continue; } if (strncmp (cp, "SUBSEND", 7) == 0) { subsegs = FALSE; continue; } if ( *cp == '\0' || *cp == '\n' || *cp == '#' ) { continue; } if (subsegs) continue; type = *cp++; improvedEnds = isPolyV2 = hasElev = isPolyV1 = FALSE; if ( isdigit( *cp ) ) { long iVersion = strtol( cp, &cp, 10 ); if ( iVersion >= 3 ) hasElev = isPolyV1 = TRUE; if ( iVersion >= 4 ) improvedEnds = isPolyV2 = TRUE; if ( iVersion > 4 ) { InputError( "Invalid segment version number, maximum is %d", TRUE, 4 ); break; } } switch (type) { case SEG_STRLIN: case SEG_TBLEDGE: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = type; if ( !GetArgs( cp, hasElev?"lwpfpf":"lwpYpY", &rgb, &s->width, &s->u.l.pos[0], &elev0, &s->u.l.pos[1], &elev1 ) ) { rc = FALSE; break; } s->u.l.option = 0; s->color = wDrawFindColor( rgb ); break; case SEG_DIMLIN: case SEG_BENCH: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = type; if ( !GetArgs( cp, hasElev?"lwpfpfl":"lwpYpYZ", &rgb, &s->width, &s->u.l.pos[0], &elev0, &s->u.l.pos[1], &elev1, &option ) ) { rc = FALSE; break; } if ( type == SEG_DIMLIN ) s->u.l.option = option; else s->u.l.option = BenchInputOption(option); s->color = wDrawFindColor( rgb ); break; case SEG_CRVLIN: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = SEG_CRVLIN; if ( !GetArgs( cp, hasElev?"lwfpfff":"lwfpYff", &rgb, &s->width, &s->u.c.radius, &s->u.c.center, &elev0, &s->u.c.a0, &s->u.c.a1 ) ) { rc = FALSE; break; } s->color = wDrawFindColor( rgb ); break; case SEG_STRTRK: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = SEG_STRTRK; s->bezSegs.max = 0; s->bezSegs.cnt = 0; s->bezSegs.ptr = NULL; if ( !GetArgs( cp, hasElev?"lwpfpf":"lwpYpY", &rgb, &s->width, &s->u.l.pos[0], &elev0, &s->u.l.pos[1], &elev1 ) ) { rc = FALSE; break; } s->color = wDrawFindColor( rgb ); break; case SEG_CRVTRK: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = SEG_CRVTRK; s->bezSegs.max = 0; s->bezSegs.cnt = 0; s->bezSegs.ptr = NULL; if ( !GetArgs( cp, hasElev?"lwfpfff":"lwfpYff", &rgb, &s->width, &s->u.c.radius, &s->u.c.center, &elev0, &s->u.c.a0, &s->u.c.a1 ) ) { rc = FALSE; break; } s->color = wDrawFindColor( rgb ); break; case SEG_JNTTRK: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = SEG_JNTTRK; if ( !GetArgs( cp, hasElev?"lwpffffffl":"lwpYfffffl", &rgb, &s->width, &s->u.j.pos, &elev0, &s->u.j.angle, &s->u.j.l0, &s->u.j.l1, &s->u.j.R, &s->u.j.L, &option ) ) { rc = FALSE; break; } s->u.j.negate = ( option&1 )!=0; s->u.j.flip = ( option&2 )!=0; s->u.j.Scurve = ( option&4 )!=0; s->color = wDrawFindColor( rgb ); break; case SEG_BEZTRK: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10); s = &tempSegs(tempSegs_da.cnt-1); s->type=SEG_BEZTRK; s->bezSegs.max=0; s->bezSegs.ptr= NULL; s->bezSegs.cnt=0; if ( !GetArgs( cp, "lwpppp", &rgb, &s->width, &s->u.b.pos[0], &s->u.b.pos[1], &s->u.b.pos[2], &s->u.b.pos[3])) { rc = FALSE; break; } s->color = wDrawFindColor( rgb ); break; case SEG_BEZLIN: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10); s = &tempSegs(tempSegs_da.cnt-1); s->type=SEG_BEZLIN; s->bezSegs.max=0; s->bezSegs.ptr= NULL; s->bezSegs.cnt=0; if ( !GetArgs( cp, "lwpppp", &rgb, &s->width, &s->u.b.pos[0], &s->u.b.pos[1], &s->u.b.pos[2], &s->u.b.pos[3])) { rc = FALSE; break; } s->color = wDrawFindColor( rgb ); break; case SEG_FILCRCL: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = SEG_FILCRCL; if ( !GetArgs( cp, hasElev?"lwfpf":"lwfpY", &rgb, &s->width, &s->u.c.radius, &s->u.c.center, &elev0 ) ) { rc = FALSE; /*??*/break; } s->u.c.a0 = 0.0; s->u.c.a1 = 360.0; s->color = wDrawFindColor( rgb ); break; case SEG_POLY: case SEG_FILPOLY: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = type; s->u.p.polyType = FREEFORM; if ( !GetArgs( cp, isPolyV2?"lwdd":"lwdX", &rgb, &s->width, &s->u.p.cnt, &s->u.p.polyType) ) { rc = FALSE; /*??*/break; } if (s->type == SEG_FILPOLY) s->u.p.polyType = FREEFORM; //Ensure closed if filled s->color = wDrawFindColor( rgb ); s->u.p.pts = (pts_t*)MyMalloc( s->u.p.cnt * sizeof (pts_t) ); for ( i=0; iu.p.cnt; i++ ) { cp = GetNextLine(); if (cp == NULL || !GetArgs( cp, // TODO: does elev belong here instead of the seg header? isPolyV2?"pdY":isPolyV1?"pXf":"pXY", &s->u.p.pts[i].pt, &s->u.p.pts[i].pt_type, &elev0 )) { rc = FALSE; } } s->u.p.angle = 0.0; s->u.p.orig = zero; break; case SEG_TEXT: DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); s = &tempSegs(tempSegs_da.cnt-1); s->type = type; s->u.t.fontP = NULL; if ( !GetArgs( cp, "lpfdfq", &rgb, &s->u.t.pos, &s->u.t.angle, &s->u.t.boxed, &s->u.t.fontSize, &plain_text ) ) { rc = FALSE; /*??*/break; } s->u.t.string = MyStrdup(plain_text); s->color = wDrawFindColor( rgb ); break; case SEG_UNCEP: case SEG_CONEP: DYNARR_APPEND( trkEndPt_t, tempEndPts_da, 10 ); e = &tempEndPts(tempEndPts_da.cnt-1); if (type == SEG_CONEP) { if ( !GetArgs( cp, "dc", &e->index, &cp ) ) { rc = FALSE; /*??*/break; } } else { e->index = -1; } if ( !GetArgs( cp, "pfc", &e->pos, &e->angle, &cp) ) { rc = FALSE; /*??*/break; } e->elev.option = 0; e->elev.u.height = 0.0; e->elev.doff = zero; e->option = 0; if (improvedEnds) { //E4 and T4 if (!GetArgs( cp, "lpc", &option, &e->elev.doff, &cp )) { rc = FALSE; /*??*/break; } switch (option&ELEV_MASK) { case ELEV_STATION: GetArgs( cp, "qc", &e->elev.u.name, &cp); break; default: GetArgs( cp, "fc", &e->elev.u.height, &cp); //First height } DIST_T height2; if (!GetArgs( cp, "flLlc", &height2, &option2, &e->elev.option, &e->option, &cp ) ) { rc = FALSE; break; } if (option2) e->elev.option |= ELEV_VISIBLE; GetArgs(cp, "fc", &ignoreFloat, &cp); break; } if ( cp != NULL ) { if (paramVersion < 7) { GetArgs( cp, "dfp", &e->elev.option, &e->elev.u.height, &e->elev.doff, &cp ); /*??*/break; } GetArgs( cp, "lpc", &option, &e->elev.doff, &cp ); e->option = option >> 8; e->elev.option = (int)(option&0xFF); if ( (e->elev.option&ELEV_MASK) != ELEV_NONE ) { switch (e->elev.option&ELEV_MASK) { case ELEV_DEF: GetArgs( cp, "fc", &e->elev.u.height, &cp ); break; case ELEV_STATION: GetArgs( cp, "qc", &e->elev.u.name, &cp ); /*??*/break; default: ; } } } break; case SEG_PATH: while (isspace(*cp)) cp++; if (*cp == '\"') cp++; pathCnt--; // Overwrite previous terminator while ( *cp != '\"') AppendPath((signed char)*cp++); // Name of path AppendPath(0); // End of name cp++; while (1) { i = (int)strtol(cp, &cpp, 10); if (cp == cpp) /*??*/break; cp = cpp; AppendPath( (signed char)i ); // Segment # of path component } AppendPath( 0 ); // End of last path components AppendPath( 0 ); // End of path AppendPath( 0 ); // End of all paths break; case SEG_SPEC: strncpy( tempSpecial, cp+1, sizeof tempSpecial - 1 ); break; case SEG_CUST: strncpy( tempCustom, cp+1, sizeof tempCustom - 1 ); break; case SEG_DOFF: if ( !GetArgs( cp, "ff", &descriptionOff.x, &descriptionOff.y ) ) { rc = FALSE; /*??*/break; } break; default: InputError( "Unknown segment type: %c", TRUE, type ); rc = FALSE; break; } } // We've hit an InputError. // The user may have chosen to not continue (paramFile == NULL) // Otherwise we have to flush until we see END$SEGS while ( GetNextLine() && !IsEND( END_SEGS ) ); // Chomp, Chomp return FALSE; } EXPORT BOOL_T WriteSegs( FILE * f, wIndex_t segCnt, trkSeg_p segs ) { return WriteSegsEnd(f,segCnt,segs,TRUE); } EXPORT BOOL_T WriteSegsEnd( FILE * f, wIndex_t segCnt, trkSeg_p segs, BOOL_T writeEnd) { int i, j; BOOL_T rc = TRUE; long option; char * escaped_text; for ( i=0; i 0; break; case SEG_STRLIN: case SEG_TBLEDGE: rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f 0 %0.6f %0.6f 0\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y ) > 0; break; case SEG_DIMLIN: rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f 0 %0.6f %0.6f 0 %ld\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y, segs[i].u.l.option ) > 0; break; case SEG_BENCH: rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f 0 %0.6f %0.6f 0 %ld\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y, BenchOutputOption(segs[i].u.l.option) ) > 0; break; case SEG_CRVTRK: rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.c.radius, segs[i].u.c.center.x, segs[i].u.c.center.y, segs[i].u.c.a0, segs[i].u.c.a1 ) > 0; break; case SEG_JNTTRK: option = (segs[i].u.j.negate?1:0) + (segs[i].u.j.flip?2:0) + (segs[i].u.j.Scurve?4:0); rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %ld\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.j.pos.x, segs[i].u.j.pos.y, segs[i].u.j.angle, segs[i].u.j.l0, segs[i].u.j.l1, segs[i].u.j.R, segs[i].u.j.L, option )>0; break; case SEG_BEZTRK: case SEG_BEZLIN: rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f %0.6f\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.l.pos[0].x, segs[i].u.l.pos[0].y, segs[i].u.l.pos[1].x, segs[i].u.l.pos[1].y, segs[i].u.l.pos[2].x, segs[i].u.l.pos[2].y, segs[i].u.l.pos[3].x, segs[i].u.l.pos[3].y ) > 0; rc &= fprintf(f,"\tSUBSEGS\n"); rc &= WriteSegsEnd(f,segs[i].bezSegs.cnt,segs[i].bezSegs.ptr,FALSE); rc &= fprintf(f,"\tSUBSEND\n"); break; case SEG_CRVLIN: rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %0.6f 0 %0.6f %0.6f\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.c.radius, segs[i].u.c.center.x, segs[i].u.c.center.y, segs[i].u.c.a0, segs[i].u.c.a1 ) > 0; break; case SEG_FILCRCL: rc &= fprintf( f, "\t%c3 %ld %0.6f %0.6f %0.6f %0.6f 0\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.c.radius, segs[i].u.c.center.x, segs[i].u.c.center.y ) > 0; break; case SEG_POLY: case SEG_FILPOLY: // TODO: to be consistent, we should add a dummy 0 for elev. See ReadSegs/SEG_POLY rc &= fprintf( f, "\t%c4 %ld %0.6f %d %d \n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].width, segs[i].u.p.cnt, segs[i].u.p.polyType ) > 0; for ( j=0; j 0; break; case SEG_TEXT: /* 0pf0fq */ escaped_text = ConvertToEscapedText(segs[i].u.t.string); rc &= fprintf( f, "\t%c %ld %0.6f %0.6f %0.6f %d %0.6f \"%s\"\n", segs[i].type, wDrawGetRGB(segs[i].color), segs[i].u.t.pos.x, segs[i].u.t.pos.y, segs[i].u.t.angle, segs[i].u.t.boxed, segs[i].u.t.fontSize, escaped_text ) > 0; MyFree(escaped_text); break; } } if (writeEnd) rc &= fprintf( f, "\t%s\n", END_SEGS )>0; return rc; } EXPORT void SegProc( segProc_e cmd, trkSeg_p segPtr, segProcData_p data ) { switch (segPtr->type) { case SEG_STRTRK: case SEG_STRLIN: StraightSegProc( cmd, segPtr, data ); break; case SEG_CRVTRK: case SEG_CRVLIN: CurveSegProc( cmd, segPtr, data ); break; case SEG_JNTTRK: JointSegProc( cmd, segPtr, data ); break; case SEG_BEZTRK: case SEG_BEZLIN: BezierSegProc( cmd, segPtr, data); break; default: AbortProg( "SegProg( %d )", segPtr->type ); break; } } /* * Draw Segs */ EXPORT void DrawDimLine( drawCmd_p d, coOrd p0, coOrd p1, char * dimP, wFontSize_t fs, FLOAT_T middle, wDrawWidth width, wDrawColor color, long option ) { ANGLE_T a0, a1; wFont_p fp; coOrd size, p, pc; DIST_T dist, dist1, fx, fy; POS_T x, y; coOrd textsize; if ( middle < 0.0 ) middle = 0.0; if ( middle > 1.0 ) middle = 1.0; a0 = FindAngle( p0, p1 ); dist = fs/144.0; if ( ( option & 0x10 ) == 0 ) { Translate( &p, p0, a0-45, dist ); DrawLine( d, p0, p, width, color ); Translate( &p, p0, a0+45, dist ); DrawLine( d, p0, p, width, color ); } if ( ( option & 0x20 ) == 0 ) { Translate( &p, p1, a0-135, dist ); DrawLine( d, p1, p, width, color ); Translate( &p, p1, a0+135, dist ); DrawLine( d, p1, p, width, color ); } if ( fs < 2*d->scale ) { DrawLine( d, p0, p1, 0, color ); return; } fp = wStandardFont( (option&0x01)?F_TIMES:F_HELV, FALSE, FALSE ); dist = FindDistance( p0, p1 ); DrawTextSize( &mainD, dimP, fp, fs, TRUE, &textsize ); size.x = textsize.x/2.0; size.y = textsize.y/2.0; dist1 = FindDistance( zero, size ); if ( dist <= dist1*1.5 ) { DrawLine( d, p0, p1, width, color ); coOrd s_pos; s_pos.x = (p1.x-p0.x)*middle+p0.x; s_pos.y = (p1.y-p0.y)*middle+p0.y; ANGLE_T a = FindAngle(p0,p1); Translate(&s_pos,s_pos,a+90,textsize.y/2); DrawString( d, s_pos, 0.0, dimP, fp, fs, color ); return; } a1 = FindAngle( zero, size ); p.x = p0.x+(p1.x-p0.x)*middle; p.y = p0.y+(p1.y-p0.y)*middle; pc = p; p.x -= size.x; p.y -= size.y; fx = fy = 1; if (a0>180) { a0 = a0-180; fx = fy = -1; } if (a0>90) { a0 = 180-a0; fy *= -1; } if (a0>a1) { x = size.x; y = x * tan(D2R(90-a0)); } else { y = size.y; x = y * tan(D2R(a0)); } DrawString( d, p, 0.0, dimP, fp, fs, color ); p = pc; p.x -= fx*x; p.y -= fy*y; DrawLine( d, p0, p, width, color ); p = pc; p.x += fx*x; p.y += fy*y; DrawLine( d, p, p1, width, color ); } /* * Display the array of segments. * Note that Bezier segments in particular contain sub-arrays of Curve and Straight segments. */ EXPORT void DrawSegsO( drawCmd_p d, track_p trk, coOrd orig, ANGLE_T angle, trkSeg_p segPtr, wIndex_t segCnt, DIST_T trackGauge, wDrawColor color, long options ) { wIndex_t i, j; coOrd p0, p1, p2, p3, c; ANGLE_T a0; wDrawColor color1, color2; DIST_T factor = d->dpi/d->scale; trkSeg_p tempPtr; long option; wFontSize_t fs; wBool_t bFill,bThick; for (i=0; icolor; color2 = wDrawColorBlack; } else { color1 = color2 = color; } wDrawWidth thick = 3; #ifdef WINDOWS thick *= (wDrawWidth)(d->dpi/75.0); #endif switch (segPtr->type) { case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: case SEG_STRTRK: REORIGIN( p0, segPtr->u.l.pos[0], angle, orig ) REORIGIN( p1, segPtr->u.l.pos[1], angle, orig ) switch (segPtr->type) { case SEG_STRTRK: if (color1 == wDrawColorBlack) color1 = normalColor; if ( segPtr->color == wDrawColorWhite ) break; if (options&DTS_CENTERONLY) { DrawLine( d, p0, p1, thick, color1 ); break; } DrawStraightTrack( d, p0, p1, FindAngle(p1, p0 ), trk, color1, options ); break; case SEG_STRLIN:; wDrawWidth w; if (segPtr->width <0) w = (int)floor(fabs(segPtr->width)+0.5); else w = (int)floor(fabs(segPtr->width*factor)+0.5); DrawLine( d, p0, p1, (d->options&DC_THICK)?thick:w, color1 ); break; case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: if ( (d->options&DC_SEGTRACK) ) { DYNARR_APPEND( trkSeg_t, tempSegs_da, 10 ); tempPtr = &tempSegs(tempSegs_da.cnt-1); memcpy( tempPtr, segPtr, sizeof segPtr[0] ); tempPtr->u.l.pos[0] = p0; tempPtr->u.l.pos[1] = p1; } else { switch ( segPtr->type ) { case SEG_DIMLIN: fs = descriptionFontSize*4; option = segPtr->u.l.option; fs /= (option==0?8:option==1?4:option==2?2:1); if ( fs < 2 ) fs = 2; DrawDimLine( d, p0, p1, FormatDistance(FindDistance(p0,p1)), fs, 0.5, (d->options&DC_THICK)?thick:0, color, option & 0x00 ); break; case SEG_BENCH: DrawBench( d, p0, p1, color1, color2, options, segPtr->u.l.option ); break; case SEG_TBLEDGE: DrawLine( d, p0, p1, (wDrawWidth)floor(3.0/BASE_DPI*d->dpi+0.5) , color ); break; } } break; } break; case SEG_CRVLIN: case SEG_CRVTRK: a0 = NormalizeAngle(segPtr->u.c.a0 + angle); REORIGIN( c, segPtr->u.c.center, angle, orig ); if (segPtr->type == SEG_CRVTRK) { if (color1 == wDrawColorBlack) color1 = normalColor; if ( segPtr->color == wDrawColorWhite ) break; p0.x = p0.y = p1.x = p1.y = 0; if (options&DTS_CENTERONLY) { DrawArc( d, c, fabs(segPtr->u.c.radius), a0, segPtr->u.c.a1, FALSE, thick, color1 ); break; } DrawCurvedTrack( d, c, fabs(segPtr->u.c.radius), a0, segPtr->u.c.a1, trk, color1, options ); } else { wDrawWidth w; if (segPtr->width <0) w = (int)floor(fabs(segPtr->width)+0.5); else w = (int)floor(segPtr->width*factor+0.5); DrawArc( d, c, fabs(segPtr->u.c.radius), a0, segPtr->u.c.a1, FALSE, (d->options&DC_THICK)?thick:w, color1 ); } break; case SEG_BEZTRK: case SEG_BEZLIN: if (segPtr->type == SEG_BEZTRK) { if (color1 == wDrawColorBlack) color1 = normalColor; if ( segPtr->color == wDrawColorWhite ) break; } //else { REORIGIN(p0,segPtr->u.b.pos[0],angle,orig); REORIGIN(p1,segPtr->u.b.pos[1],angle,orig); REORIGIN(p2,segPtr->u.b.pos[2],angle,orig); REORIGIN(p3,segPtr->u.b.pos[3],angle,orig); //} for(int j=0;jbezSegs.cnt;j++) { //Loop through sub Segs tempPtr = &DYNARR_N(trkSeg_t,segPtr->bezSegs,j); switch (tempPtr->type) { case SEG_CRVTRK: case SEG_CRVLIN: a0 = NormalizeAngle(tempPtr->u.c.a0 + angle); REORIGIN( c, tempPtr->u.c.center, angle, orig ); if (tempPtr->type == SEG_CRVTRK) { if (color1 == wDrawColorBlack) color1 = normalColor; if ( tempPtr->color == wDrawColorWhite ) break; p0.x = p0.y = p1.x = p1.y = 0; if (options&DTS_CENTERONLY) { DrawArc( d, c, fabs(segPtr->u.c.radius), a0, segPtr->u.c.a1, FALSE, thick, color1 ); break; } DrawCurvedTrack( d, c, fabs(tempPtr->u.c.radius), a0, tempPtr->u.c.a1, trk, color1, options ); } else if (tempPtr->type == SEG_CRVLIN) { wDrawWidth w; if (tempPtr->width <0) w = (int)floor(fabs(tempPtr->width)+0.5); else w = (int)floor(tempPtr->width*factor+0.5); DrawArc( d, c, fabs(tempPtr->u.c.radius), a0, tempPtr->u.c.a1, FALSE, (d->options&DC_THICK)?thick:w, color1 ); } break; case SEG_STRTRK: if (color1 == wDrawColorBlack) color1 = normalColor; if ( tempPtr->color == wDrawColorWhite ) break; REORIGIN(p0,tempPtr->u.l.pos[0], angle, orig); REORIGIN(p1,tempPtr->u.l.pos[1], angle, orig); if (options&DTS_CENTERONLY) { DrawLine( d, p0, p1, thick, color1 ); break; } DrawStraightTrack( d, p0, p1, FindAngle(p1,p0), trk,color1,options); break; case SEG_STRLIN: REORIGIN(p0,tempPtr->u.l.pos[0], angle, orig); REORIGIN(p1,tempPtr->u.l.pos[1], angle, orig); wDrawWidth w; if (tempPtr->width <0) w = (int)floor(fabs(tempPtr->width)+0.5); else w = (int)floor(tempPtr->width*factor+0.5); DrawLine( d, p0, p1, (d->options&DC_THICK)?thick:w, color1 ); break; } } break; case SEG_JNTTRK: REORIGIN( p0, segPtr->u.j.pos, angle, orig ); DrawJointTrack( d, p0, NormalizeAngle(segPtr->u.j.angle+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, NULL, -1, -1, trackGauge, color1, options ); break; case SEG_TEXT: REORIGIN( p0, segPtr->u.t.pos, angle, orig ) DrawMultiString( d, p0, segPtr->u.t.string, segPtr->u.t.fontP, segPtr->u.t.fontSize, color1, NormalizeAngle(angle + segPtr->u.t.angle), NULL, NULL, segPtr->u.t.boxed ); break; case SEG_FILPOLY: case SEG_POLY: ; /* Note: if we call tempSegDrawFillPoly we get a nasty bug /+ because we don't make a private copy of p.pts */ coOrd *tempPts = malloc(sizeof(coOrd)*segPtr->u.p.cnt); int *tempTypes = malloc(sizeof(int)*segPtr->u.p.cnt); // coOrd tempPts[segPtr->u.p.cnt]; for (j=0;ju.p.cnt;j++) { REORIGIN( tempPts[j], segPtr->u.p.pts[j].pt, angle, orig ); tempTypes[j] = segPtr->u.p.pts[j].pt_type; } bFill = (segPtr->type == SEG_FILPOLY); if ( (d->options&DC_SIMPLE) && programMode != MODE_TRAIN ) bFill = FALSE; // If we are drawing highlights for Select, don't fill just edges bThick = d->options&DC_THICK; if (&tempD == d && ( color == wDrawColorPreviewSelected || color == wDrawColorPreviewUnselected || color == selectedColor)) { bFill = FALSE; bThick = TRUE; } wDrawWidth w; if (segPtr->width <0) w = (int)floor(fabs(segPtr->width)+0.5); else w = (int)floor(segPtr->width*factor+0.5); drawFill_e eOptFill; if ( bFill ) eOptFill = DRAW_FILL; else if ( segPtr->u.p.polyType == POLYLINE ) eOptFill = DRAW_OPEN; else eOptFill = DRAW_CLOSED; DrawPoly( d, segPtr->u.p.cnt, tempPts, tempTypes, color1, bThick?thick:w, eOptFill ); free(tempPts); free(tempTypes); break; case SEG_FILCRCL: REORIGIN( c, segPtr->u.c.center, angle, orig ) bFill = TRUE; if ( (d->options&DC_SIMPLE) && programMode != MODE_TRAIN ) bFill = FALSE; // If we are drawing highlights for Select, don't fill just edges bThick = d->options&DC_THICK; if (&tempD == d && (color == wDrawColorPreviewSelected || color == wDrawColorPreviewUnselected || color == selectedColor)) { bFill = FALSE; bThick = TRUE; } if ( bFill ) { DrawFillCircle( d, c, fabs(segPtr->u.c.radius), color1 ); } else { DrawArc( d, c, fabs(segPtr->u.c.radius), 0, 360, FALSE, bThick?thick:(wDrawWidth)0, color1 ); } break; } } } /* * Draw Segments without setting DTS_ options. */ EXPORT void DrawSegs( drawCmd_p d, coOrd orig, ANGLE_T angle, trkSeg_p segPtr, wIndex_t segCnt, DIST_T trackGauge, wDrawColor color ) { DrawSegsO( d, NULL, orig, angle, segPtr, segCnt, trackGauge, color, DTS_LEFT|DTS_RIGHT ); } /* * Free dynamic storage added to each of an array of Track Segments. */ EXPORT void CleanSegs(dynArr_t * seg_p) { if (seg_p->cnt ==0) return; for (int i=0;icnt;i++) { trkSeg_t t = DYNARR_N(trkSeg_t,* seg_p,i); if (t.type == SEG_BEZLIN || t.type == SEG_BEZTRK) { if (t.bezSegs.ptr) MyFree(t.bezSegs.ptr); t.bezSegs.cnt = 0; t.bezSegs.max = 0; t.bezSegs.ptr = NULL; } if (t.type == SEG_POLY || t.type == SEG_FILPOLY) { if (t.u.p.pts) MyFree(t.u.p.pts); t.u.p.cnt = 0; t.u.p.pts = NULL; } } seg_p->cnt = 0; if (seg_p->ptr) MyFree(seg_p->ptr); seg_p->ptr = NULL; seg_p->max = 0; } /* * Copy Segs from one array to another */ EXPORT void AppendSegsToArray(dynArr_t * seg_to, dynArr_t * seg_from) { if (seg_from->cnt ==0) return; int j = 0; DYNARR_APPEND(trkSeg_t, * seg_to, seg_from->cnt); for (int i=0; icnt;i++,j++) { trkSeg_p from_p = &DYNARR_N(trkSeg_t, * seg_from,j); trkSeg_p to_p = &DYNARR_N(trkSeg_t, * seg_to,i); memcpy(to_p,from_p,sizeof( trkSeg_t)); if (from_p->type == SEG_BEZLIN || from_p->type == SEG_BEZTRK) { if (from_p->bezSegs.ptr) { to_p->bezSegs.ptr = memdup(from_p->bezSegs.ptr,from_p->bezSegs.cnt*sizeof(trkSeg_t)); } } if (from_p->type == SEG_POLY || from_p->type == SEG_FILPOLY) { if (from_p->u.p.pts) { to_p->u.p.pts = memdup(from_p->u.p.pts,from_p->u.p.cnt*sizeof(pts_t)); } } } } EXPORT void AppendTransformedSegs(dynArr_t * seg_to, dynArr_t * seg_from, coOrd orig, coOrd rotateOrig, ANGLE_T angle) { if (seg_from->cnt ==0) return; int j = 0; DYNARR_APPEND(trkSeg_t, * seg_to, seg_from->cnt); for (int i=0; icnt;i++,j++) { trkSeg_p from_p = &DYNARR_N(trkSeg_t, * seg_from,j); trkSeg_p to_p = &DYNARR_N(trkSeg_t, * seg_to,i); memcpy(to_p,from_p,sizeof( trkSeg_t)); if (from_p->type == SEG_BEZLIN || from_p->type == SEG_BEZTRK) { if (from_p->bezSegs.ptr) { to_p->bezSegs.ptr = memdup(from_p->bezSegs.ptr,from_p->bezSegs.cnt*sizeof(trkSeg_t)); } } if (from_p->type == SEG_POLY || from_p->type == SEG_FILPOLY) { if (from_p->u.p.pts) { to_p->u.p.pts = memdup(from_p->u.p.pts,from_p->u.p.cnt*sizeof(pts_t)); } } RotateSegs(1,to_p,rotateOrig,angle); coOrd move; move.x = orig.x - rotateOrig.x; move.y = orig.y - rotateOrig.y; MoveSegs(1,to_p,move); } } EXPORT void CopyPoly(trkSeg_p p, wIndex_t segCnt) { pts_t * newPts; for (int i=0;itype == SEG_POLY) || (p->type == SEG_FILPOLY)) { newPts = memdup( p->u.p.pts, p->u.p.cnt*sizeof (pts_t) ); p->u.p.pts = newPts; } if ( p->type == SEG_TEXT ) { p->u.t.string = MyStrdup( p->u.t.string ); } } } EXPORT wBool_t CompareSegs( trkSeg_p segPtr1, int segCnt1, trkSeg_p segPtr2, int segCnt2 ) { char * cp = message+strlen(message); if ( segCnt1 != segCnt2 ) { sprintf( cp, "SegCnt %d %d\n", segCnt1, segCnt2 ); return FALSE; } char * cq = cp-2;; for ( int segInx = 0; segInx < segCnt1; segInx++ ) { cp = cq; sprintf( cp, "SEG:%d - ", segInx ); cp += strlen( cp ); trkSeg_p segP1 = &segPtr1[segInx]; trkSeg_p segP2 = &segPtr2[segInx]; REGRESS_CHECK_INT( "Type", segP1, segP2, type ); REGRESS_CHECK_COLOR( "Color", segP1, segP2, color ); switch( segP1->type ) { case SEG_DIMLIN: case SEG_TBLEDGE: case SEG_BENCH: // These don't have widths break; default: REGRESS_CHECK_WIDTH( "Width", segP1, segP2, width ); } switch( segP1->type ) { case SEG_DIMLIN: case SEG_TBLEDGE: // These don't have color break; default: REGRESS_CHECK_COLOR( "Color", segP1, segP2, color ); } switch( segP1->type ) { case SEG_STRTRK: case SEG_STRLIN: case SEG_DIMLIN: case SEG_BENCH: case SEG_TBLEDGE: REGRESS_CHECK_POS( "Pos[0]", segP1, segP2, u.l.pos[0] ) REGRESS_CHECK_POS( "Pos[1]", segP1, segP2, u.l.pos[1] ) // REGRESS_CHECK_POS( "Pos[2]", segP1, segP2, u.l.pos[2] ) // REGRESS_CHECK_POS( "Pos[3]", segP1, segP2, u.l.pos[3] ) REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.l.angle ) REGRESS_CHECK_INT( "Option", segP1, segP2, u.l.option ) break; case SEG_BEZTRK: case SEG_BEZLIN: break; // u.b case SEG_CRVLIN: case SEG_CRVTRK: case SEG_FILCRCL: REGRESS_CHECK_POS( "Center", segP1, segP2, u.c.center ) REGRESS_CHECK_ANGLE( "A0", segP1, segP2, u.c.a0 ) REGRESS_CHECK_ANGLE( "A1", segP1, segP2, u.c.a1 ) REGRESS_CHECK_DIST( "Radius", segP1, segP2, u.c.radius ) break; // u.c case SEG_JNTTRK: REGRESS_CHECK_POS( "Pos", segP1, segP2, u.j.pos ) REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.j.angle ) REGRESS_CHECK_DIST( "R", segP1, segP2, u.j.R ) REGRESS_CHECK_DIST( "L", segP1, segP2, u.j.L ) REGRESS_CHECK_DIST( "L0", segP1, segP2, u.j.l0 ) REGRESS_CHECK_DIST( "L1", segP1, segP2, u.j.l1 ); REGRESS_CHECK_INT( "Flip", segP1, segP2, u.j.flip ) REGRESS_CHECK_INT( "Negate", segP1, segP2, u.j.negate ) REGRESS_CHECK_INT( "Scurve", segP1, segP2, u.j.Scurve ) break; // u.j case SEG_TEXT: REGRESS_CHECK_POS( "Pos", segP1, segP2, u.t.pos ) REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.t.angle ) // CHECK fontP REGRESS_CHECK_DIST( "Fontsize", segP1, segP2, u.t.fontSize ) REGRESS_CHECK_INT( "Boxed", segP1, segP2, u.t.boxed ) // CHECK string break; // u.t case SEG_POLY: case SEG_FILPOLY: REGRESS_CHECK_INT( "Cnt", segP1, segP2, u.p.cnt ) // CHECK pts REGRESS_CHECK_POS( "Orig", segP1, segP2, u.p.orig ) REGRESS_CHECK_ANGLE( "Angle", segP1, segP2, u.p.angle ) break; // u.p // EndPts case SEG_UNCEP: case SEG_CONEP: break; // Turnout/Struct case SEG_PATH: case SEG_SPEC: case SEG_CUST: case SEG_DOFF: break; default: break; } } return TRUE; }