svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.symbology / org.gvsig.symbology.lib / org.gvsig.symbology.lib.impl / src / main / java / org / gvsig / symbology / fmap / mapcontext / rendering / symbol / style / Line2DOffset.java @ 40560
History | View | Annotate | Download (20.5 KB)
1 | 40560 | jjdelcerro | /**
|
---|---|---|---|
2 | * gvSIG. Desktop Geographic Information System.
|
||
3 | 40435 | jjdelcerro | *
|
4 | 40560 | jjdelcerro | * Copyright (C) 2007-2013 gvSIG Association.
|
5 | 40435 | jjdelcerro | *
|
6 | * This program is free software; you can redistribute it and/or
|
||
7 | * modify it under the terms of the GNU General Public License
|
||
8 | 40560 | jjdelcerro | * as published by the Free Software Foundation; either version 3
|
9 | 40435 | jjdelcerro | * of the License, or (at your option) any later version.
|
10 | *
|
||
11 | * This program is distributed in the hope that it will be useful,
|
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
14 | * GNU General Public License for more details.
|
||
15 | *
|
||
16 | * You should have received a copy of the GNU General Public License
|
||
17 | * along with this program; if not, write to the Free Software
|
||
18 | 40560 | jjdelcerro | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 | * MA 02110-1301, USA.
|
||
20 | 40435 | jjdelcerro | *
|
21 | 40560 | jjdelcerro | * For any additional information, do not hesitate to contact us
|
22 | * at info AT gvsig.com, or visit our website www.gvsig.com.
|
||
23 | 40435 | jjdelcerro | */
|
24 | package org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style; |
||
25 | |||
26 | import java.awt.Shape; |
||
27 | import java.awt.geom.Line2D; |
||
28 | import java.awt.geom.PathIterator; |
||
29 | import java.awt.geom.Point2D; |
||
30 | import java.util.ArrayList; |
||
31 | import java.util.HashMap; |
||
32 | import java.util.List; |
||
33 | import java.util.Map; |
||
34 | |||
35 | import org.gvsig.fmap.geom.Geometry; |
||
36 | import org.gvsig.fmap.geom.primitive.GeneralPathX; |
||
37 | import org.slf4j.Logger; |
||
38 | import org.slf4j.LoggerFactory; |
||
39 | |||
40 | import com.vividsolutions.jts.geom.Coordinate; |
||
41 | import com.vividsolutions.jts.geom.LineSegment; |
||
42 | /**
|
||
43 | *
|
||
44 | * Line2DOffset.java
|
||
45 | *
|
||
46 | *
|
||
47 | * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 3, 2008
|
||
48 | *
|
||
49 | */
|
||
50 | |||
51 | public class Line2DOffset { |
||
52 | final static private Logger logger = LoggerFactory.getLogger(Line2DOffset.class); |
||
53 | |||
54 | public static GeneralPathX offsetLine(Shape p, double offset) { |
||
55 | |||
56 | if (Math.abs(offset) < 1) { |
||
57 | return new GeneralPathX(p.getPathIterator(null)); |
||
58 | } |
||
59 | PathIterator pi = p.getPathIterator(null); |
||
60 | double[] dataCoords = new double[6]; |
||
61 | Coordinate from = null, first = null; |
||
62 | List<LineSegment> segments = new ArrayList<LineSegment>(); |
||
63 | GeneralPathX offsetSegments = new GeneralPathX();
|
||
64 | LineSegment line; |
||
65 | try {
|
||
66 | while (!pi.isDone()) {
|
||
67 | // while not done
|
||
68 | int type = pi.currentSegment(dataCoords);
|
||
69 | |||
70 | switch (type) {
|
||
71 | case PathIterator.SEG_MOVETO: |
||
72 | if(from == null){ |
||
73 | from = new Coordinate(dataCoords[0], dataCoords[1]); |
||
74 | first = from; |
||
75 | break;
|
||
76 | } else {
|
||
77 | /* Puede significar un agujero en un pol?gono o un salto en una linea.
|
||
78 | * Entonces, consumimos los segmentos que llevamos
|
||
79 | * y empezamos un nuevo pol?gono o una nueva linea.
|
||
80 | */
|
||
81 | try {
|
||
82 | if(((Geometry)p).getType() == Geometry.TYPES.SURFACE){
|
||
83 | offsetSegments.append(offsetAndConsumeClosedSegments(offset, segments).getPathIterator(null),
|
||
84 | false);
|
||
85 | } else {
|
||
86 | offsetSegments.append(offsetAndConsumeSegments(offset, segments).getPathIterator(null),
|
||
87 | false);
|
||
88 | } |
||
89 | } catch (NotEnoughSegmentsToClosePathException e) {
|
||
90 | logger.error( |
||
91 | e.getMessage(), e); |
||
92 | } |
||
93 | segments.clear(); |
||
94 | from = new Coordinate(dataCoords[0], dataCoords[1]); |
||
95 | first = from; |
||
96 | break;
|
||
97 | } |
||
98 | |||
99 | case PathIterator.SEG_LINETO: |
||
100 | |||
101 | // System.out.println("SEG_LINETO");
|
||
102 | Coordinate to = new Coordinate(dataCoords[0], dataCoords[1]); |
||
103 | if(from.compareTo(to)!=0){ |
||
104 | line = new LineSegment(from, to);
|
||
105 | segments.add(line); |
||
106 | from = to; |
||
107 | } |
||
108 | break;
|
||
109 | case PathIterator.SEG_CLOSE: |
||
110 | line = new LineSegment(from, first);
|
||
111 | segments.add(line); |
||
112 | // from = first;
|
||
113 | try {
|
||
114 | offsetSegments.append(offsetAndConsumeClosedSegments( |
||
115 | offset, segments).getPathIterator(null), false); |
||
116 | } catch (NotEnoughSegmentsToClosePathException e) {
|
||
117 | logger.error( |
||
118 | e.getMessage(), e); |
||
119 | } |
||
120 | segments.clear(); |
||
121 | first =null;
|
||
122 | from = null;
|
||
123 | |||
124 | break;
|
||
125 | |||
126 | } // end switch
|
||
127 | |||
128 | pi.next(); |
||
129 | } |
||
130 | offsetSegments.append(offsetAndConsumeSegments(offset, segments) |
||
131 | .getPathIterator(null), false); |
||
132 | |||
133 | return offsetSegments;
|
||
134 | } catch (ParallelLinesCannotBeResolvedException e) {
|
||
135 | logger.error(e.getMessage(), e); |
||
136 | return new GeneralPathX(p.getPathIterator(null)); |
||
137 | } |
||
138 | } |
||
139 | |||
140 | private static GeneralPathX offsetAndConsumeSegments(double offset, |
||
141 | List<LineSegment> segments)
|
||
142 | throws ParallelLinesCannotBeResolvedException {
|
||
143 | Map<LineSegment, LineEquation> offsetLines =
|
||
144 | new HashMap<LineSegment, LineEquation>(); |
||
145 | int segmentCount = segments.size();
|
||
146 | // first calculate offset lines with the starting point
|
||
147 | for (int i = 0; i < segmentCount; i++) { |
||
148 | LineSegment segment = (LineSegment)segments.get(i); |
||
149 | double theta = segment.angle();
|
||
150 | //FIXME: ?Esto para qu? es?
|
||
151 | // if (Math.abs(theta) % (Math.PI*0.5) < 0.00001){
|
||
152 | // theta=theta+0.00000000000001;
|
||
153 | // }
|
||
154 | |||
155 | double xOffset = offset * Math.sin(theta); |
||
156 | double yOffset = offset * Math.cos(theta); |
||
157 | |||
158 | Coordinate p0 = segment.p0; |
||
159 | double x0 = p0.x + xOffset;
|
||
160 | double y0 = p0.y - yOffset;
|
||
161 | |||
162 | Coordinate p1 = segment.p1; |
||
163 | double x1 = p1.x + xOffset;
|
||
164 | double y1 = p1.y - yOffset;
|
||
165 | |||
166 | LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1);
|
||
167 | offsetLines.put(segment, offsetLine); |
||
168 | } |
||
169 | |||
170 | /*
|
||
171 | * let's now calculate the end point of each segment with the point
|
||
172 | * where each line crosses the next one. this point will be the end
|
||
173 | * point of the first line, and the start point of its next one.
|
||
174 | */
|
||
175 | Point2D pIni = null; |
||
176 | Point2D pEnd = null; |
||
177 | GeneralPathX gpx = new GeneralPathX();
|
||
178 | for (int i = 0; i < segmentCount; i++) { |
||
179 | LineSegment segment = (LineSegment)segments.get(0);
|
||
180 | LineEquation eq = (LineEquation)offsetLines.get(segment); |
||
181 | if (i == 0) { |
||
182 | pIni = new Point2D.Double(eq.x, eq.y); |
||
183 | } else {
|
||
184 | pIni = pEnd; |
||
185 | } |
||
186 | |||
187 | if (i < segmentCount - 1) { |
||
188 | LineEquation eq1 = (LineEquation) offsetLines.get(segments.get(1));
|
||
189 | try{
|
||
190 | pEnd = eq.resolve(eq1); |
||
191 | } catch (ParallelLinesCannotBeResolvedException e) { //Las lineas son paralelas y NO son la misma. |
||
192 | pEnd = new Point2D.Double(eq.xEnd, eq.yEnd); |
||
193 | gpx.append(new Line2D.Double(pIni, pEnd) |
||
194 | .getPathIterator(null), true); // a?adimos una linea |
||
195 | // hasta el final
|
||
196 | // del primer
|
||
197 | // segmento
|
||
198 | // y asignamos como punto final el principio del siguiente segmento
|
||
199 | // para que en la siguiente iteraci?n lo tome como punto inicial.
|
||
200 | pIni = pEnd; |
||
201 | pEnd = new Point2D.Double(eq1.x, eq1.y); |
||
202 | segments.remove(0);
|
||
203 | continue;
|
||
204 | } |
||
205 | } else {
|
||
206 | pEnd = new Point2D.Double(eq.xEnd, eq.yEnd); |
||
207 | } |
||
208 | |||
209 | gpx.append(new Line2D.Double(pIni, pEnd).getPathIterator(null), true); |
||
210 | segments.remove(0);
|
||
211 | } |
||
212 | return gpx;
|
||
213 | } |
||
214 | |||
215 | private static GeneralPathX offsetAndConsumeClosedSegments(double offset, |
||
216 | List<LineSegment> segments)
|
||
217 | throws ParallelLinesCannotBeResolvedException, NotEnoughSegmentsToClosePathException {
|
||
218 | int segmentCount = segments.size();
|
||
219 | if (segmentCount > 1) { |
||
220 | Map<LineSegment, LineEquation> offsetLines = new HashMap<LineSegment, LineEquation>(); |
||
221 | // first calculate offset lines with the starting point
|
||
222 | for (int i = 0; i < segmentCount; i++) { |
||
223 | LineSegment segment = (LineSegment)segments.get(i); |
||
224 | double theta = segment.angle();
|
||
225 | //FIXME: ?Esto para qu? es?
|
||
226 | // if (Math.abs(theta) % (Math.PI*0.5) < 0.00001){
|
||
227 | // theta=theta+0.00000000000001;
|
||
228 | // }
|
||
229 | |||
230 | double xOffset = offset * Math.sin(theta); |
||
231 | double yOffset = offset * Math.cos(theta); |
||
232 | |||
233 | Coordinate p0 = segment.p0; |
||
234 | double x0 = p0.x + xOffset;
|
||
235 | double y0 = p0.y - yOffset;
|
||
236 | |||
237 | Coordinate p1 = segment.p1; |
||
238 | double x1 = p1.x + xOffset;
|
||
239 | double y1 = p1.y - yOffset;
|
||
240 | |||
241 | LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1);
|
||
242 | offsetLines.put(segment, offsetLine); |
||
243 | } |
||
244 | |||
245 | /*
|
||
246 | * let's now calculate the end point of each segment with the point
|
||
247 | * where each line crosses the next one. this point will be the end
|
||
248 | * point of the first line, and the start point of its next one.
|
||
249 | */
|
||
250 | Point2D pIni = null; |
||
251 | Point2D pEnd = null; |
||
252 | Point2D firstP = null; |
||
253 | GeneralPathX gpx = new GeneralPathX();
|
||
254 | for (int i = 0; i < segmentCount; i++) { |
||
255 | LineSegment segment = (LineSegment)segments.get(0);
|
||
256 | LineEquation eq = offsetLines.get(segment); |
||
257 | if (i == 0) { //Calculo de la intersecci?n entre el primer segmento y el ?ltimo |
||
258 | LineEquation eq0 = offsetLines.get((LineSegment)segments.get(segmentCount-1));
|
||
259 | try{
|
||
260 | pIni = eq0.resolve(eq); |
||
261 | } catch (ParallelLinesCannotBeResolvedException e) { //Las lineas son paralelas y NO son la misma. |
||
262 | // pIni = new Point2D.Double(eq0.xEnd, eq0.yEnd);
|
||
263 | pIni = new Point2D.Double(eq.x, eq.y); |
||
264 | } |
||
265 | firstP = pIni; |
||
266 | } else {
|
||
267 | pIni = pEnd; |
||
268 | } |
||
269 | |||
270 | if (i < segmentCount - 1) { |
||
271 | LineEquation eq1 = offsetLines.get((LineSegment)segments.get(1));
|
||
272 | try{
|
||
273 | pEnd = eq.resolve(eq1); |
||
274 | } catch (ParallelLinesCannotBeResolvedException e) { //Las lineas son paralelas y NO son la misma. |
||
275 | pEnd = new Point2D.Double(eq.xEnd, eq.yEnd); |
||
276 | gpx.append(new Line2D.Double(pIni, pEnd).getPathIterator(null), true); // a?adimos una linea hasta el final del primer segmento |
||
277 | // y asignamos como punto final el principio del siguiente segmento
|
||
278 | // para que en la siguiente iteraci?n lo tome como punto inicial.
|
||
279 | pIni = pEnd; |
||
280 | pEnd = new Point2D.Double(eq1.x, eq1.y); |
||
281 | segments.remove(0);
|
||
282 | continue;
|
||
283 | } |
||
284 | } else {
|
||
285 | // pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
|
||
286 | pEnd = new Point2D.Double(firstP.getX(), firstP.getY()); |
||
287 | } |
||
288 | |||
289 | gpx.append(new Line2D.Double(pIni, pEnd).getPathIterator(null), true); |
||
290 | segments.remove(0);
|
||
291 | } |
||
292 | return gpx;
|
||
293 | } |
||
294 | throw new NotEnoughSegmentsToClosePathException(segments); |
||
295 | |||
296 | } |
||
297 | |||
298 | // private static GeneralPathX offsetAndConsumeClosedSegments(double offset,
|
||
299 | // List<LineSegment> segments)
|
||
300 | // throws ParallelLinesCannotBeResolvedException,
|
||
301 | // NotEnoughSegmentsToClosePathException {
|
||
302 | // int segmentCount = segments.size();
|
||
303 | // if (segmentCount > 1) {
|
||
304 | // GeneralPathX openPath = offsetAndConsumeSegments(offset, segments);
|
||
305 | // openPath.closePath();
|
||
306 | // return openPath;
|
||
307 | // }
|
||
308 | // throw new NotEnoughSegmentsToClosePathException(segments);
|
||
309 | // }
|
||
310 | } |
||
311 | |||
312 | class LineEquation { |
||
313 | double theta, m, x, y;
|
||
314 | |||
315 | double xEnd, yEnd; // just for simplicity of code |
||
316 | |||
317 | public LineEquation(double theta, double x, double y, double xEnd, |
||
318 | double yEnd) {
|
||
319 | // this.theta = Math.tan(theta); //Esto es un error, no podemos confundir el angulo de la recta con su pendiente
|
||
320 | this.theta = theta;
|
||
321 | this.m = Math.tan(theta); |
||
322 | this.x = x;
|
||
323 | this.y = y;
|
||
324 | this.xEnd = xEnd;
|
||
325 | this.yEnd = yEnd;
|
||
326 | } |
||
327 | |||
328 | public Point2D resolve(LineEquation otherLine) |
||
329 | throws ParallelLinesCannotBeResolvedException {
|
||
330 | /*
|
||
331 | * line1 (this): y - y0 = m*(x - x0)
|
||
332 | * line2 (otherLine): y' - y'0 = m'*(x' - x'0)
|
||
333 | */
|
||
334 | |||
335 | double X;
|
||
336 | double Y;
|
||
337 | if(Math.abs(this.x - this.xEnd)<0.00001) { //Esta linea es vertical |
||
338 | // System.out.println("1 VERTICAL");
|
||
339 | X = this.xEnd;
|
||
340 | if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical |
||
341 | // System.out.println(" 2 PERPENDICULAR");
|
||
342 | if(Math.abs(this.x - otherLine.x)<0.00001){ //Son la misma linea, devolvemos el primer punto de la otra linea. |
||
343 | // System.out.println(" MISMA LINEA");
|
||
344 | Y = otherLine.y; |
||
345 | } else { //No son la misma linea, entonces son paralelas, excepcion. |
||
346 | // System.out.println(" CASCO POR 1");
|
||
347 | throw new ParallelLinesCannotBeResolvedException(this, otherLine); |
||
348 | } |
||
349 | } else if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal |
||
350 | // System.out.println(" 2 HORIZONTAL");
|
||
351 | Y = otherLine.y; |
||
352 | } else { //Si no |
||
353 | // System.out.println(" 2 CUALQUIERA");
|
||
354 | Y = otherLine.m*(X - otherLine.x)+otherLine.y; |
||
355 | } |
||
356 | |||
357 | } else if (Math.abs(this.y - this.yEnd)<0.00001) { //Esta linea es horizontal |
||
358 | // System.out.println("1 HORIZONTAL");
|
||
359 | Y = this.yEnd;
|
||
360 | if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal |
||
361 | // System.out.println(" 2 HORIZONTAL");
|
||
362 | if(Math.abs(this.y - otherLine.y)<0.00001){ //Son la misma linea, devolvemos el primer punto de la otra linea. |
||
363 | // System.out.println(" MISMA LINEA");
|
||
364 | X = otherLine.x; |
||
365 | } else { //No son la misma linea, entonces son paralelas, excepcion. |
||
366 | // System.out.println(" CASCO POR 2");
|
||
367 | throw new ParallelLinesCannotBeResolvedException(this, otherLine); |
||
368 | } |
||
369 | } else if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical |
||
370 | // System.out.println(" 2 VERTICAL");
|
||
371 | X = otherLine.x; |
||
372 | } else { //Si no |
||
373 | // System.out.println(" 2 CUALQUIERA");
|
||
374 | X = (Y - otherLine.y)/otherLine.m +otherLine.x; |
||
375 | } |
||
376 | } else { //Esta linea no es ni vertical ni horizontal |
||
377 | // System.out.println("1 CUALQUIERA");
|
||
378 | if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal |
||
379 | // System.out.println(" 2 HORIZONTAL");
|
||
380 | Y = otherLine.y; |
||
381 | X = (Y - this.y)/this.m +this.x; |
||
382 | } else if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical |
||
383 | // System.out.println(" 2 VERTICAL");
|
||
384 | X = otherLine.x; |
||
385 | Y = this.m*(X - this.x)+this.y; |
||
386 | } else if ((Math.abs(otherLine.m - this.m)<0.00001)) { //Tienen la misma pendiente |
||
387 | // System.out.println(" MISMA PENDIENTE");
|
||
388 | Y = otherLine.m*(this.x - otherLine.x)+otherLine.y;
|
||
389 | if (Math.abs(this.y - Y)<0.00001){ //Las lineas son la misma |
||
390 | // System.out.println(" MISMA LINEA");
|
||
391 | X = otherLine.x; |
||
392 | Y = otherLine.y; |
||
393 | } else {
|
||
394 | // System.out.println(" CASCO POR 3");
|
||
395 | throw new ParallelLinesCannotBeResolvedException(this, otherLine); |
||
396 | } |
||
397 | } else {
|
||
398 | // System.out.println(" AMBAS CUALESQUIERA");
|
||
399 | double mTimesX = this.m * this.x; |
||
400 | X = (mTimesX - this.y - otherLine.m * otherLine.x + otherLine.y) / (this.m - otherLine.m); |
||
401 | Y = this.m * X - mTimesX + this.y; |
||
402 | } |
||
403 | } |
||
404 | |||
405 | // System.out.println("DEVOLVEMOS X = "+X+" Y = "+Y);
|
||
406 | return new Point2D.Double(X, Y); |
||
407 | |||
408 | } |
||
409 | |||
410 | public String toString() { |
||
411 | return "Y - " + y + " = " + m + "*(X - " + x + ")"; |
||
412 | } |
||
413 | } |
||
414 | |||
415 | class NotEnoughSegmentsToClosePathException extends Exception { |
||
416 | private static final long serialVersionUID = 95503944546535L; |
||
417 | |||
418 | public NotEnoughSegmentsToClosePathException(List<LineSegment> segments) { |
||
419 | super("Need at least 2 segments to close a path. I've got " |
||
420 | + segments.size() + ".");
|
||
421 | } |
||
422 | } |
||
423 | |||
424 | class ParallelLinesCannotBeResolvedException extends Exception { |
||
425 | private static final long serialVersionUID = 8322556508820067641L; |
||
426 | |||
427 | public ParallelLinesCannotBeResolvedException(LineEquation eq1,
|
||
428 | LineEquation eq2) { |
||
429 | super("Lines '" + eq1 + "' and '" + eq2 |
||
430 | + "' are parallel and don't share any point!");
|
||
431 | } |
||
432 | } |
||
433 | //public class Line2DOffset {
|
||
434 | |||
435 | //private static final double TOL = 1E-8;
|
||
436 | //private static final double ANGLE_TOL = 0.01/180*Math.PI;
|
||
437 | |||
438 | //public static GeneralPathX offsetLine(Shape p, double offset) {
|
||
439 | |||
440 | //PathIterator pi = p.getPathIterator(null);
|
||
441 | //double[] dataCoords = new double[6];
|
||
442 | //Coordinate from = null, first = null;
|
||
443 | //ArrayList<LineSegment> segments = new ArrayList<LineSegment>();
|
||
444 | //GeneralPathX offsetSegments = new GeneralPathX();
|
||
445 | //try {
|
||
446 | //while (!pi.isDone()) {
|
||
447 | //// while not done
|
||
448 | //int type = pi.currentSegment(dataCoords);
|
||
449 | |||
450 | //switch (type) {
|
||
451 | //case PathIterator.SEG_MOVETO:
|
||
452 | //from = new Coordinate(dataCoords[0], dataCoords[1]);
|
||
453 | //first = from;
|
||
454 | //break;
|
||
455 | |||
456 | //case PathIterator.SEG_LINETO:
|
||
457 | |||
458 | //// System.out.println("SEG_LINETO");
|
||
459 | //Coordinate to = new Coordinate(dataCoords[0], dataCoords[1]);
|
||
460 | //LineSegment line = new LineSegment(from, to);
|
||
461 | //int size = segments.size();
|
||
462 | //if (size>0) {
|
||
463 | //LineSegment prev = segments.get(size-1);
|
||
464 | //if (line.angle() == prev.angle()) {
|
||
465 | //if (Math.abs(line.p0.x - prev.p1.x) < TOL &&
|
||
466 | //Math.abs(line.p0.y - prev.p1.y) < TOL) {
|
||
467 | //prev.p1 = line.p1;
|
||
468 | //break;
|
||
469 | //}
|
||
470 | //}
|
||
471 | //}
|
||
472 | //from = to;
|
||
473 | //segments.add(line);
|
||
474 | |||
475 | //break;
|
||
476 | //case PathIterator.SEG_CLOSE:
|
||
477 | //line = new LineSegment(from, first);
|
||
478 | //segments.add(line);
|
||
479 | //from = first;
|
||
480 | //try {
|
||
481 | //offsetSegments.append(offsetAndConsumeClosedSegments(offset, segments), false);
|
||
482 | //} catch (NotEnoughSegmentsToClosePathException e) {
|
||
483 | //Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
|
||
484 | //}
|
||
485 | //break;
|
||
486 | |||
487 | //} // end switch
|
||
488 | |||
489 | //pi.next();
|
||
490 | //}
|
||
491 | //offsetSegments.append(offsetAndConsumeSegments(offset, segments), true);
|
||
492 | |||
493 | //return offsetSegments;
|
||
494 | //} catch (ParallelLinesCannotBeResolvedException e) {
|
||
495 | //Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
|
||
496 | //return new GeneralPathX(p);
|
||
497 | //}
|
||
498 | //}
|
||
499 | |||
500 | //private static GeneralPathX offsetAndConsumeSegments(double offset, ArrayList<LineSegment> segments) {
|
||
501 | //Hashtable<LineSegment, LineEquation> offsetLines = new Hashtable<LineSegment, LineEquation>();
|
||
502 | //int segmentCount = segments.size();
|
||
503 | //// first calculate offset lines with the starting point
|
||
504 | //for (int i = 0; i < segmentCount; i++) {
|
||
505 | //LineSegment segment = segments.get(i);
|
||
506 | //double theta = segment.angle();
|
||
507 | |||
508 | //double xOffset = offset*Math.sin(theta);
|
||
509 | //double yOffset = offset*Math.cos(theta);
|
||
510 | |||
511 | //Coordinate p0 = segment.p0;
|
||
512 | //double x0 = p0.x + xOffset;
|
||
513 | //double y0 = p0.y - yOffset;
|
||
514 | |||
515 | //Coordinate p1 = segment.p1;
|
||
516 | //double x1 = p1.x + xOffset;
|
||
517 | //double y1 = p1.y - yOffset;
|
||
518 | |||
519 | //LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1, offset);
|
||
520 | //offsetLines.put(segment, offsetLine);
|
||
521 | //}
|
||
522 | |||
523 | ///*
|
||
524 | //* let's now calculate the end point of each segment with
|
||
525 | //* the point where each line crosses the next one.
|
||
526 | //* this point will be the end point of the first line, and
|
||
527 | //* the start point of its next one.
|
||
528 | //*/
|
||
529 | //Point2D pIni = null;
|
||
530 | //Point2D pEnd = null;
|
||
531 | //GeneralPathX gpx = new GeneralPathX();
|
||
532 | //for (int i = 0; i < segmentCount; i++) {
|
||
533 | //LineSegment segment = segments.get(0);
|
||
534 | //LineEquation eq = offsetLines.get(segment);
|
||
535 | //Point2D pAux = null;
|
||
536 | //if (i < segmentCount -1) {
|
||
537 | //try {
|
||
538 | //pAux = eq.resolve(offsetLines.get(segments.get(1)));
|
||
539 | //if (i == 0) {
|
||
540 | //pIni = new Point2D.Double(eq.x, eq.y);
|
||
541 | //} else {
|
||
542 | //pIni = pEnd;
|
||
543 | //}
|
||
544 | //} catch (ParallelLinesCannotBeResolvedException e) {
|
||
545 | //segments.remove(0);
|
||
546 | //continue;
|
||
547 | //}
|
||
548 | //}
|
||
549 | |||
550 | |||
551 | //if (pAux != null) {
|
||
552 | //pEnd = pAux;
|
||
553 | //} else {
|
||
554 | //pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
|
||
555 | //}
|
||
556 | |||
557 | //gpx.append(new Line2D.Double(pIni, pEnd), true);
|
||
558 | //segments.remove(0);
|
||
559 | //}
|
||
560 | //return gpx;
|
||
561 | //}
|
||
562 | |||
563 | //private static GeneralPathX offsetAndConsumeClosedSegments(double offset, ArrayList<LineSegment> segments) throws ParallelLinesCannotBeResolvedException, NotEnoughSegmentsToClosePathException {
|
||
564 | //int segmentCount = segments.size();
|
||
565 | //if (segmentCount > 1) {
|
||
566 | //GeneralPathX openPath = offsetAndConsumeSegments(offset, segments);
|
||
567 | //openPath.closePath();
|
||
568 | //return openPath;
|
||
569 | //}
|
||
570 | //throw new NotEnoughSegmentsToClosePathException(segments);
|
||
571 | //}
|
||
572 | //}
|
||
573 | |||
574 | //class LineEquation {
|
||
575 | //double theta, x, y;
|
||
576 | //double xEnd, yEnd; // just for simplicity of code
|
||
577 | //double offset;
|
||
578 | |||
579 | //public LineEquation(double theta, double x, double y, double xEnd, double yEnd, double offset) {
|
||
580 | //this.theta = theta;
|
||
581 | //this.x = x;
|
||
582 | //this.y = y;
|
||
583 | //this.xEnd = xEnd;
|
||
584 | //this.yEnd = yEnd;
|
||
585 | //this.offset = offset;
|
||
586 | //}
|
||
587 | |||
588 | //public Point2D resolve(LineEquation otherLine) throws ParallelLinesCannotBeResolvedException {
|
||
589 | //double X;
|
||
590 | //double Y;
|
||
591 | |||
592 | |||
593 | ///*
|
||
594 | //* line1 (this): y - y0 = m*(x - x0)
|
||
595 | //* line2 (otherLine): y' - y'0 = m'*(x' - x'0)
|
||
596 | //*/
|
||
597 | //if (otherLine.theta == this.theta)
|
||
598 | //throw new ParallelLinesCannotBeResolvedException(this, otherLine);
|
||
599 | |||
600 | //if (Math.cos(theta) == 0) {
|
||
601 | |||
602 | //X = otherLine.x + offset*Math.cos(otherLine.theta);
|
||
603 | //Y = otherLine.y + offset*Math.sin(otherLine.theta);
|
||
604 | //} else if (Math.cos(otherLine.theta) == 0) {
|
||
605 | //X = x + offset*Math.cos(theta);
|
||
606 | //Y = y + offset*Math.sin(theta);
|
||
607 | //} else {
|
||
608 | ///*
|
||
609 | //* m*(X - x0) + y0 = m'*(X - x'0) + y0'
|
||
610 | //* X = (m*x0 - y0 - m'*x0' + y'0) / (m - m')
|
||
611 | //*/
|
||
612 | //double tanTheta = Math.tan(theta);
|
||
613 | //double otherTanTheta = Math.tan(otherLine.theta);
|
||
614 | //double thetaTimesX = tanTheta*this.x;
|
||
615 | //X = (thetaTimesX - this.y - otherTanTheta*otherLine.x + otherLine.y) / (tanTheta - otherTanTheta);
|
||
616 | |||
617 | ///*
|
||
618 | //* Y - y0 = m*(X - x0)
|
||
619 | //* Y = m*X - m*x0 + y0
|
||
620 | //*/
|
||
621 | //Y = tanTheta*X - thetaTimesX + this.y;
|
||
622 | //}
|
||
623 | //return new Point2D.Double(X, Y);
|
||
624 | //}
|
||
625 | |||
626 | //@Override
|
||
627 | //public String toString() {
|
||
628 | //return "Y - "+y+" = "+theta+"*(X - "+x+")";
|
||
629 | //}
|
||
630 | //}
|
||
631 | |||
632 | //class NotEnoughSegmentsToClosePathException extends Exception {
|
||
633 | //private static final long serialVersionUID = 95503944546535L;
|
||
634 | //public NotEnoughSegmentsToClosePathException(ArrayList<LineSegment> segments) {
|
||
635 | //super("Need at least 2 segments to close a path. I've got "+segments.size()+".");
|
||
636 | //}
|
||
637 | //}
|
||
638 | |||
639 | //class ParallelLinesCannotBeResolvedException extends Exception {
|
||
640 | //private static final long serialVersionUID = 8322556508820067641L;
|
||
641 | |||
642 | //public ParallelLinesCannotBeResolvedException(LineEquation eq1, LineEquation eq2) {
|
||
643 | //super("Lines '"+eq1+"' and '"+eq2+"' are parallel and don't share any point!");
|
||
644 | //}
|
||
645 | //} |