summaryrefslogtreecommitdiff
path: root/app/bin/turnout.c
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2024-11-14 19:35:45 +0100
committerJörg Frings-Fürst <debian@jff-webhosting.net>2024-11-14 19:35:45 +0100
commitdf5520aa2dae5b3ce7abf8733dcdd152898af163 (patch)
tree00d3047bfb14f682bfb5a21010c731ed649bfed7 /app/bin/turnout.c
parentdf247efec654e512242e4f4f1b0212034f9e01fe (diff)
parentec3c0f6f6e7153fa797dc57a0e95779cbc63a23b (diff)
Merge branch 'release/debian/1_5.3.0GA-1'debian/1_5.3.0GA-1
Diffstat (limited to 'app/bin/turnout.c')
-rw-r--r--app/bin/turnout.c2157
1 files changed, 2157 insertions, 0 deletions
diff --git a/app/bin/turnout.c b/app/bin/turnout.c
new file mode 100644
index 0000000..57c398b
--- /dev/null
+++ b/app/bin/turnout.c
@@ -0,0 +1,2157 @@
+/** \file turnout.c
+ * Turnout drawing
+ */
+
+/* XTrkCad - Model Railroad CAD
+ * Copyright (C) 2005 Dave Bullis
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "common.h"
+#include "common-ui.h"
+#include "compound.h"
+#include "custom.h"
+#include "draw.h"
+#include "track.h"
+
+/* Draw turnout data */
+
+/**
+ * The types of turnouts that get enhanced drawing methods
+ */
+enum dtoType {
+ DTO_INVALID,
+ DTO_NORMAL,
+ DTO_THREE,
+ DTO_WYE,
+
+ DTO_CURVED,
+
+ DTO_XING,
+ DTO_XNG9,
+ DTO_SSLIP,
+ DTO_DSLIP,
+
+ DTO_LCROSS,
+ DTO_RCROSS,
+ DTO_DCROSS
+};
+
+// Define to plot control points (DTO_NORMAL, DTO_CURVED, DTO_XING, DTO_LCROSS)
+// #define DTO_DEBUG DTO_XING
+
+#define DTO_DIM 4 // Maximum number of paths
+#define DTO_SEGS 24 // Maximum number of control points
+
+static struct DrawToData_t {
+ TRKINX_T index;
+ enum dtoType toType;
+ track_p trk;
+ tieData_t td;
+ int bridge;
+ int roadbed;
+ int endCnt;
+ int pathCnt;
+ int routeCnt;
+ int strCnt;
+ int crvCnt;
+ int rgtCnt;
+ int lftCnt;
+ int strPath;
+ int str2Path;
+ int crvPath;
+ int crv2Path;
+ int origCnt;
+ int origins[DTO_DIM];
+ coOrd midPt;
+ struct extraDataCompound_t* xx;
+} dtod;
+
+struct DrawTo_t {
+ int n;
+ trkSeg_p trkSeg[DTO_SEGS];
+ coOrd base[DTO_SEGS];
+ coOrd baseLast;
+ DIST_T dy[DTO_SEGS];
+ ANGLE_T angle;
+ ANGLE_T crvAngle;
+ coOrd pts[DTO_SEGS];
+ coOrd ptsLast;
+ char type;
+};
+
+static struct DrawTo_t dto[DTO_DIM];
+
+
+
+/****************************************
+ *
+ * Draw Turnout Roadbed
+ *
+ */
+
+int roadbedOnScreen = 0;
+
+
+void DrawTurnoutRoadbedSide(drawCmd_p d, wDrawColor color, coOrd orig,
+ ANGLE_T angle, trkSeg_p sp, ANGLE_T side, int first, int last)
+{
+ segProcData_t data;
+ if (last <= first) {
+ return;
+ }
+ data.drawRoadbedSide.first = first;
+ data.drawRoadbedSide.last = last;
+ data.drawRoadbedSide.side = side;
+ data.drawRoadbedSide.roadbedWidth = roadbedWidth;
+ data.drawRoadbedSide.rbw = (wDrawWidth)floor(roadbedLineWidth *
+ (d->dpi / d->scale) + 0.5);
+ data.drawRoadbedSide.orig = orig;
+ data.drawRoadbedSide.angle = angle;
+ data.drawRoadbedSide.color = color;
+ data.drawRoadbedSide.d = d;
+ SegProc(SEGPROC_DRAWROADBEDSIDE, sp, &data);
+}
+
+
+static void ComputeAndDrawTurnoutRoadbedSide(
+ drawCmd_p d,
+ wDrawColor color,
+ coOrd orig,
+ ANGLE_T angle,
+ trkSeg_p segPtr,
+ int segCnt,
+ int segInx,
+ ANGLE_T side)
+{
+ unsigned long res, res1;
+ int b0, b1;
+ res = ComputeTurnoutRoadbedSide(segPtr, segCnt, segInx, side, roadbedWidth);
+ if (res == 0L) {
+ } else if (res == 0xFFFFFFFF) {
+ DrawTurnoutRoadbedSide(d, color, orig, angle, &segPtr[segInx], side, 0, 32);
+ } else {
+ for (b0 = 0, res1 = 0x00000001; res1 && (res1 & res); b0++, res1 <<= 1);
+ for (b1 = 32, res1 = 0x80000000; res1 && (res1 & res); b1--, res1 >>= 1);
+ DrawTurnoutRoadbedSide(d, color, orig, angle, &segPtr[segInx], side, 0, b0);
+ DrawTurnoutRoadbedSide(d, color, orig, angle, &segPtr[segInx], side, b1, 32);
+ }
+}
+
+
+static void DrawTurnoutRoadbed(
+ drawCmd_p d,
+ wDrawColor color,
+ coOrd orig,
+ ANGLE_T angle,
+ trkSeg_p segPtr,
+ int segCnt)
+{
+ int inx, trkCnt = 0, segInx = 0;
+ for (inx = 0; inx < segCnt; inx++) {
+ if (IsSegTrack(&segPtr[inx])) {
+ segInx = inx;
+ trkCnt++;
+ if (trkCnt > 1) {
+ break;
+ }
+ }
+ }
+ if (trkCnt == 0) {
+ return;
+ }
+ if (trkCnt == 1) {
+ DrawTurnoutRoadbedSide(d, color, orig, angle, &segPtr[segInx], +90, 0, 32);
+ DrawTurnoutRoadbedSide(d, color, orig, angle, &segPtr[segInx], -90, 0, 32);
+ } else {
+ for (inx = 0; inx < segCnt; inx++) {
+ if (IsSegTrack(&segPtr[inx])) {
+ ComputeAndDrawTurnoutRoadbedSide(d, color, orig, angle, segPtr, segCnt, inx,
+ +90);
+ ComputeAndDrawTurnoutRoadbedSide(d, color, orig, angle, segPtr, segCnt, inx,
+ -90);
+ }
+ }
+ }
+}
+
+
+/****************************************
+ *
+ * TURNOUT DRAWING
+ *
+ */
+
+/**
+* Get the paths from the turnout definition. Puts the results into static dto structure.
+* Curved segments are broken up into short sections of the lesser of 5 degrees or 5 * tie spacing.
+*
+* \param trk track_p pointer to a track
+* \param xx pointer to the extraDataCompound struct
+*
+* \returns the number of paths
+*/
+int GetTurnoutPaths(track_p trk, struct extraDataCompound_t* xx)
+{
+ wIndex_t segInx;
+ wIndex_t segEP;
+
+ int i;
+ ANGLE_T a0, a1, aa0, aa1;
+ DIST_T r, len;
+ coOrd p0, p1;
+
+ PATHPTR_T pp;
+
+ dtod.trk = trk;
+ dtod.index = GetTrkIndex( trk );
+ dtod.xx = xx;
+
+ dtod.td = GetTrkTieData( trk );
+
+ int pathCnt = 0, routeCnt = 0;
+
+ for (i = 0; i < DTO_DIM; i++) {
+ dto[i].n = 0;
+ }
+
+ // Validate that the first segment starts at (0, 0)
+ // and if STR p1.y == 0, if CRV angle == 0 or angle == 180
+ GetSegInxEP(1, &segInx, &segEP);
+ trkSeg_p segPtr = &xx->segs[segInx];
+ switch (segPtr->type) {
+ case SEG_STRTRK:
+ p0 = segPtr->u.l.pos[0];
+ p1 = segPtr->u.l.pos[1];
+ if ((FindDistance(p0, zero) > EPSILON) || (fabs(p1.y) > EPSILON)) {
+ return -1;
+ }
+ break;
+ case SEG_CRVTRK:
+ r = fabs(segPtr->u.c.radius);
+ a0 = segPtr->u.c.a0;
+ a1 = segPtr->u.c.a1;
+
+ if (segPtr->u.c.radius > 0) {
+ aa0 = a0;
+ } else {
+ aa0 = a0 + a1;
+ }
+ PointOnCircle(&p0, segPtr->u.c.center, r, aa0);
+ if ((FindDistance(p0, zero) > EPSILON)
+ || ((fabs(aa0 - 180) > EPSILON) && (fabs(aa0) > EPSILON))) {
+ return -1;
+ }
+ break;
+ }
+
+ pp = GetPaths(trk);
+ while (pp[0]) {
+ pp += strlen((char*)pp) + 1;
+
+ ANGLE_T angle = 0;
+ while (pp[0]) {
+ if (pathCnt < DTO_DIM) {
+ dto[pathCnt].type = 'S';
+ while (pp[0]) {
+ GetSegInxEP(pp[0], &segInx, &segEP);
+ // trkSeg_p
+ segPtr = &xx->segs[segInx];
+ switch (segPtr->type) {
+ case SEG_STRTRK:
+ p0 = segPtr->u.l.pos[0];
+ p1 = segPtr->u.l.pos[1];
+
+ wIndex_t n = dto[pathCnt].n;
+ dto[pathCnt].trkSeg[n] = segPtr;
+ dto[pathCnt].base[n] = p0;
+ n++;
+ dto[pathCnt].trkSeg[n] = segPtr;
+ dto[pathCnt].base[n] = p1;
+ // n++;
+ dto[pathCnt].n = n;
+
+ if (n >= DTO_SEGS - 1) { return -1; }
+
+ break;
+ case SEG_CRVTRK:
+ r = fabs(segPtr->u.c.radius);
+
+ dto[pathCnt].type = segPtr->u.c.center.y < 0 ? 'R' : 'L';
+
+ a0 = segPtr->u.c.a0;
+ a1 = segPtr->u.c.a1;
+
+ angle += a1;
+
+ len = D2R(a1) * r;
+ // Every 5 degrees or 5 * tie spacing
+ int cnt = (int)floor(a1 / 5.0);
+ int cnt2 = (int)floor(len / 5 / dtod.td.spacing);
+ if (cnt2 > cnt) { cnt = cnt2; }
+ if (cnt <= 0) { cnt = 1; }
+
+ aa1 = a1 / cnt;
+ if (dto[pathCnt].type == 'R') {
+ aa0 = a0;
+ } else {
+ aa0 = a0 + a1;
+ aa1 = -aa1;
+ }
+ PointOnCircle(&p0, segPtr->u.c.center, r, aa0);
+ n = dto[pathCnt].n;
+ dto[pathCnt].trkSeg[n] = segPtr;
+ dto[pathCnt].base[n] = p0;
+ n++;
+ dto[pathCnt].n = n;
+
+ while (cnt > 0) {
+ aa0 += aa1;
+ PointOnCircle(&p0, segPtr->u.c.center, r, aa0);
+
+ // n = dto[pathCnt].n;
+ dto[pathCnt].trkSeg[n] = segPtr;
+ dto[pathCnt].base[n] = p0;
+ n++;
+
+ if (n >= DTO_SEGS - 1) { return -1; }
+
+ cnt--;
+ }
+ n--; // remove that last point count
+ dto[pathCnt].n = n;
+ }
+ pp++;
+ }
+ // Include the last point
+ dto[pathCnt].crvAngle = angle;
+ dto[pathCnt].n++;
+ }
+
+ pathCnt++;
+ if (pathCnt > DTO_DIM) { return -1; }
+ pp++;
+ }
+ routeCnt++;
+ pp++;
+ }
+ dtod.pathCnt = pathCnt;
+ dtod.routeCnt = routeCnt;
+ dtod.endCnt = GetTrkEndPtCnt( trk );
+
+ // Guard value: n < DTO_SEGS - 2
+ for (i = 0; i < pathCnt; i++) {
+ dto[i].pts[dto[i].n].x = DIST_INF;
+ }
+
+ return pathCnt;
+}
+
+/**
+* Sets the turnout type if compatible with enhanced drawing methods. The data is
+* from the path data saved in dtod and dto by GetTurnoutPaths. The turnout type is
+* stored in the dtod.toType. DTO_INVALID (0) if the enhanced methods cannot handle
+* it.
+*/
+void GetTurnoutType()
+{
+ dtod.strPath = -1;
+ dtod.str2Path = -1;
+ dtod.crvPath = -1;
+ dtod.crv2Path = -1;
+
+ dtod.toType = DTO_INVALID;
+
+ int strCnt = 0, crvCnt = 0, lftCnt = 0, rgtCnt = 0;
+// enum dtoType toType = DTO_INVALID;
+ int i, j;
+
+ // Count path origins
+ dtod.origCnt = 1;
+ dtod.origins[0] = 0;
+
+ for (i = 1; i < dtod.pathCnt; i++) {
+ int eq = 0;
+ for (j = 0; j < i; j++) {
+ if (CoOrdEqual(dto[dtod.origins[j]].base[0], dto[i].base[0])) {
+ eq++;
+ break;
+ }
+ }
+ if (eq == 0) {
+ if (dtod.origCnt < DTO_DIM) {
+ dtod.origins[dtod.origCnt] = i;
+ }
+ dtod.origCnt++;
+ }
+
+ if (dtod.origCnt >= DTO_DIM) {
+ return;
+ }
+ }
+
+ // Determine the path type
+ for (i = 0; i < dtod.pathCnt; i++) {
+ switch (dto[i].type) {
+ case 'S':
+ strCnt++;
+ if (strCnt == 1) {
+ dtod.strPath = i;
+ } else {
+ dtod.str2Path = i;
+ }
+ break;
+ case 'L':
+ lftCnt++;
+ crvCnt++;
+ if (crvCnt == 1) {
+ dtod.crvPath = i;
+ } else {
+ dtod.crv2Path = i;
+ }
+ break;
+ case 'R':
+ rgtCnt++;
+ crvCnt++;
+ if (crvCnt == 1) {
+ dtod.crvPath = i;
+ } else {
+ dtod.crv2Path = i;
+ }
+ break;
+ }
+ }
+
+ dtod.strCnt = strCnt;
+ dtod.crvCnt = crvCnt;
+ dtod.lftCnt = lftCnt;
+ dtod.rgtCnt = rgtCnt;
+
+ // Normal two- or three-way turnout, or a curved turnout
+ if (dtod.origCnt == 1) {
+ if (dtod.pathCnt == 2) {
+ if (strCnt == 1 && crvCnt == 1) {
+ dtod.toType = DTO_NORMAL;
+ } else if ((strCnt == 0) && ((lftCnt == 2) || (rgtCnt == 2))) {
+ // Assumes outer curve is [0] and inner is [1]
+ if ((dto[0].crvAngle <= 20) && (dto[1].crvAngle - dto[0].crvAngle <= 15)) {
+ dtod.toType = DTO_CURVED;
+ }
+ } else if (lftCnt == 1 && rgtCnt == 1) {
+ dtod.toType = DTO_WYE;
+ }
+ } else if ((dtod.pathCnt == 3) && (strCnt == 1)
+ && (lftCnt == 1) && (rgtCnt == 1)) {
+ dtod.toType = DTO_THREE;
+ }
+ } else
+ // Crossing, single- and double-slip
+ if ((dtod.origCnt == 2) && (dtod.endCnt == 4)
+ && strCnt == 2) {
+
+ ANGLE_T a0, a1, a2;
+ a1 = FindAngle(dto[dtod.strPath].base[0], dto[dtod.strPath].base[1]);
+ a2 = FindAngle(dto[dtod.str2Path].base[0], dto[dtod.str2Path].base[1]);
+ // Swap the ends of the strPath if large angle
+ if((a1 > 180.0) && (dto[dtod.strPath].n == 2)) {
+ coOrd tmp = dto[dtod.strPath].base[0];
+ dto[dtod.strPath].base[0] = dto[dtod.strPath].base[1];
+ dto[dtod.strPath].base[1] = tmp;
+
+ i = dto[dtod.strPath].n - 1;
+ tmp = dto[dtod.strPath].pts[0];
+ dto[dtod.strPath].pts[0] = dto[dtod.strPath].pts[i];
+ dto[dtod.strPath].pts[i] = tmp;
+
+ a1 = a1 - 180.0;
+ dto[dtod.strPath].angle = a1;
+ }
+ // Swap the ends of the str2Path if large angle
+ if((a2 > 180.0) && (dto[dtod.str2Path].n == 2)) {
+ coOrd tmp = dto[dtod.str2Path].base[0];
+ dto[dtod.str2Path].base[0] = dto[dtod.str2Path].base[1];
+ dto[dtod.str2Path].base[1] = tmp;
+
+ i = dto[dtod.str2Path].n - 1;
+ tmp = dto[dtod.str2Path].pts[0];
+ dto[dtod.str2Path].pts[0] = dto[dtod.str2Path].pts[i];
+ dto[dtod.str2Path].pts[i] = tmp;
+
+ a2 = a2 - 180.0;
+ dto[dtod.str2Path].angle = a2;
+ }
+ a0 = DifferenceBetweenAngles(a1, a2);
+ if(a0 < 0) {
+ int tmp = dtod.strPath;
+ dtod.strPath = dtod.str2Path;
+ dtod.str2Path = tmp;
+ a0 = NormalizeAngle(-a0);
+ }
+ if ((a0 > 90.0) || (a0 < 0.0)) {
+ return;
+ }
+
+ coOrd p1 = dto[dtod.strPath].base[0];
+ coOrd p2 = dto[dtod.str2Path].base[0];
+ coOrd pos = zero;
+ int intersect = FindIntersection(&pos, p1, a1, p2, a2);
+
+ if (intersect) {
+ if(strCnt == 2 && dtod.pathCnt == 2) {
+ if((a0 <= 61) && (a0 >= -61)) {
+ dtod.toType = DTO_XING;
+ } else {
+ dtod.toType = DTO_XNG9;
+ }
+ } else if(dtod.pathCnt == 3 && (lftCnt == 1 || rgtCnt == 1)) {
+ dtod.toType = DTO_SSLIP;
+ } else if(dtod.pathCnt == 4 && lftCnt == 1 && rgtCnt == 1) {
+ dtod.toType = DTO_DSLIP;
+ }
+ }
+ // No intersect, it could be a crossover
+ else if (strCnt == 2) {
+ if (dtod.pathCnt == 4 && lftCnt == 1 && rgtCnt == 1) {
+ dtod.toType = DTO_DCROSS;
+ } else if(dtod.pathCnt == 3) {
+ // Perverse test because the cross paths go Left then Right, for example
+ if(lftCnt == 1) {
+ dtod.toType = DTO_RCROSS;
+ } else if(rgtCnt == 1) {
+ dtod.toType = DTO_LCROSS;
+ } else {
+ dtod.toType = DTO_INVALID;
+ }
+ }
+ }
+ }
+}
+
+#if 0
+/**
+ * Draw Layout lines and points
+ *
+ * \param d The drawing object
+ * \param scaleInx The layout/track scale index
+ */
+static void DrawDtoLayout(
+ drawCmd_p d,
+ SCALEINX_T scaleInx
+)
+{
+#ifdef DTO_DEBUG
+ // Draw the points and lines from dto
+ double r = dtod.td->width / 2;
+ // if (r < 1) r = 1;
+
+ int i, j;
+ for (i = 0; i < DTO_DIM; i++) {
+ for (j = 0; j < dto[i].n; j++) {
+ DrawFillCircle(d, dto[i].pts[j], r, drawColorPurple);
+ if (j < dto[i].n - 1) {
+ DrawLine(d, dto[i].pts[j], dto[i].pts[j + 1], 0, drawColorPurple);
+ }
+ }
+ }
+#endif
+}
+#endif
+
+/**
+* Use the coOrds to build a polygon and draw the bridge fill. Note that the coordinates are
+* passed as pairs, and rearranged into a polygon with the 1,2,4,3 order.
+*
+* \param d The drawing object
+* \param c The color
+* \param b1 The first coordinate
+* \param b2 The second coordinate
+* \param b3 The third coordinate
+* \param b4 The fourth coordinate
+*/
+static void DrawFill(
+ drawCmd_p d,
+ wDrawColor c,
+ coOrd b1,
+ coOrd b2,
+ coOrd b3,
+ coOrd b4
+)
+{
+ coOrd p[4] = {b1, b2, b4, b3};
+ DrawPoly(d,4,p,NULL,c,0,DRAW_FILL );
+}
+
+/**
+* Draw Bridge parapets and background or Roadbed for a turnout
+*
+* \param d The drawing object
+* \param fillType 0 for bridge, 1 for roadbed
+* \param path1 The first path
+* \param path2 The second path
+*/
+static void DrawTurnoutFill(
+ drawCmd_p d,
+ int fillType,
+ int path1,
+ int path2
+)
+{
+ DIST_T trackGauge = GetTrkGauge(dtod.trk);
+ wDrawWidth width2 = (wDrawWidth)round((2.0 * d->dpi) / BASE_DPI);
+ if (d->options&DC_PRINT) {
+ width2 = (wDrawWidth)round(d->dpi / BASE_DPI);
+ }
+
+ wDrawColor color = (fillType==0?bridgeColor:roadbedColor);
+ double fillWidth = 1.5;
+ coOrd b1,b2,b3,b4,b5,b6;
+ ANGLE_T angle = dtod.xx->angle,a = 0.0;
+ int i,j,i1,i2;
+ i1 = path1;
+ i2 = path2;
+ if(dto[i1].base[dto[i1].n - 1].y < dto[i2].base[dto[i2].n - 1].y) {
+ i1 = path2;
+ i2 = path1;
+ // a = -a;
+ }
+
+ if(dtod.toType == DTO_THREE) {
+ i = dtod.strPath;
+ DIST_T dy = fabs(dto[i].dy[0]) + trackGauge * fillWidth;
+ b1 = dto[i].pts[0];
+ Translate(&b3,b1,(angle + a),dy);
+ b1 = dto[i].pts[dto[i].n - 1];
+ Translate(&b4,b1,(angle + a),dy);
+ b2 = dto[i].pts[0];
+ Translate(&b5,b2,(angle + a),-dy);
+ b2 = dto[i].pts[dto[i].n - 1];
+ Translate(&b6,b2,(angle + a),-dy);
+
+ // Draw the background
+ DrawFill(d,color,b3,b4,b5,b6);
+ }
+
+ for(i = i1; 1; i = i2,a = 180.0) {
+ DIST_T dy = fabs(dto[i].dy[0]) + trackGauge * fillWidth;
+ b1 = dto[i].pts[0];
+ Translate(&b3,b1,(angle + a),dy);
+ Translate(&b5,b1,(angle + a),-(dy * 0.75));
+ for(j = 1; j < dto[i].n; j++) {
+ dy = fabs(dto[i].dy[j]) + trackGauge * fillWidth;
+ b2 = dto[i].pts[j];
+ Translate(&b4,b2,(angle + a),dy);
+ Translate(&b6,b2,(angle + a),-(dy * 0.75));
+
+ // Draw the background
+ DrawFill(d,color,b3,b4,b5,b6);
+
+ // Draw the bridge edge
+ if(fillType==0) {
+ DrawLine( d,b3,b4,width2,drawColorBlack );
+ }
+
+ b1 = b2;
+ b3 = b4;
+ b5 = b6;
+ }
+
+ if(i == i2) {
+ break;
+ }
+ }
+
+ EPINX_T ep;
+ coOrd p;
+ track_p trk1;
+ coOrd p0,p1;
+
+ // Bridge parapet ends
+ if(fillType==0) {
+ for(ep = 0; ep < 3; ep++) {
+ trk1 = GetTrkEndTrk(dtod.trk,ep);
+
+ if((trk1) && (!GetTrkBridge(trk1))) {
+
+ p = GetTrkEndPos(dtod.trk,ep);
+ a = GetTrkEndAngle(dtod.trk,ep) + 90.0;
+
+ int i = (dtod.lftCnt > 0) && (dtod.rgtCnt == 0) ? 2 : 1;
+ if(ep != i) {
+ Translate(&p0,p,a,trackGauge * 1.5);
+ Translate(&p1,p0,a - 45.0,trackGauge * 1.5);
+ DrawLine(d,p0,p1,width2,drawColorBlack);
+ }
+ if(ep != (3 - i)) {
+ Translate(&p0,p,a,-trackGauge * 1.5);
+ Translate(&p1,p0,a + 45.0,-trackGauge * 1.5);
+ DrawLine(d,p0,p1,width2,drawColorBlack);
+ }
+ }
+ }
+ }
+}
+
+/**
+* Draw Bridge parapets and background or Roadbed for a cross-over
+*
+* \param d The drawing object
+* \param fillType 0 for bridge, 1 for roadbed
+* \param path1 The first path, straight
+* \param path2 The second path, straight
+*/
+static void DrawCrossFill(
+ drawCmd_p d,
+ int fillType,
+ int path1,
+ int path2
+)
+{
+ DIST_T trackGauge = GetTrkGauge(dtod.trk);
+ wDrawWidth width2 = (wDrawWidth)round((2.0 * d->dpi)/BASE_DPI);
+ if (d->options&DC_PRINT) {
+ width2 = (wDrawWidth)round(d->dpi / BASE_DPI);
+ }
+
+ wDrawColor color = (fillType==0?bridgeColor:roadbedColor);
+ double fillWidth = 1.5;
+ coOrd b1, b2, b3, b4, b5, b6;
+ ANGLE_T angle = dtod.xx->angle, a = 0.0;
+ int i1, i2;
+ i1 = path1;
+ i2 = path2;
+ if(dto[i1].base[dto[i1].n - 1].y < dto[i2].base[dto[i2].n - 1].y) {
+ i1 = path2;
+ i2 = path1;
+ // a = -a;
+ }
+
+ DIST_T dy = fabs(dto[i1].dy[0]) + trackGauge * fillWidth;
+ b1 = dto[i1].pts[0];
+ Translate(&b3,b1,(angle + a),dy);
+ b1 = dto[i1].pts[dto[i1].n-1];
+ Translate(&b4,b1,(angle + a),dy);
+ b2 = dto[i2].pts[0];
+ Translate(&b5,b2,(angle + a),-dy);
+ b2 = dto[i2].pts[dto[i2].n-1];
+ Translate(&b6,b2,(angle + a),-dy);
+
+ // Draw the background
+ DrawFill(d, color, b3, b4, b5, b6);
+
+ // Draw the bridge edges
+ if(fillType == 0) {
+ DrawLine(d,b3,b4,width2,drawColorBlack);
+ DrawLine(d,b5,b6,width2,drawColorBlack);
+ }
+
+ EPINX_T ep;
+ coOrd p;
+ track_p trk1;
+ coOrd p0,p1;
+
+ // Bridge parapet ends
+ if(fillType==0) {
+ for(ep = 0; ep < 4; ep++) {
+ trk1 = GetTrkEndTrk(dtod.trk,ep);
+
+ if((trk1) && (!GetTrkBridge(trk1))) {
+ p = GetTrkEndPos(dtod.trk,ep);
+ a = GetTrkEndAngle(dtod.trk,ep) + 90.0;
+
+ if((ep == 1) || (ep == 2)) {
+ Translate(&p0,p,a,trackGauge * 1.5);
+ Translate(&p1,p0,a - 45.0,trackGauge * 1.5);
+ DrawLine(d,p0,p1,width2,drawColorBlack);
+ }
+ if((ep == 0) || (ep == 3)) {
+ Translate(&p0,p,a,-trackGauge * 1.5);
+ Translate(&p1,p0,a + 45.0,-trackGauge * 1.5);
+ DrawLine(d,p0,p1,width2,drawColorBlack);
+ }
+ }
+ }
+ }
+}
+
+/**
+* Draw Bridge parapets and background or Roadbed for a crossing
+*
+* \param d The drawing object
+* \param fillType 0 for bridge, 1 for roadbed
+* \param path1 The first path
+* \param path2 The second path
+*/
+static void DrawXingFill(
+ drawCmd_p d,
+ int fillType,
+ int path1,
+ int path2
+)
+{
+ DIST_T trackGauge = GetTrkGauge(dtod.trk);
+ wDrawWidth width2 = (wDrawWidth)round((2.0 * d->dpi)/BASE_DPI);
+ if (d->options&DC_PRINT) {
+ width2 = (wDrawWidth)round(d->dpi / BASE_DPI);
+ }
+
+ double fillWidth = 1.5;
+ coOrd b0, b1, b2, b3, b4, b5, b6;
+ int i, j, i1, i2;
+ i1 = dtod.strPath;
+ i2 = dtod.str2Path;
+
+ // Fill both straight sections
+ wDrawWidth width3 = (wDrawWidth)round(trackGauge * 2 * fillWidth * d->dpi /
+ d->scale);
+ wDrawColor color = (fillType==0?bridgeColor:roadbedColor);
+ b1 = dto[i1].pts[0];
+ b2 = dto[i1].pts[dto[i1].n-1];
+ DrawLine(d,b1,b2,width3,color);
+ b1 = dto[i2].pts[0];
+ b2 = dto[i2].pts[dto[i1].n-1];
+ DrawLine(d,b1,b2,width3,color);
+
+ i1 = path1;
+ i2 = path2;
+ if(dto[i1].base[dto[i1].n - 1].y < dto[i2].base[dto[i2].n - 1].y) {
+ i1 = path2;
+ i2 = path1;
+ }
+
+ // Handle curved sections for slips
+ BOOL_T hasLeft = 0, hasRgt = 0;
+ ANGLE_T angle = dtod.xx->angle, a = 0.0;
+ for(i = i1; 1; i = i2,a = 180.0) {
+ DIST_T dy = fabs(dto[i].dy[0]) + trackGauge * fillWidth;
+ b1 = dto[i].pts[0];
+ Translate(&b3,b1,(angle + a),dy);
+ Translate(&b5,b1,(angle + a),-(dy * 0.75));
+ if(dto[i].type != 'S') {
+ if(dto[i].type == 'L') {
+ hasLeft = 1;
+ } else if(dto[i].type == 'R') {
+ hasRgt = 1;
+ }
+ for(j = 1; j < dto[i].n; j++) {
+ dy = fabs(dto[i].dy[j]) + trackGauge * fillWidth;
+ b2 = dto[i].pts[j];
+ Translate(&b4,b2,(angle + a),dy);
+ Translate(&b6,b2,(angle + a),-(dy * 0.75));
+
+ // Draw the background
+ DrawFill(d,color,b3,b4,b5,b6);
+
+ // Draw the bridge edge
+ if(fillType==0) {
+ DrawLine( d,b3,b4,width2,drawColorBlack );
+ }
+ b1 = b2;
+ b3 = b4;
+ b5 = b6;
+ }
+ }
+ if(i == i2) {
+ break;
+ }
+ }
+
+ if(dtod.strPath >= 0 && dtod.str2Path >= 0) {
+ i1 = dtod.strPath;
+ i2 = dtod.str2Path;
+ if(!hasRgt&&fillType==0) {
+ DIST_T dy = trackGauge * 1.5;
+ ANGLE_T a1, a2;
+ b1 = dto[i1].pts[0];
+ a1 = dto[i1].angle + 90;
+ Translate(&b3,b1,a1,dy);
+
+ b2 = dto[i2].pts[dto[i2].n - 1];
+ a2 = dto[i2].angle + 90;
+ Translate(&b4,b2,a2,dy);
+
+ FindIntersection(&b0, b3, a1-90.0, b4, a2-90.0);
+
+ // Draw the bridge edge
+ DrawLine(d,b3,b0,width2,drawColorBlack);
+ DrawLine(d,b0,b4,width2,drawColorBlack);
+ }
+
+ if(!hasLeft&&fillType==0) {
+ DIST_T dy = trackGauge * 1.5;
+ ANGLE_T a1, a2;
+ b1 = dto[i2].pts[0];
+ a1 = dto[i2].angle - 90;
+ Translate(&b3,b1,a1,dy);
+
+ b2 = dto[i1].pts[dto[i1].n - 1];
+ a2 = dto[i1].angle - 90;
+ Translate(&b4,b2,a2,dy);
+
+ FindIntersection(&b0, b3, a1+90.0, b4, a2+90.0);
+
+ // Draw the bridge edge
+ DrawLine(d,b3,b0,width2,drawColorBlack);
+ DrawLine(d,b0,b4,width2,drawColorBlack);
+ }
+
+ if(dtod.toType == DTO_XNG9 && fillType==0) {
+ DIST_T dy = trackGauge * 1.5;
+ ANGLE_T a1, a2;
+ b1 = dto[i1].pts[dto[i1].n - 1];
+ a1 = dto[i1].angle + 90;
+ Translate(&b3,b1,a1,dy);
+
+ b2 = dto[i2].pts[dto[i2].n - 1];
+ a2 = dto[i2].angle - 90;
+ Translate(&b4,b2,a2,dy);
+
+ FindIntersection(&b0, b3, a1-90.0, b4, a2+90.0);
+
+ // Draw the bridge edge
+ DrawLine(d,b3,b0,width2,drawColorBlack);
+ DrawLine(d,b0,b4,width2,drawColorBlack);
+
+ b1 = dto[i1].pts[0];
+ a1 = dto[i1].angle - 90;
+ Translate(&b3,b1,a1,dy);
+
+ b2 = dto[i2].pts[0];
+ a2 = dto[i2].angle + 90;
+ Translate(&b4,b2,a2,dy);
+
+ FindIntersection(&b0, b3, a1+90.0, b4, a2-90.0);
+
+ // Draw the bridge edge
+ DrawLine(d,b3,b0,width2,drawColorBlack);
+ DrawLine(d,b0,b4,width2,drawColorBlack);
+ }
+ }
+
+ // Bridge wings
+ EPINX_T ep;
+ coOrd p;
+ track_p trk1;
+ coOrd p0,p1;
+
+ if(fillType==0) {
+ for(ep = 0; ep < 4; ep++) {
+ trk1 = GetTrkEndTrk(dtod.trk,ep);
+
+ if((trk1) && (!GetTrkBridge(trk1))) {
+ p = GetTrkEndPos(dtod.trk,ep);
+ a = GetTrkEndAngle(dtod.trk,ep) + 90.0;
+
+ if((dtod.toType == DTO_XNG9) || (ep == 2) || (ep == 3)) {
+ Translate(&p0,p,a,trackGauge * 1.5);
+ Translate(&p1,p0,a - 45.0,trackGauge * 1.5);
+ DrawLine(d,p0,p1,width2,drawColorBlack);
+ }
+ if((dtod.toType == DTO_XNG9) || (ep == 0) || (ep == 1)) {
+ Translate(&p0,p,a,-trackGauge * 1.5);
+ Translate(&p1,p0,a + 45.0,-trackGauge * 1.5);
+ DrawLine(d,p0,p1,width2,drawColorBlack);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Init Normal Turnout data structure
+ * Calculates the dy value of each segment
+ * Sets pts values REORIGIN base to actual position and angle
+ * Save often used last base and last point coOrd
+ */
+static void DrawDtoInit()
+{
+ struct extraDataCompound_t* xx = dtod.xx;
+ coOrd p1;
+ int i, j;
+
+ for(i = 0; i < DTO_DIM; i++) {
+ int n = dto[i].n;
+ for(j = 0; j < n; j++) {
+ REORIGIN(p1,dto[i].base[j],xx->angle,xx->orig);
+ dto[i].pts[j] = p1;
+ if(j < n - 1) {
+ dto[i].dy[j] = (dto[i].base[j + 1].y - dto[i].base[j].y) /
+ (dto[i].base[j + 1].x - dto[i].base[j].x);
+ }
+ }
+ dto[i].ptsLast = dto[i].pts[n - 1];
+ dto[i].baseLast = dto[i].base[n - 1];
+ }
+}
+
+/**
+ * Draw Normal (Single Origin) Turnout Bridge and Ties. Uses the static dto and dtod structures.
+ *
+ * \param d The drawing object
+ * \param scaleInx The layout/track scale index
+ * \param color The tie color. If black the color is read from the global tieColor.
+ */
+static void DrawNormalTurnout(
+ drawCmd_p d,
+ SCALEINX_T scaleInx,
+ BOOL_T omitTies,
+ wDrawColor color)
+{
+ DIST_T len;
+ coOrd pos;
+ int cnt;
+ ANGLE_T angle;
+ coOrd s1, s2, p1, p2, q1, q2;
+ int p0, q0;
+// int s0;
+ ANGLE_T a0;
+
+ if (color == wDrawColorBlack) {
+ color = tieColor;
+ }
+
+// DIST_T trackGauge = GetTrkGauge(dtod.trk);
+
+ DrawDtoInit();
+
+ // draw the points
+#ifdef DTO_DEBUG
+ if (DTO_DEBUG == DTO_NORMAL) { DrawDtoLayout(d, scaleInx); }
+#endif
+
+ int strPath = dtod.strPath, othPath = 0, secPath = 1;
+ int toType = dtod.toType;
+// int first = 1;
+
+ switch (toType) {
+ case DTO_NORMAL:
+ othPath = 1 - strPath;
+ secPath = strPath;
+ break;
+ case DTO_WYE:
+ // strPath = 2;
+ othPath = 0; secPath = 1;
+ break;
+ case DTO_THREE:
+ switch (strPath) {
+ case 0:
+ othPath = 1; secPath = 2;
+ break;
+ case 1:
+ othPath = 0; secPath = 2;
+ break;
+ case 2:
+ othPath = 0; secPath = 1;
+ break;
+ }
+ break;
+ }
+
+ if(dtod.bridge) {
+ DrawTurnoutFill(d,0,othPath,secPath);
+ } else if(dtod.roadbed) {
+ DrawTurnoutFill( d,1,othPath,secPath );
+ }
+ if (omitTies) {
+ return;
+ }
+
+ // Straight vector for tie angle
+ if (toType == DTO_WYE) {
+ s1 = dto[othPath].pts[0];
+ s2 = MidPtCoOrd(dto[othPath].ptsLast, dto[secPath].ptsLast);
+ } else {
+ s1 = dto[strPath].pts[0];
+ s2 = dto[strPath].ptsLast;
+ }
+ // Diverging vector(s)
+ p1 = dto[othPath].pts[0];
+ p2 = dto[othPath].ptsLast;
+ q1 = dto[secPath].pts[0];
+ q2 = dto[secPath].ptsLast;
+
+ len = FindDistance(s1, s2);
+ angle = FindAngle(s1, s2); // The straight segment
+
+ cnt = (int)floor(len / dtod.td.spacing + 0.5);
+ if (cnt > 0) {
+ int pn = dto[othPath].n;
+ int qn = dto[secPath].n;
+ DIST_T dx = len / cnt;
+ /*s0 =*/ p0 = q0 = 0;
+ DIST_T tdlen = dtod.td.length;
+ DIST_T tdmax = (toType == DTO_WYE) ? 2.0 * tdlen : 2.5 * tdlen;
+ DIST_T tdwid = dtod.td.width;
+ DIST_T px = len, dlenx = dx / 2;
+
+ cnt = cnt > 1 ? cnt - 1 : 1;
+ for (px = dlenx; cnt; cnt--, px += dx) {
+ if (px >= dto[othPath].base[p0 + 1].x) { p0++; }
+ if (px >= dto[secPath].base[q0 + 1].x) { q0++; }
+ if (p0 >= pn || q0 >= qn) {
+ break;
+ }
+
+ if ((px + dx >= dto[othPath].baseLast.x)
+ || (px + dx >= dto[secPath].baseLast.x)) {
+ break;
+ }
+
+ DIST_T dy1 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ DIST_T dy2 = dto[secPath].base[q0].y + (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ tdlen = dtod.td.length + fabs(dy1) + fabs(dy2);
+ if (tdlen > tdmax) {
+ break;
+ }
+
+ DIST_T dy = dy1 + dy2;
+ Translate(&pos, s1, angle, px);
+ Translate(&pos, pos, (angle - 90.0), dy / 2);
+
+ DrawTie(d, pos, angle, tdlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+
+ // Asymmetric? Use longer ties for remaining two tracks (strPath, othPath)
+ DIST_T sx = px; // Save these values for second code block
+ int s0 = p0;
+ if((dtod.toType == DTO_THREE) && (px + dx >= dto[secPath].baseLast.x)) {
+ for ( ; cnt; cnt--, px += dx) {
+ if (px >= dto[othPath].base[p0 + 1].x) { p0++; }
+ // if (px >= dto[secPath].base[q0 + 1].x) q0++;
+ if (p0 >= pn) {
+ break;
+ }
+
+ if (px + dx >= dto[othPath].baseLast.x) {
+ break;
+ }
+
+ DIST_T dy1 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ tdlen = dtod.td.length + fabs(dy1);
+ if (tdlen > tdmax) {
+ break;
+ }
+
+ DIST_T dy = dy1;
+ Translate(&pos, s1, angle, px);
+ Translate(&pos, pos, (angle - 90.0), dy / 2);
+
+ DrawTie(d, pos, angle, tdlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+ }
+
+ // Draw remaining ties, if any
+ if (px + dx < dto[othPath].baseLast.x) {
+ p1 = dto[othPath].pts[p0];
+ p2 = dto[othPath].ptsLast;
+ angle = FindAngle(p1, p2);
+ a0 = FindAngle(dto[othPath].base[p0], dto[othPath].baseLast);
+ DIST_T lenr = (dto[othPath].baseLast.x - px + dlenx) / cos(D2R(90.0 - a0));
+ Translate(&p1, p2, angle, -lenr);
+ DrawStraightTies(d, dtod.td, p1, p2, color);
+ } else {
+ p1 = dto[othPath].pts[pn - 2];
+ a0 = FindAngle(p1, p2);
+ Translate(&pos, p2, a0, -dx / 2);
+ DrawTie(d, pos, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+ // Restore saved values
+ if(dtod.toType == DTO_THREE) {
+ px = sx;
+ p0 = s0;
+ }
+
+ // Asymmetric? Use longer ties for remaining two tracks (strPath, secPath)
+ if((dtod.toType == DTO_THREE) && (px + dx >= dto[othPath].baseLast.x)) {
+ for ( ; cnt; cnt--, px += dx) {
+ // if (px >= dto[othPath].base[p0 + 1].x) p0++;
+ if (px >= dto[secPath].base[q0 + 1].x) { q0++; }
+ if (q0 >= qn) {
+ break;
+ }
+
+ if (px + dx >= dto[secPath].baseLast.x) {
+ break;
+ }
+
+ DIST_T dy1 = dto[secPath].base[q0].y + (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ tdlen = dtod.td.length + fabs(dy1);
+ if (tdlen > tdmax) {
+ break;
+ }
+
+ DIST_T dy = dy1;
+ Translate(&pos, s1, angle, px);
+ Translate(&pos, pos, (angle - 90.0), dy / 2);
+
+ DrawTie(d, pos, angle, tdlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+ }
+ if (px + dx < dto[secPath].baseLast.x) {
+ q1 = dto[secPath].pts[q0];
+ q2 = dto[secPath].ptsLast;
+ angle = FindAngle(q1, q2);
+ a0 = FindAngle(dto[secPath].base[q0], dto[secPath].baseLast);
+ DIST_T lenr = (dto[secPath].baseLast.x - px + dlenx) / cos(D2R(90.0 - a0));
+ Translate(&q1, q2, angle, -lenr);
+ DrawStraightTies(d, dtod.td, q1, q2, color);
+ } else {
+ q1 = dto[secPath].pts[qn - 2];
+ a0 = FindAngle(q1, q2);
+ Translate(&pos, q2, a0, -dx / 2);
+ DrawTie(d, pos, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+
+ // Final ties at end
+ if (dtod.toType == DTO_THREE) {
+
+ int n = (int)(dto[strPath].baseLast.x);
+ if (px + dx < len) {
+ angle = FindAngle(s1, s2);
+ DIST_T lenr = len - px + dlenx;
+ Translate(&s1, s2, angle, -lenr);
+ DrawStraightTies(d, dtod.td, s1, s2, color);
+ } else {
+ n = dto[strPath].n;
+ s1 = dto[strPath].pts[n - 2];
+ a0 = FindAngle(s1, s2);
+ Translate(&pos, s2, a0, -dx / 2);
+ DrawTie(d, pos, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+ }
+ }
+}
+
+/**
+ * Draw Curved (Single Origin) Turnout Bridge and Ties. Uses the static dto and dtod structures.
+ *
+ * \param d The drawing object
+ * \param scaleInx The layout/track scale index
+ * \param color The tie color. If black the color is read from the global tieColor.
+ */
+static void DrawCurvedTurnout(
+ drawCmd_p d,
+ SCALEINX_T scaleInx,
+ BOOL_T omitTies,
+ wDrawColor color)
+{
+ DIST_T len, r;
+ coOrd pos;
+ int cnt;
+ ANGLE_T angle, dang;
+ coOrd center;
+ coOrd p1, p2, q1, q2;
+ ANGLE_T a0, a1, a2;
+ struct extraDataCompound_t* xx = dtod.xx;
+
+ if (color == wDrawColorBlack) {
+ color = tieColor;
+ }
+
+ DrawDtoInit();
+
+ // draw the points
+#ifdef DTO_DEBUG
+ if (DTO_DEBUG == DTO_CURVED) { DrawDtoLayout(d, scaleInx); }
+#endif
+
+ int othPath = 0, secPath = 1;
+// int toType = dtod.toType;
+
+ if(dtod.bridge) {
+ DrawTurnoutFill(d,0,othPath,secPath);
+ } else if(dtod.roadbed) {
+ DrawTurnoutFill(d,1,othPath,secPath);
+ }
+ if (omitTies) {
+ return;
+ }
+
+ // Save the ending coordinates
+ coOrd othEnd = zero, secEnd = zero;
+
+ trkSeg_p trk;
+ DIST_T tdlen = dtod.td.length, tdmax = tdlen * 2.5;
+ DIST_T tdspc = dtod.td.spacing, tdspc2 = tdspc / 2.0;
+ DIST_T tdwid = dtod.td.width;
+// double rdot = tdwid / 2;
+
+ int pn = dto[othPath].n;
+ int qn = dto[secPath].n;
+ int p0 = 0, q0 = 0;
+ DIST_T px = 0, qx = 0, dy = 0, dy1 = 0, dy2 = 0;
+
+ double cosAdj = 1.0;
+
+ angle = 0;
+ px = tdspc2;
+ qx = tdspc2;
+ int segs = max(dto[othPath].n, dto[secPath].n);
+ for (; segs > 0; segs--) {
+
+ if (px >= dto[othPath].base[p0 + 1].x) {
+ p0++;
+ }
+ if (qx >= dto[secPath].base[q0 + 1].x) {
+ q0++;
+ }
+ if ((p0 >= pn - 1) || (q0 >= qn - 1)) {
+ break;
+ }
+
+ trk = dto[othPath].trkSeg[p0];
+ if (trk->type == SEG_CRVTRK) {
+
+ center = trk->u.c.center;
+ r = fabs(trk->u.c.radius);
+ a0 = NormalizeAngle(trk->u.c.a0 + dtod.xx->angle);
+ a1 = trk->u.c.a1;
+
+ pos = center;
+ REORIGIN(center, pos, xx->angle, xx->orig);
+
+ len = r * D2R(a1);
+ cnt = (int)floor(len / tdspc + 0.5);
+ if (len - tdspc * cnt >= tdspc2) {
+ cnt++;
+ }
+ DIST_T tdlen = dtod.td.length;
+// DIST_T dx = len / cnt, dx2 = dx / 2;
+
+ if (cnt != 0) {
+ dang = (len / cnt) * 360 / (2 * M_PI * r);
+ DIST_T dx = len / cnt, dx2 = dx / 2;
+
+ if (dto[othPath].type == 'R') {
+ a2 = a0 + dang / 2;
+ } else {
+ a2 = a0 + a1 - dang / 2;
+ dang = -dang;
+ }
+ angle += fabs(dang / 2);
+
+ cosAdj = fabs(cos(D2R(angle)));
+ px += dx2 * cosAdj;
+ qx += dx2 * cosAdj;
+
+ for (; cnt; cnt--, a2 += dang, angle += dang) {
+ if (px >= dto[othPath].base[p0 + 1].x) {
+ p0++;
+ }
+ if (qx >= dto[secPath].base[q0 + 1].x) {
+ q0++;
+ }
+ if ((p0 >= pn - 1) || (q0 >= qn - 1)) {
+ break;
+ }
+
+ coOrd e1, e2;
+ PointOnCircle(&e1, center, r, a2);
+
+ q1 = dto[secPath].pts[q0];
+ q2 = dto[secPath].pts[q0 + 1];
+ FindIntersection(&e2, e1, a2, q1, FindAngle(q1, q2));
+
+ dy = FindDistance(e1, e2);
+ DIST_T tlen = tdlen + dy;
+
+ if (tlen > tdmax) {
+ break;
+ }
+
+ Translate(&pos, e1, a2, -dy / 2);
+ DrawTie(d, pos, angle + xx->angle + 90, tlen, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+
+ // Assures that these ends are the last point drawn before break
+ othEnd = e1;
+ secEnd = e2;
+
+ cosAdj = fabs(cos(D2R(angle)));
+ if (cnt > 1) {
+ px += dx * cosAdj;
+ qx += dx * cosAdj;
+ } else {
+ px += dx2 * cosAdj;
+ qx += dx2 * cosAdj;
+ }
+ }
+ }
+ } else {
+ cosAdj = fabs(cos(D2R(angle)));
+
+ p1 = dto[othPath].base[p0];
+ p2 = dto[othPath].base[p0 + 1];
+ len = FindDistance(p1, p2);
+ cnt = (int)floor(len / tdspc + 0.6);
+ if (cnt > 0) {
+ DIST_T dx = len / cnt/*, dx2 = dx / 2*/;
+
+ for (; cnt; cnt--) {
+ if (px >= dto[othPath].base[p0 + 1].x) {
+ p0++;
+ }
+ if (qx >= dto[secPath].base[q0 + 1].x) {
+ q0++;
+ }
+ if ((p0 >= pn - 1) || (q0 >= qn - 1)) {
+ break;
+ }
+
+ p1 = dto[othPath].base[p0];
+ p2 = dto[othPath].base[p0 + 1];
+
+ if ((px >= dto[othPath].baseLast.x)
+ || (qx >= dto[secPath].baseLast.x)) {
+ break;
+ }
+
+ dy1 = dto[secPath].base[q0].y + (qx - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ dy2 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ dy = dy1 - dy2;
+ DIST_T tlen = tdlen + fabs(cosAdj * dy);
+ if (tlen > tdmax) {
+ break;
+ }
+
+ q1 = dto[secPath].pts[q0];
+ q2 = dto[secPath].pts[q0 + 1];
+ a1 = FindAngle(q1, q2);
+ DIST_T xlen = qx - dto[secPath].base[q0].x;
+ Translate(&pos, q1, a1, xlen);
+ secEnd = pos;
+
+ q1 = dto[othPath].pts[p0];
+ q2 = dto[othPath].pts[p0 + 1];
+ a1 = FindAngle(q1, q2);
+ xlen = px - dto[othPath].base[p0].x;
+ Translate(&pos, q1, a1, xlen);
+ othEnd = pos;
+
+ Translate(&pos, pos, (a1 - 90.0), dy / 2);
+ DrawTie(d, pos, a1, tlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+
+ cosAdj = fabs(cos(D2R(angle)));
+ px += dx * cosAdj;
+ qx += dx * cosAdj;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+
+#ifdef DTO_DEBUG
+ if (DTO_DEBUG == DTO_CURVED) {
+ DrawFillCircle(d, othEnd, rdot, drawColorGreen);
+ DrawFillCircle(d, secEnd, rdot, drawColorGreen);
+
+ DrawFillCircle(d, dto[othPath].pts[p0], rdot, drawColorBlue);
+ DrawFillCircle(d, dto[secPath].pts[q0], rdot, drawColorBlue);
+ }
+#endif
+
+ // Draw remaining ties, if any
+ p1 = othEnd;
+ p2 = dto[othPath].ptsLast;
+ a0 = FindAngle(p1, p2);
+ len = FindDistance(p1, p2);
+ if (len >= 2 * tdspc) {
+ Translate(&p1, p1, a0, tdspc2);
+ DrawStraightTies(d, dtod.td, p1, p2, color);
+ } else if (len > tdspc2) {
+ Translate(&p2, p2, a0, -tdspc2);
+ DrawTie(d, p2, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+
+ q1 = secEnd;
+ q2 = dto[secPath].ptsLast;
+ a0 = FindAngle(q1, q2);
+ len = FindDistance(q1, q2);
+ if (len >= 2 * tdspc) {
+ Translate(&q1, q1, a0, tdspc2);
+ DrawStraightTies(d, dtod.td, q1, q2, color);
+ } else if (len > tdspc2) {
+ Translate(&q2, q2, a0, -tdspc2);
+ DrawTie(d, q2, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+}
+
+/**
+ * Draw Crossing and Slip Turnout Bridge and Ties - Uses the static dto and dtod structures.
+ *
+ * \param d The drawing object
+ * \param scaleInx The layout/track scale index
+ * \param color The tie color. If black the color is read from the global tieColor.
+ */
+static void DrawXingTurnout(
+ drawCmd_p d,
+ SCALEINX_T scaleInx,
+ BOOL_T omitTies,
+ wDrawColor color)
+{
+ DIST_T len;
+ coOrd pos;
+ int cnt;
+ ANGLE_T cAngle;
+
+ if (color == wDrawColorBlack) {
+ color = tieColor;
+ }
+
+ coOrd c1, c2, s1, s2, p1, p2, q1;
+ int p0, q0;
+ ANGLE_T a0, a1, a2;
+ int strPath = dtod.strPath, str2Path = dtod.str2Path;
+
+ struct extraDataCompound_t* xx = dtod.xx;
+
+ DrawDtoInit();
+
+ dto[strPath].angle = FindAngle(dto[strPath].pts[0], dto[strPath].ptsLast);
+ dto[str2Path].angle = FindAngle(dto[str2Path].pts[0], dto[str2Path].ptsLast);
+
+ int othPath = strPath, secPath = str2Path;
+ int toType = dtod.toType;
+
+ int i, j;
+ switch (toType) {
+ case DTO_XING:
+ case DTO_XNG9:
+ break;
+ case DTO_SSLIP:
+ for (i = 0; i < dtod.pathCnt; i++) {
+ if (dto[i].type == 'L' || dto[i].type == 'R') {
+ secPath = i;
+ break;
+ }
+ }
+ break;
+ case DTO_DSLIP:
+ for (i = 0; i < dtod.pathCnt; i++) {
+ if (dto[i].type == 'L') {
+ othPath = i;
+ } else if (dto[i].type == 'R') {
+ secPath = i;
+ }
+ }
+ break;
+ }
+
+ if(dtod.bridge) {
+ DrawXingFill(d,0,othPath,secPath);
+ } else if(dtod.roadbed) {
+ DrawXingFill(d,1,othPath,secPath);
+ }
+
+ // draw the points
+#ifdef DTO_DEBUG
+ if (DTO_DEBUG == DTO_XING) { DrawDtoLayout(d, scaleInx); }
+#endif
+
+ if (omitTies) {
+ return;
+ }
+
+ DIST_T tdlen = dtod.td.length, tdmax = 2.0 * tdlen;
+ DIST_T tdwid = dtod.td.width;
+ DIST_T tdspc = dtod.td.spacing, tdspc2 = tdspc / 2;
+
+ // Midpoint
+ p1 = dto[strPath].pts[0];
+ a1 = dto[strPath].angle;
+
+ q1 = dto[str2Path].pts[0];
+ a2 = dto[str2Path].angle;
+
+ FindIntersection(&pos, p1, a1, q1, a2);
+ dtod.midPt = pos;
+
+#ifdef DTO_DEBUG
+ if(DTO_DEBUG == DTO_XING) {
+ double r = td->width / 2;
+ DrawFillCircle(d,p1,r,drawColorPurple);
+ DrawFillCircle(d,q1,r,drawColorPurple);
+ DrawFillCircle(d,dtod.midPt,r,drawColorPurple);
+ }
+#endif
+
+ // Tie length adjust
+ double dAngle = fabs(DifferenceBetweenAngles(a1, a2));
+ double magic = 1.0;
+
+ // Short circuit the complex code for this simple case
+ if (toType == DTO_XNG9) {
+ p1 = dto[strPath].pts[0];
+ p2 = dto[strPath].ptsLast;
+ DrawStraightTies(d, dtod.td, p1, p2, color);
+
+ p1 = dto[str2Path].pts[0];
+ p2 = dto[str2Path].ptsLast;
+
+ // Omit the center ties
+ magic = 1 / cos(D2R(90 - dAngle));
+ DIST_T tdadj = (tdlen / 2) * magic;
+ DIST_T tdadj2 = tdspc2 * magic;
+
+ dAngle = (dAngle - 90) / 2;
+ Translate(&pos, dtod.midPt, a2, -tdadj - tdadj2);
+ DrawTie(d, pos, a2 - dAngle, tdlen, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+
+ Translate(&pos, dtod.midPt, a2, -tdadj - tdspc);
+ DrawStraightTies(d, dtod.td, p1, pos, color);
+
+ Translate(&pos, dtod.midPt, a2, tdadj + tdadj2);
+ DrawTie(d, pos, a2 - dAngle, tdlen, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+
+ Translate(&pos, dtod.midPt, a2, tdadj + tdspc);
+ DrawStraightTies(d, dtod.td, pos, p2, color);
+ return;
+ }
+
+ // Straight vector for tie angle
+ s1 = MidPtCoOrd(dto[strPath].base[0], dto[str2Path].base[0]);
+ s2 = MidPtCoOrd(dto[strPath].baseLast, dto[str2Path].baseLast);
+
+ // Rotate base coordinates so that the tie line is aligned with x-axis and origin is at zero
+ cAngle = FindAngle(s1, s2);
+ for (i = 0; i < DTO_DIM; i++)
+ for (j = 0; j < dto[i].n; j++) {
+ dto[i].base[j].x -= s1.x;
+ dto[i].base[j].y -= s1.y;
+ Rotate(&dto[i].base[j], zero, (90.0 - cAngle));
+ }
+
+ for (i = 0; i < DTO_DIM; i++) {
+ for (j = 0; j < dto[i].n - 1; j++) {
+ dto[i].dy[j] = (dto[i].base[j + 1].y - dto[i].base[j].y) /
+ (dto[i].base[j + 1].x - dto[i].base[j].x);
+ }
+ if (dto[i].type == 'S') {
+ dto[i].angle = FindAngle(dto[i].pts[0], dto[i].ptsLast);
+ }
+ }
+
+ // Tie center line in drawing coordinates
+ REORIGIN(c1, s1, xx->angle, xx->orig);
+ REORIGIN(c2, s2, xx->angle, xx->orig);
+ cAngle = FindAngle(c1, c2);
+
+ int pn = dto[othPath].n;
+ int qn = dto[secPath].n;
+
+ // Tie length adjust
+ magic = 1 / cos(0.5 * D2R(dAngle));
+ // Extra ties length adjust
+ double magic2 = 1.0 / cos(0.5 * D2R(dAngle));
+
+ // Draw right half
+ len = FindDistance(dtod.midPt, c2);
+ cnt = (int)floor(len / dtod.td.spacing + 0.5);
+ if (cnt <= 0) {
+ return;
+ }
+
+ DIST_T dx = len / cnt;
+ p0 = q0 = 0;
+ DIST_T dx2 = dx / 2;
+ DIST_T px = len + dx2;
+ DIST_T lenx = 0;
+
+ while (p0 < pn && px > dto[othPath].base[p0 + 1].x) { p0++; }
+ while (q0 < qn && px > dto[secPath].base[q0 + 1].x) { q0++; }
+ while (p0 < pn && q0 < qn) {
+ if (px > dto[othPath].base[p0 + 1].x) { p0++; }
+ if (px > dto[secPath].base[q0 + 1].x) { q0++; }
+ if (p0 >= pn || q0 >= qn) {
+ break;
+ }
+ // Dont use baseLast, as base coOrds have been rotated
+ if ((px + dx >= dto[othPath].base[pn - 1].x)
+ || (px + dx >= dto[secPath].base[qn - 1].x)) {
+ break;
+ }
+
+ DIST_T dy1 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ DIST_T dy2 = dto[secPath].base[q0].y + (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ tdlen = (dtod.td.length + fabs(dy1) + fabs(dy2)) * magic;
+ if(tdlen > tdmax) {
+ if(dAngle >= 30) {
+ DIST_T dy = (dy1 + dy2) / 2;
+ Translate(&pos,dtod.midPt,cAngle,px - len);
+ Translate(&pos,pos,(cAngle - 90.0),dy);
+ DrawTie(d,pos,cAngle,tdlen - dtod.td.length * magic,tdwid,color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ lenx += dx2 * magic2;
+ }
+ break;
+ }
+
+ DIST_T dy = (dy1 + dy2) / 2;
+ Translate(&pos, dtod.midPt, cAngle, px - len);
+ Translate(&pos, pos, (cAngle - 90.0), dy);
+ DrawTie(d, pos, cAngle, tdlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+
+ px += dx;
+ lenx += dx;
+ }
+
+ p1 = dtod.midPt;
+ p2 = dto[strPath].ptsLast;
+ DIST_T lenr = FindDistance(p1, p2) - lenx * magic2;
+ a0 = dto[strPath].angle;
+ if (lenr > dx) {
+ Translate(&pos, p2, a0, -lenr);
+ DrawStraightTies(d, dtod.td, pos, p2, color);
+ } else {
+ Translate(&pos, p2, a0, -dx2);
+ DrawTie(d, pos, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+
+ // p1 = dtod.midPt;
+ p2 = dto[str2Path].ptsLast;
+ lenr = FindDistance(p1, p2) - lenx * magic2;
+ a0 = dto[str2Path].angle;
+ if (lenr > dx) {
+ Translate(&pos, p2, a0, -lenr);
+ DrawStraightTies(d, dtod.td, pos, p2, color);
+ } else {
+ Translate(&pos, p2, a0, -dx2);
+ DrawTie(d, pos, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+
+ // Draw left half
+ // Change the straight path used
+ if (dtod.toType == DTO_SSLIP) {
+ othPath = str2Path;
+ }
+
+ len = FindDistance(c1, dtod.midPt);
+ cnt = (int)floor(len / dtod.td.spacing + 0.5);
+ if (cnt <= 0) {
+ return;
+ }
+
+ p0 = q0 = 0;
+ tdlen = dtod.td.length;
+
+ dx = len / cnt;
+ dx2 = dx / 2;
+ px = len - dx2;
+ lenx = 0;
+
+ while (p0 < pn && px > dto[othPath].base[p0 + 1].x) { p0++; }
+ while (q0 < qn && px > dto[secPath].base[q0 + 1].x) { q0++; }
+ while (p0 >= 0 && q0 >= 0) {
+ if (px < dto[othPath].base[p0].x) { p0--; }
+ if (px < dto[secPath].base[q0].x) { q0--; }
+ if (p0 < 0 || q0 < 0) {
+ break;
+ }
+
+ if ((px - dx < dto[othPath].base[0].x)
+ || (px - dx < dto[secPath].base[0].x)) {
+ break;
+ }
+
+ DIST_T dy1 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ DIST_T dy2 = dto[secPath].base[q0].y + (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ tdlen = (dtod.td.length + fabs(dy1) + fabs(dy2)) * magic;
+ if(tdlen > tdmax) {
+ if(dAngle >= 30) {
+ DIST_T dy = (dy1 + dy2) / 2;
+ Translate(&pos,dtod.midPt,cAngle,px - len);
+ Translate(&pos,pos,(cAngle - 90.0),dy);
+ DrawTie(d,pos,cAngle,tdlen - dtod.td.length * magic,tdwid,color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ lenx += dx2 * magic2;
+ }
+ break;
+ }
+
+ DIST_T dy = (dy1 + dy2) / 2;
+ Translate(&pos, dtod.midPt, cAngle, px - len);
+ Translate(&pos, pos, (cAngle - 90.0), dy);
+ DrawTie(d, pos, cAngle, tdlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+
+ px -= dx;
+ lenx += dx;
+ }
+
+ p1 = dto[strPath].pts[0];
+ p2 = dtod.midPt;
+ a0 = dto[strPath].angle;
+ lenr = FindDistance(p1, p2) - lenx * magic2;
+ if (lenr > dx) {
+ Translate(&pos, p1, a0, lenr);
+ DrawStraightTies(d, dtod.td, p1, pos, color);
+ } else {
+ Translate(&pos, p1, a0, dx2);
+ DrawTie(d, pos, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+ p1 = dto[str2Path].pts[0];
+ // p2 = dtod.midPt;
+ a0 = dto[str2Path].angle;
+ lenr = FindDistance(p1, p2) - lenx * magic2;
+ if (lenr > dx) {
+ Translate(&pos, p1, a0, lenr);
+ DrawStraightTies(d, dtod.td, p1, pos, color);
+ } else {
+ Translate(&pos, p1, a0, dx2);
+ DrawTie(d, pos, a0, dtod.td.length, tdwid, color,
+ tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+}
+
+/**
+ * Draw Crossover (Two Origin) Turnout Bridge and Ties. Uses the static dto and dtod structures.
+ *
+ * \param d The drawing object
+ * \param scaleInx The layout/track scale index
+ * \param color The tie color. If black the color is read from the global tieColor.
+ */
+static void DrawCrossTurnout(
+ drawCmd_p d,
+ SCALEINX_T scaleInx,
+ BOOL_T omitTies,
+ wDrawColor color)
+{
+ DIST_T len, dx;
+ coOrd pos;
+ int cnt;
+ ANGLE_T angle;
+
+ if (color == wDrawColorBlack) {
+ color = tieColor;
+ }
+
+// struct extraDataCompound_t* xx = dtod.xx;
+
+ DrawDtoInit();
+
+ // draw the points
+#ifdef DTO_DEBUG
+ if (DTO_DEBUG == DTO_LCROSS) { DrawDtoLayout(d, scaleInx); }
+#endif
+
+ int strPath = dtod.strPath, str2Path = dtod.str2Path;
+ // Bad assumption
+ int othPath = 2, secPath = 2;
+ if (dtod.pathCnt == 4) { secPath = 3; }
+
+ dto[strPath].angle = FindAngle(dto[strPath].pts[0], dto[strPath].ptsLast);
+ dto[str2Path].angle = FindAngle(dto[str2Path].pts[0], dto[str2Path].ptsLast);
+
+ if(dtod.bridge) {
+ DrawCrossFill(d,0,strPath,str2Path);
+ } else if(dtod.roadbed) {
+ DrawCrossFill(d,1,strPath,str2Path);
+ }
+ if (omitTies) {
+ return;
+ }
+
+ coOrd s1, s2, t1;
+// coOrd t2, p1, p2, q1, q2;
+ int s0, t0, p0, q0;
+
+ int sn = dto[strPath].n;
+ int tn = dto[str2Path].n;
+ int pn = dto[othPath].n;
+ int qn = dto[secPath].n;
+
+ s1 = dto[strPath].pts[0];
+ s2 = dto[strPath].ptsLast;
+ t1 = dto[str2Path].pts[0];
+// t2 = dto[str2Path].ptsLast;
+ angle = dto[strPath].angle;
+
+// p1 = dto[othPath].base[0];
+// p2 = dto[othPath].baseLast;
+// q1 = dto[secPath].base[0];
+// q2 = dto[secPath].baseLast;
+
+ len = FindDistance(s1, s2);
+ angle = dto[strPath].angle;
+
+ cnt = (int)floor(len / dtod.td.spacing + 0.5);
+ if (cnt > 0) {
+ DIST_T px = 0;
+ DIST_T dy, dy1, dy2;
+ int cflag = 0;
+ dy = dto[str2Path].base[0].y - dto[strPath].base[0].y;
+
+ dx = len / cnt;
+ s0 = t0 = p0 = q0 = 0;
+ DIST_T tdlen = dtod.td.length;
+ DIST_T tdwid = dtod.td.width;
+ DIST_T dlenx = dx / 2;
+
+ DIST_T px1 = len / 2 - dlenx * 5,
+ px2 = len / 2 + dlenx * 4;
+
+ for (px = dlenx; cnt; cnt--, px += dx) {
+ if (px >= dto[strPath].base[s0 + 1].x) { s0++; }
+ if (px >= dto[str2Path].base[t0 + 1].x) { t0++; }
+ if (px >= dto[othPath].base[p0 + 1].x) { p0++; }
+ if (px >= dto[secPath].base[q0 + 1].x) { q0++; }
+ if (s0 >= sn || t0 >= tn || p0 >= pn || q0 >= qn) {
+ break;
+ }
+
+ if ((px >= dto[strPath].baseLast.x)
+ || (px >= dto[str2Path].baseLast.x)) {
+ break;
+ }
+
+ dy1 = dy2 = 0;
+ cflag = 0;
+ if (px < px1) {
+ switch (dtod.toType) {
+ case DTO_DCROSS:
+ dy1 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ dy2 = dy - dto[secPath].base[q0].y - (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ break;
+ case DTO_LCROSS:
+ dy1 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ dy2 = 0;
+ break;
+ case DTO_RCROSS:
+ dy1 = 0;
+ dy2 = dy - dto[secPath].base[q0].y - (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ break;
+ default:
+ break;
+ }
+ } else if (px < px2) {
+ dy1 = (dto[str2Path].base[s0].y - dto[strPath].base[t0].y);
+ dy2 = 0;
+ cflag = 1;
+ } else {
+ switch (dtod.toType) {
+ case DTO_DCROSS:
+ dy1 = dto[secPath].base[q0].y + (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ dy2 = dy - dto[othPath].base[p0].y - (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ break;
+ case DTO_LCROSS:
+ dy1 = 0;
+ dy2 = dy - dto[secPath].base[q0].y - (px - dto[secPath].base[q0].x) *
+ dto[secPath].dy[q0];
+ break;
+ case DTO_RCROSS:
+ dy1 = dto[othPath].base[p0].y + (px - dto[othPath].base[p0].x) *
+ dto[othPath].dy[p0];
+ dy2 = 0;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (fabs(dy1) + fabs(dy2) >= dy) {
+ dy1 = (dto[str2Path].base[s0].y - dto[strPath].base[t0].y);
+ dy2 = 0;
+ cflag = 1;
+ }
+
+ tdlen = dtod.td.length + fabs(dy1);
+ Translate(&pos, s1, angle, px);
+ Translate(&pos, pos, (angle - 90.0), dy1 / 2);
+ DrawTie(d, pos, angle, tdlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+
+ if (!cflag) {
+ tdlen = dtod.td.length + fabs(dy2);
+ Translate(&pos, t1, angle, px);
+ Translate(&pos, pos, (angle - 90.0), -dy2 / 2);
+ DrawTie(d, pos, angle, tdlen, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+ }
+ return;
+
+ // Draw remaining ties, if any
+ // Currently by definition, there won't be any
+ /*
+ if (px + dx < dto[strPath].baseLast.x) {
+ p1 = dto[strPath].pts[p0];
+ p2 = dto[strPath].ptsLast;
+ angle = FindAngle(p1, p2);
+ a0 = FindAngle(dto[strPath].base[p0], dto[strPath].baseLast);
+ DIST_T lenr = (dto[strPath].baseLast.x - px + dlenx) / cos(D2R(90.0 - a0));
+ Translate(&p1, p2, angle, -lenr);
+ DrawStraightTies(d, dtod.td, p1, p2, color);
+ }
+ else {
+ p1 = dto[strPath].pts[pn - 2];
+ a0 = FindAngle(p1, p2);
+ Translate(&pos, p2, a0, -dx / 2);
+ DrawTie(d, pos, a0, td->length, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+
+ if (px + dx < dto[str2Path].baseLast.x) {
+ q1 = dto[str2Path].pts[q0];
+ q2 = dto[str2Path].ptsLast;
+ angle = FindAngle(q1, q2);
+ a0 = FindAngle(dto[str2Path].base[q0], dto[str2Path].baseLast);
+ DIST_T lenr = (dto[str2Path].baseLast.x - px + dlenx) / cos(D2R(90.0 - a0));
+ Translate(&q1, q2, angle, -lenr);
+ DrawStraightTies(d, dtod.td, q1, q2, color);
+ }
+ else {
+ q1 = dto[str2Path].pts[qn - 2];
+ a0 = FindAngle(q1, q2);
+ Translate(&pos, q2, a0, -dx / 2);
+ DrawTie(d, pos, a0, td->length, tdwid, color, tieDrawMode == TIEDRAWMODE_SOLID);
+ }
+ */
+ }
+}
+
+/**
+ * Draw all turnout components: ties, rail, roadbed, etc.
+ *
+ * The turnout is checked to see if the enhanced methods can be used.
+ * If so the ties are drawn and the TB_NOTIES bit is set so that the
+ * rails and such are drawn on top of the ties. Similarly the bridge
+ * and roadbed bits are cleared so the enhanced method can be used.
+ * Those bits are restored to the previous state before return.
+ *
+ * \param trk Pointer to the track object
+ * \param d The drawing object
+ * \param color The turnout color.
+ */
+EXPORT void DrawTurnout(
+ track_p trk,
+ drawCmd_p d,
+ wDrawColor color)
+{
+ struct extraDataCompound_t* xx = GET_EXTRA_DATA(trk, T_TURNOUT,
+ extraDataCompound_t);
+ wIndex_t i;
+ long widthOptions = 0;
+ SCALEINX_T scaleInx = GetTrkScale(trk);
+ BOOL_T omitTies = !DoDrawTies(d, trk) || !DrawTwoRails(d,1)
+ || ((d->options & DC_SIMPLE) != 0); // || (scaleInx == 0);
+
+ widthOptions = DTS_LEFT | DTS_RIGHT;
+
+ // Save these values
+ int noTies = GetTrkNoTies(trk);
+ int bridge = GetTrkBridge(trk);
+ int roadbed = GetTrkRoadbed(trk);
+
+ long skip = 0;
+ /** @prefs [Preference] NormalTurnoutDraw=1 to skip enhanced drawing methods */
+ wPrefGetInteger("Preference", "NormalTurnoutDraw", (long *) &skip, 0);
+
+ int pathCnt = (skip == 0 ? GetTurnoutPaths(trk, xx) : 0);
+
+ if ( (pathCnt > 1) && (pathCnt <= DTO_DIM)
+ && ( GetTrkEndPtCnt( trk ) <= 4)
+ && (xx->special == TOnormal) ) {
+
+ dtod.bridge = bridge;
+ dtod.roadbed = roadbed;
+
+// int strPath = -1;
+ GetTurnoutType();
+
+ if (dtod.toType != DTO_INVALID) {
+
+ switch (dtod.toType) {
+ case DTO_NORMAL:
+ case DTO_THREE:
+ case DTO_WYE:
+ DrawNormalTurnout(d, scaleInx, omitTies, color);
+ break;
+ case DTO_CURVED:
+ DrawCurvedTurnout(d, scaleInx, omitTies, color);
+ break;
+ case DTO_XING:
+ case DTO_XNG9:
+ case DTO_SSLIP:
+ case DTO_DSLIP:
+ DrawXingTurnout(d, scaleInx, omitTies, color);
+ break;
+ case DTO_LCROSS:
+ case DTO_RCROSS:
+ case DTO_DCROSS:
+ DrawCrossTurnout(d, scaleInx, omitTies, color);
+ break;
+ default:
+ break;
+ }
+ // Ignore these settings
+ SetTrkNoTies(trk, 1);
+ ClrTrkBits( trk,TB_BRIDGE );
+ ClrTrkBits(trk, TB_ROADBED);
+ }
+ }
+
+ // Begin standard DrawTurnout code to draw rails or centerline
+ // no curve center for turnouts, leave centerline for sectional curved
+ long opts = widthOptions | (xx->segCnt > 1 ? DTS_NOCENTER : 0);
+ DrawSegsO(d, trk, xx->orig, xx->angle, xx->segs, xx->segCnt, GetTrkGauge(trk),
+ color, opts);
+
+
+ for (i = 0; i < GetTrkEndPtCnt(trk); i++) {
+ DrawEndPt(d, trk, i, color);
+ }
+ if ((d->options & DC_SIMPLE) == 0 &&
+ (labelWhen == 2 || (labelWhen == 1 && (d->options & DC_PRINT))) &&
+ labelScale >= d->scale &&
+ (GetTrkBits(trk) & TB_HIDEDESC) == 0) {
+ DrawCompoundDescription(trk, d, color);
+ if (!xx->handlaid) {
+ LabelLengths(d, trk, color);
+ }
+ }
+ if (roadbedWidth > GetTrkGauge(trk) &&
+ DrawTwoRails( d, 1 ) &&
+ ( (d->options & DC_PRINT) || roadbedOnScreen ) ) {
+ DrawTurnoutRoadbed(d, color, xx->orig, xx->angle, xx->segs, xx->segCnt);
+ }
+
+ // Restore these settings
+ if (noTies == 0) { ClrTrkBits(trk, TB_NOTIES); }
+ if (bridge) { SetTrkBits(trk, TB_BRIDGE); }
+ if (roadbed) { SetTrkBits(trk, TB_ROADBED); }
+}