/* * Licensed Materials - Property of Perforce Software, Inc. * © Copyright Perforce Software, Inc. 2014, 2021 * © Copyright IBM Corp. 2009, 2014 * © Copyright ILOG 1996, 2009 * All Rights Reserved. * * Note to U.S. Government Users Restricted Rights: * The Software and Documentation were developed at private expense and * are "Commercial Items" as that term is defined at 48 CFR 2.101, * consisting of "Commercial Computer Software" and * "Commercial Computer Software Documentation", as such terms are * used in 48 CFR 12.212 or 48 CFR 227.7202-1 through 227.7202-4, * as applicable. */ import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.io.IOException; import ilog.views.IlvGraphic; import ilog.views.IlvGraphicBag; import ilog.views.IlvGraphicUtil; import ilog.views.IlvManager; import ilog.views.IlvPoint; import ilog.views.IlvRect; import ilog.views.IlvStroke; import ilog.views.IlvTransformer; import ilog.views.graphic.IlvGraphicSet; import ilog.views.graphic.IlvPolyPoints; import ilog.views.io.IlvFieldNotFoundException; import ilog.views.io.IlvInputStream; import ilog.views.io.IlvOutputStream; import ilog.views.io.IlvReadFileException; import ilog.views.maps.IlvCoordinateSystemProperty; import ilog.views.maps.IlvLinearUnit; import ilog.views.maps.IlvScaleController; import ilog.views.maps.projection.IlvProjectionUtil; import ilog.views.maps.srs.coordsys.IlvCoordinateSystem; class LineInfo { double len; double ddx; double ddy; double x1; double y1; double x2; double y2; double l1x1; double l1y1; double l1x2; double l1y2; double l2x1; double l2y1; double l2x2; double l2y2; IlvPoint p1 = new IlvPoint(); IlvPoint p2 = new IlvPoint(); int _ix; int _iy; boolean doJoin; double jx1; double jy1; double jx2; double jy2; static final double SMALL = 1e-10d; LineInfo() { } void create(IlvPoint p[], int i, IlvTransformer t, double spacing) { p1.x = p[i].x; p1.y = p[i].y; i++; p2.x = p[i].x; p2.y = p[i].y; if (t != null) { t.apply(p1); t.apply(p2); } x1 = p1.x; y1 = p1.y; x2 = p2.x; y2 = p2.y; double dx = x2 - x1; double dy = y2 - y1; len = Math.sqrt(dx * dx + dy * dy); ddx = spacing * dy / len; ddy = spacing * dx / len; l1x1 = x1 + ddx; l1y1 = y1 - ddy; l1x2 = x2 + ddx; l1y2 = y2 - ddy; l2x1 = x1 - ddx; l2y1 = y1 + ddy; l2x2 = x2 - ddx; l2y2 = y2 + ddy; } void join(double x11, double y11, double x21, double y21) { jx1 = x11; jy1 = y11; jx2 = x21; jy2 = y21; doJoin = true; } void intersect(LineInfo info) { doJoin = false; if (intersect(l1x1, l1y1, l1x2, l1y2, info.l1x1, info.l1y1, info.l1x2, info.l1y2)) { double x = _ix; double y = _iy; l1x2 = x; l1y2 = y; info.l1x1 = x; info.l1y1 = y; } else { join(l1x2, l1y2, info.l1x1, info.l1y1); } if (intersect(l2x1, l2y1, l2x2, l2y2, info.l2x1, info.l2y1, info.l2x2, info.l2y2)) { double x = _ix; double y = _iy; l2x2 = x; l2y2 = y; info.l2x1 = x; info.l2y1 = y; } else { join(l2x2, l2y2, info.l2x1, info.l2y1); } } boolean intersect(double x11, double y11, double x21, double y21, double x3, double y3, double x4, double y4) { double d = (y4 - y3) * (x21 - x11) - (x4 - x3) * (y21 - y11); if (Math.abs(d) == 0.) return false; double na = ((x4 - x3) * (y11 - y3) - (y4 - y3) * (x11 - x3)) / d; if (na < 0d || na > 1d) return false; double nb = ((x21 - x11) * (y11 - y3) - (y21 - y11) * (x11 - x3)) / d; if (nb < 0d || nb > 1d) return false; _ix = (int) (x11 + na * (x21 - x11)); _iy = (int) (y11 + na * (y21 - y11)); return true; } void affect(LineInfo source) { len = source.len; ddx = source.ddx; ddy = source.ddy; x1 = source.x1; y1 = source.y1; x2 = source.x2; y2 = source.y2; l1x1 = source.l1x1; l1y1 = source.l1y1; l1x2 = source.l1x2; l1y2 = source.l1y2; l2x1 = source.l2x1; l2y1 = source.l2y1; l2x2 = source.l2x2; l2y2 = source.l2y2; doJoin = false; } } /** * A graphical object representing a road/railroad. This object is configured by * another class, <code>IlvRailroadAttributes</code>. */ public class IlvRailroad extends IlvPolyPoints { private IlvRailroadAttributes _railAttributes; private LineInfo _current = new LineInfo(); private LineInfo _prev = new LineInfo(); private LineInfo _next = new LineInfo(); private IlvPoint[] _tmp; private int _x[]; private int _y[]; BasicStroke _basicStroke; BasicStroke _resetStroke = new BasicStroke(); public IlvRailroad(IlvPoint[] points, boolean copy) { super(points, copy); } public IlvRailroad(IlvPoint[] points, boolean copy, IlvRailroadAttributes a) { super(points, copy); setRailroadAttributes(a); } public IlvRailroad(IlvRailroad source) { super(source); if (source._railAttributes != null) source.setRailroadAttributes(source._railAttributes.copy()); } public IlvRailroad(IlvInputStream stream) throws IlvReadFileException { super(stream); try { _railAttributes = (IlvRailroadAttributes) stream.readPersistentObject("railAttributes"); } catch (IlvFieldNotFoundException e) { } } Override public void write(IlvOutputStream stream) throws IOException { super.write(stream); if (_railAttributes != null) stream.write("railAttributes", _railAttributes); } Override public IlvGraphic copy() { return new IlvRailroad(this); } public IlvRailroadAttributes getRailroadAttributes() { if (_railAttributes == null) _railAttributes = new IlvRailroadAttributes(); return _railAttributes; } public void setRailroadAttributes(IlvRailroadAttributes ra) { _railAttributes = ra; } private Double _factor; private double getZoomFactorFromScale(double scale) { if (_factor != null) return _factor.doubleValue(); IlvManager manager = null; IlvGraphicBag b = getGraphicBag(); while (b instanceof IlvGraphicSet) b = ((IlvGraphic) b).getGraphicBag(); if (b instanceof IlvManager) manager = (IlvManager) b; double tFactor = 1.; if (manager != null) { IlvCoordinateSystem cs = IlvCoordinateSystemProperty.GetCoordinateSystem(manager); if (cs == null) tFactor = 1.; else { IlvLinearUnit unit = IlvProjectionUtil.GetLinearUnit(cs); tFactor = unit.toMeters(scale) * 1000f; tFactor /= IlvScaleController.GetScreenPixelSizeMM(); } } _factor = Double.valueOf(tFactor); return tFactor; } public double getRailSpacing(IlvTransformer t) { IlvRailroadAttributes ra = getRailroadAttributes(); double val = ra.getRailSpacing(); double max = ra.getMaximumRailSpacing(); double scale = ra.getScale(); double zl = getZoomFactorFromScale(scale); if (max == 0) return val; double zoomlevel = (t == null ? 1d : t.zoomFactor()); double v = zoomlevel * val / zl; if (v >= max) v = max; return v; } public double getTieWidth(IlvTransformer t) { IlvRailroadAttributes ra = getRailroadAttributes(); double val = ra.getTieWidth(); double max = ra.getMaximumTieWidth(); double scale = ra.getScale(); double zl = getZoomFactorFromScale(scale); if (max == 0) return val; double zoomlevel = (t == null ? 1d : t.zoomFactor()); double v = zoomlevel * val / zl; if (v >= max) v = max; return v; } Override public IlvRect boundingBox(IlvTransformer t) { IlvRect box; box = super.boundingBox(t); getRailroadAttributes(); double rs = getRailSpacing(t); double tw = getTieWidth(t); double width = Math.max(tw, rs); box.expand(width / 2d); return box; } Override public boolean pointsInBBox() { return true; } Override public boolean contains(IlvPoint p, IlvPoint tp, IlvTransformer t) { double zoom = t == null ? 1d : t.zoomFactor(); IlvPoint pts[] = getPoints(); getRailroadAttributes(); double tw = getTieWidth(t); double rs = getRailSpacing(t); double width = Math.max(tw, rs); return IlvGraphicUtil.PointInPolyline(tp, pts, pts.length, width / zoom, IlvStroke.JOIN_MITER, IlvStroke.CAP_BUTT, t); } private void initTmpPoints() { if (_tmp != null) return; _tmp = new IlvPoint[3]; _tmp[0] = new IlvPoint(); _tmp[1] = new IlvPoint(); _tmp[2] = new IlvPoint(); } private void initTmpXYPoints() { if (_x == null) _x = new int[5]; if (_y == null) _y = new int[5]; } double drawTies(Graphics dst, IlvTransformer t, double offset, LineInfo current, LineInfo prev, LineInfo next) { double d = current.len; if (offset > d) return offset - d; IlvRailroadAttributes ra = getRailroadAttributes(); double tieSpacing = ra.getTieSpacing() * 2d; double tieWidth = getTieWidth(t); double tt = ra.getTieThickness(); if (tt > 0f) initTmpPoints(); dst.setColor(ra.getTieColor()); double x1 = current.x1; double y1 = current.y1; double dx = current.x2 - x1; double dy = current.y2 - y1; double ddx = tieWidth * dy / d; double ddy = tieWidth * dx / d; double u; for (u = offset; u < d; u += tieSpacing) { double ud = u / d; double px = x1 + ud * dx; double py = y1 + ud * dy; if (!dst.hitClip((int) (px - tieWidth) - 1, (int) (py - tieWidth) - 1, (int) (2f * tieWidth) + 2, (int) (2f * tieWidth) + 2)) continue; double fx = px - ddx; double fy = py + ddy; double tx = px + ddx; double ty = py - ddy; float limit = ra.getSlantingLimit(); if (limit > 0f) { if ((next != null) && ((d - u) <= limit)) { double angle = (angle(current, next) - Math.PI) / 2d; angle *= 1f - (d - u) / limit; double sa = - Math.sin(angle); double ca = Math.cos(angle); fx = -ddx * ca - ddy * sa + px; fy = -ddx * sa + ddy * ca + py; tx = ddx * ca + ddy * sa + px; ty = ddx * sa - ddy * ca + py; } if ((prev != null) && (u <= limit)) { double angle = (angle(prev, current) - Math.PI) / 2d; angle *= 1f - u / limit; double sa = Math.sin(angle); double ca = Math.cos(angle); fx = -ddx * ca - ddy * sa + px; fy = -ddx * sa + ddy * ca + py; tx = ddx * ca + ddy * sa + px; ty = ddx * sa - ddy * ca + py; } } if (tt > 0f) { _tmp[0].move(fx + 0.5f, fy + 0.5f); _tmp[1].move(tx + 0.5f, ty + 0.5f); IlvGraphicUtil.DrawPolyline(dst, _tmp, 2, tt, IlvStroke.JOIN_MITER, IlvStroke.CAP_BUTT, null, null); } else dst.drawLine((int) (fx), (int) (fy), (int) (tx), (int) (ty)); } return (u - d); } double angle(LineInfo l1, LineInfo l2) { double dx1 = l1.x1 - l1.x2; double dy1 = l1.y1 - l1.y2; double dx2 = l2.x2 - l2.x1; double dy2 = l2.y2 - l2.y1; double dp = dx1 * dx2 + dy1 * dy2; double cos = dp / (l1.len * l2.len); if (cos < -1f) cos = -1; if (cos > 1f) cos = 1f; double angle = Math.acos(cos); double cp = dx1 * dy2 - dx2 * dy1; if (cp > 0) angle = 2d * Math.PI - angle; return angle; } void drawBackground(Graphics dst, LineInfo info) { IlvRailroadAttributes ra = getRailroadAttributes(); Color b = ra.getBackground(); if (b != null) { initTmpXYPoints(); int i = 0; _x[i] = (int) (info.l1x1); _y[i] = (int) (info.l1y1); i++; _x[i] = (int) (info.l1x2); _y[i] = (int) (info.l1y2); i++; if (info.doJoin) { _x[i] = (int) (info.jx2); _y[i] = (int) (info.jy2); i++; } _x[i] = (int) (info.l2x2); _y[i] = (int) (info.l2y2); i++; _x[i] = (int) (info.l2x1); _y[i] = (int) (info.l2y1); i++; dst.setColor(b); dst.fillPolygon(_x, _y, i); } } void drawRails(Graphics dst, LineInfo info) { IlvRailroadAttributes ra = getRailroadAttributes(); dst.setColor(ra.getRailColor()); dst.drawLine((int) (info.l1x1), (int) (info.l1y1), (int) (info.l1x2), (int) (info.l1y2)); dst.drawLine((int) (info.l2x1), (int) (info.l2y1), (int) (info.l2x2), (int) (info.l2y2)); if (info.doJoin) dst.drawLine((int) (info.jx1), (int) (info.jy1), (int) (info.jx2), (int) (info.jy2)); } void drawCenterLine(Graphics dst, LineInfo info) { IlvRailroadAttributes ra = getRailroadAttributes(); Color c = ra.getCenterLineColor(); dst.setColor(c); float lw = ra.getCenterLineWidth(); boolean stroked = false; if (lw > 1.0) { if ((_basicStroke == null) || (_basicStroke.getLineWidth() != lw)) _basicStroke = new BasicStroke(lw); stroked = true; ((Graphics2D) dst).setStroke(_basicStroke); } dst.drawLine((int) (info.x1), (int) (info.y1), (int) (info.x2), (int) (info.y2)); if (stroked) ((Graphics2D) dst).setStroke(_resetStroke); } Override public void draw(Graphics dst, IlvTransformer t) { IlvPoint pts[] = getPoints(); if (pts.length < 2) return; IlvRailroadAttributes ra = getRailroadAttributes(); double railSpacing = getRailSpacing(t); double tieWidth = getTieWidth(t); boolean doRails = !ra.isDrawingOneLine(); if (railSpacing < 1f) doRails = false; boolean doTies = ra.isDrawingTies(); if (tieWidth < 1f) doTies = false; boolean doCenterLine = ra.isDoingCenterLine(); if (!doRails) { dst.setColor(ra.getRailColor()); IlvGraphicUtil.DrawPolyline(dst, pts, pts.length, 0, IlvStroke.JOIN_MITER, IlvStroke.CAP_BUTT, null, t); if (!doTies) return; } _current.create(pts, 0, t, railSpacing); if (pts.length == 2) { if (doRails) { drawBackground(dst, _current); if (doCenterLine) drawCenterLine(dst, _current); drawRails(dst, _current); } if (doTies) drawTies(dst, t, 0f, _current, null, null); return; } double offset = 0d; for (int i = 1; i < pts.length - 1; i++) { _next.create(pts, i, t, railSpacing); _current.intersect(_next); if (doRails) { drawBackground(dst, _current); if (doCenterLine) drawCenterLine(dst, _current); drawRails(dst, _current); } if (doTies) offset = drawTies(dst, t, offset, _current, i == 1 ? null : _prev, _next); _prev.affect(_current); _current.affect(_next); } if (doRails) { drawBackground(dst, _current); if (doCenterLine) drawCenterLine(dst, _current); drawRails(dst, _current); } if (doTies) drawTies(dst, t, offset, _current, _prev, null); } }