source: josm/trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/OffsetIterator.java@ 11696

Last change on this file since 11696 was 11696, checked in by michael2402, 7 years ago

Move offset iterator to new file.

File size: 5.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.visitor.paint;
3
4import java.util.Iterator;
5import java.util.List;
6import java.util.NoSuchElementException;
7
8import org.openstreetmap.josm.data.osm.Node;
9import org.openstreetmap.josm.gui.MapViewState;
10import org.openstreetmap.josm.gui.MapViewState.MapViewPoint;
11import org.openstreetmap.josm.tools.Utils;
12
13/**
14 * Iterates over a list of Way Nodes and returns screen coordinates that
15 * represent a line that is shifted by a certain offset perpendicular
16 * to the way direction.
17 *
18 * There is no intention, to handle consecutive duplicate Nodes in a
19 * perfect way, but it should not throw an exception.
20 * @since 11696 made public
21 */
22public class OffsetIterator implements Iterator<MapViewPoint> {
23 private final MapViewState mapState;
24 private final List<Node> nodes;
25 private final double offset;
26 private int idx;
27
28 private MapViewPoint prev;
29 /* 'prev0' is a point that has distance 'offset' from 'prev' and the
30 * line from 'prev' to 'prev0' is perpendicular to the way segment from
31 * 'prev' to the current point.
32 */
33 private double xPrev0;
34 private double yPrev0;
35
36 /**
37 * Creates a new offset iterator
38 * @param mapState The map view state this iterator is for.
39 * @param nodes The nodes of the original line
40 * @param offset The offset of the line.
41 */
42 public OffsetIterator(MapViewState mapState, List<Node> nodes, double offset) {
43 this.mapState = mapState;
44 this.nodes = nodes;
45 this.offset = offset;
46 idx = 0;
47 }
48
49 @Override
50 public boolean hasNext() {
51 return idx < nodes.size();
52 }
53
54 @Override
55 public MapViewPoint next() {
56 if (!hasNext())
57 throw new NoSuchElementException();
58
59 MapViewPoint current = getForIndex(idx);
60
61 if (Math.abs(offset) < 0.1d) {
62 idx++;
63 return current;
64 }
65
66 double xCurrent = current.getInViewX();
67 double yCurrent = current.getInViewY();
68 if (idx == nodes.size() - 1) {
69 ++idx;
70 if (prev != null) {
71 return mapState.getForView(xPrev0 + xCurrent - prev.getInViewX(),
72 yPrev0 + yCurrent - prev.getInViewY());
73 } else {
74 return current;
75 }
76 }
77
78 MapViewPoint next = getForIndex(idx + 1);
79 double dxNext = next.getInViewX() - xCurrent;
80 double dyNext = next.getInViewY() - yCurrent;
81 double lenNext = Math.sqrt(dxNext*dxNext + dyNext*dyNext);
82
83 if (lenNext < 1e-11) {
84 lenNext = 1; // value does not matter, because dy_next and dx_next is 0
85 }
86
87 // calculate the position of the translated current point
88 double om = offset / lenNext;
89 double xCurrent0 = xCurrent + om * dyNext;
90 double yCurrent0 = yCurrent - om * dxNext;
91
92 if (idx == 0) {
93 ++idx;
94 prev = current;
95 xPrev0 = xCurrent0;
96 yPrev0 = yCurrent0;
97 return mapState.getForView(xCurrent0, yCurrent0);
98 } else {
99 double dxPrev = xCurrent - prev.getInViewX();
100 double dyPrev = yCurrent - prev.getInViewY();
101 // determine intersection of the lines parallel to the two segments
102 double det = dxNext*dyPrev - dxPrev*dyNext;
103 double m = dxNext*(yCurrent0 - yPrev0) - dyNext*(xCurrent0 - xPrev0);
104
105 if (Utils.equalsEpsilon(det, 0) || Math.signum(det) != Math.signum(m)) {
106 ++idx;
107 prev = current;
108 xPrev0 = xCurrent0;
109 yPrev0 = yCurrent0;
110 return mapState.getForView(xCurrent0, yCurrent0);
111 }
112
113 double f = m / det;
114 if (f < 0) {
115 ++idx;
116 prev = current;
117 xPrev0 = xCurrent0;
118 yPrev0 = yCurrent0;
119 return mapState.getForView(xCurrent0, yCurrent0);
120 }
121 // the position of the intersection or intermittent point
122 double cx = xPrev0 + f * dxPrev;
123 double cy = yPrev0 + f * dyPrev;
124
125 if (f > 1) {
126 // check if the intersection point is too far away, this will happen for sharp angles
127 double dxI = cx - xCurrent;
128 double dyI = cy - yCurrent;
129 double lenISq = dxI * dxI + dyI * dyI;
130
131 if (lenISq > Math.abs(2 * offset * offset)) {
132 // intersection point is too far away, calculate intermittent points for capping
133 double dxPrev0 = xCurrent0 - xPrev0;
134 double dyPrev0 = yCurrent0 - yPrev0;
135 double lenPrev0 = Math.sqrt(dxPrev0 * dxPrev0 + dyPrev0 * dyPrev0);
136 f = 1 + Math.abs(offset / lenPrev0);
137 double cxCap = xPrev0 + f * dxPrev;
138 double cyCap = yPrev0 + f * dyPrev;
139 xPrev0 = cxCap;
140 yPrev0 = cyCap;
141 // calculate a virtual prev point which lies on a line that goes through current and
142 // is perpendicular to the line that goes through current and the intersection
143 // so that the next capping point is calculated with it.
144 double lenI = Math.sqrt(lenISq);
145 double xv = xCurrent + dyI / lenI;
146 double yv = yCurrent - dxI / lenI;
147
148 prev = mapState.getForView(xv, yv);
149 return mapState.getForView(cxCap, cyCap);
150 }
151 }
152 ++idx;
153 prev = current;
154 xPrev0 = xCurrent0;
155 yPrev0 = yCurrent0;
156 return mapState.getForView(cx, cy);
157 }
158 }
159
160 private MapViewPoint getForIndex(int i) {
161 return mapState.getPointFor(nodes.get(i));
162 }
163
164 @Override
165 public void remove() {
166 throw new UnsupportedOperationException();
167 }
168}
Note: See TracBrowser for help on using the repository browser.