Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / core / styles / Line2DOffset.java @ 28217

History | View | Annotate | Download (15.6 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2005 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
package com.iver.cit.gvsig.fmap.core.styles;
42

    
43
import java.awt.Shape;
44
import java.awt.geom.Line2D;
45
import java.awt.geom.PathIterator;
46
import java.awt.geom.Point2D;
47
import java.util.ArrayList;
48
import java.util.Hashtable;
49

    
50
import org.apache.log4j.Logger;
51

    
52
import com.iver.cit.gvsig.fmap.core.GeneralPathX;
53
import com.vividsolutions.jts.geom.Coordinate;
54
import com.vividsolutions.jts.geom.LineSegment;
55
/**
56
 *
57
 * Line2DOffset.java
58
 *
59
 *
60
 * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 3, 2008
61
 *
62
 */
63

    
64
public class Line2DOffset {
65

    
66
        public static GeneralPathX offsetLine(Shape p, double offset) {
67

    
68
                if (Math.abs(offset) < 1) {
69
                        return new GeneralPathX(p);
70
                }
71
                PathIterator pi = p.getPathIterator(null);
72
                double[] dataCoords = new double[6];
73
                Coordinate from = null, first = null;
74
                ArrayList<LineSegment> segments = new ArrayList<LineSegment>();
75
                GeneralPathX offsetSegments = new GeneralPathX();
76
                LineSegment line;
77
                try {
78
                        while (!pi.isDone()) {
79
                                // while not done
80
                                int type = pi.currentSegment(dataCoords);
81

    
82
                                switch (type) {
83
                                case PathIterator.SEG_MOVETO:
84
                                        from = new Coordinate(dataCoords[0], dataCoords[1]);
85
                                        first = from;
86
                                        break;
87

    
88
                                case PathIterator.SEG_LINETO:
89

    
90
                                        // System.out.println("SEG_LINETO");
91
                                        Coordinate to = new Coordinate(dataCoords[0], dataCoords[1]);
92
                                        if(from.compareTo(to)!=0){
93
                                                line = new LineSegment(from, to);
94
                                                segments.add(line);
95
                                                from = to;
96
                                        }
97
                                        break;
98
                                case PathIterator.SEG_CLOSE:
99
                                        line = new LineSegment(from, first);
100
                                        segments.add(line);
101
                                        from = first;
102
                                        try {
103
                                                offsetSegments.append(offsetAndConsumeClosedSegments(
104
                                                                offset, segments), false);
105
                                        } catch (NotEnoughSegmentsToClosePathException e) {
106
                                                Logger.getLogger(Line2DOffset.class).error(
107
                                                                e.getMessage(), e);
108
                                        }
109
                                        break;
110

    
111
                                } // end switch
112

    
113
                                pi.next();
114
                        }
115
                        offsetSegments.append(offsetAndConsumeSegments(offset, segments),
116
                                        true);
117

    
118
                        return offsetSegments;
119
                } catch (ParallelLinesCannotBeResolvedException e) {
120
                        Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
121
                        return new GeneralPathX(p);
122
                }
123
        }
124

    
125
        private static GeneralPathX offsetAndConsumeSegments(double offset,
126
                        ArrayList<LineSegment> segments)
127
        throws ParallelLinesCannotBeResolvedException {
128
                Hashtable<LineSegment, LineEquation> offsetLines = new Hashtable<LineSegment, LineEquation>();
129
                int segmentCount = segments.size();
130
                // first calculate offset lines with the starting point
131
                for (int i = 0; i < segmentCount; i++) {
132
                        LineSegment segment = segments.get(i);
133
                        double theta = segment.angle();
134
                        //FIXME: ?Esto para qu? es?
135
//                        if (Math.abs(theta) % (Math.PI*0.5) < 0.00001){
136
//                                theta=theta+0.00000000000001;
137
//                        }
138

    
139
                        double xOffset = offset * Math.sin(theta);
140
                        double yOffset = offset * Math.cos(theta);
141

    
142
                        Coordinate p0 = segment.p0;
143
                        double x0 = p0.x + xOffset;
144
                        double y0 = p0.y - yOffset;
145

    
146
                        Coordinate p1 = segment.p1;
147
                        double x1 = p1.x + xOffset;
148
                        double y1 = p1.y - yOffset;
149

    
150
                        LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1);
151
                        offsetLines.put(segment, offsetLine);
152
                }
153

    
154
                /*
155
                 * let's now calculate the end point of each segment with the point
156
                 * where each line crosses the next one. this point will be the end
157
                 * point of the first line, and the start point of its next one.
158
                 */
159
                Point2D pIni = null;
160
                Point2D pEnd = null;
161
                GeneralPathX gpx = new GeneralPathX();
162
                for (int i = 0; i < segmentCount; i++) {
163
                        LineSegment segment = segments.get(0);
164
                        LineEquation eq = offsetLines.get(segment);
165
                        if (i == 0) {
166
                                pIni = new Point2D.Double(eq.x, eq.y);
167
                        } else {
168
                                pIni = pEnd;
169
                        }
170

    
171
                        if (i < segmentCount - 1) {
172
                                LineEquation eq1 = offsetLines.get(segments.get(1));
173
                                try{
174
                                        pEnd = eq.resolve(eq1);
175
                                } catch (ParallelLinesCannotBeResolvedException e) { //Las lineas son paralelas y NO son la misma.
176
                                        pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
177
                                        gpx.append(new Line2D.Double(pIni, pEnd), true); // a?adimos una linea hasta el final del primer segmento
178
//                                         y asignamos como punto final el principio del siguiente segmento
179
//                                         para que en la siguiente iteraci?n lo tome como punto inicial.
180
                                        pIni = pEnd;
181
                                        pEnd = new Point2D.Double(eq1.x, eq1.y);
182
                                        segments.remove(0);
183
                                        continue;
184
                                }
185
                        } else {
186
                                pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
187
                        }
188

    
189
                        gpx.append(new Line2D.Double(pIni, pEnd), true);
190
                        segments.remove(0);
191
                }
192
                return gpx;
193
        }
194

    
195
        private static GeneralPathX offsetAndConsumeClosedSegments(double offset,
196
                        ArrayList<LineSegment> segments)
197
        throws ParallelLinesCannotBeResolvedException,
198
        NotEnoughSegmentsToClosePathException {
199
                int segmentCount = segments.size();
200
                if (segmentCount > 1) {
201
                        GeneralPathX openPath = offsetAndConsumeSegments(offset, segments);
202
                        openPath.closePath();
203
                        return openPath;
204
                }
205
                throw new NotEnoughSegmentsToClosePathException(segments);
206
        }
207
}
208

    
209
class LineEquation {
210
        double theta, m, x, y;
211

    
212
        double xEnd, yEnd; // just for simplicity of code
213

    
214
        public LineEquation(double theta, double x, double y, double xEnd,
215
                        double yEnd) {
216
//                this.theta = Math.tan(theta); //Esto es un error, no podemos confundir el angulo de la recta con su pendiente
217
                this.theta = theta;
218
                this.m = Math.tan(theta);
219
                this.x = x;
220
                this.y = y;
221
                this.xEnd = xEnd;
222
                this.yEnd = yEnd;
223
        }
224

    
225
        public Point2D resolve(LineEquation otherLine)
226
        throws ParallelLinesCannotBeResolvedException {
227
                /*
228
                 * line1 (this): y - y0 = m*(x - x0)
229
                 * line2 (otherLine): y' - y'0 = m'*(x' - x'0)
230
                 */
231

    
232
                double X;
233
                double Y;
234
                if(Math.abs(this.x - this.xEnd)<0.00001) { //Esta linea es vertical
235
                        X = this.xEnd;
236
                        if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical
237
                                if(Math.abs(this.x - otherLine.x)<0.00001){ //Son la misma linea, devolvemos el primer punto de la otra linea.
238
                                        Y = otherLine.y;
239
                                } else { //No son la misma linea, entonces son paralelas, excepcion.
240
                                        throw new ParallelLinesCannotBeResolvedException(this, otherLine);
241
                                }
242
                        } else if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal
243
                                Y = otherLine.y;
244
                        } else { //Si la otra linea no es ni vertical ni horizontal
245
                                Y = otherLine.m*(X - otherLine.x)+otherLine.y;
246
                        }
247

    
248
                } else if (Math.abs(this.y - this.yEnd)<0.00001) { //Esta linea es horizontal
249
                        Y = this.yEnd;
250
                        if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal
251
                                if(Math.abs(this.y - otherLine.y)<0.00001){ //Son la misma linea, devolvemos el primer punto de la otra linea.
252
                                        X = otherLine.x;
253
                                } else { //No son la misma linea, entonces son paralelas, excepcion.
254
                                        throw new ParallelLinesCannotBeResolvedException(this, otherLine);
255
                                }
256
                        } else if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical
257
                                X = otherLine.x;
258
                        } else { //Si la otra linea no es ni vertical ni horizontal
259
                                X = (Y - otherLine.y)/otherLine.m +otherLine.x;
260
                        }
261
                } else { //Esta linea no es ni vertical ni horizontal
262
                        if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal
263
                                Y = otherLine.y;
264
                                X = (Y - this.y)/this.m +this.x;
265
                        } else if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical
266
                                X = otherLine.x;
267
                                Y = this.m*(X - this.x)+this.y;
268
                        } else if ((Math.abs(otherLine.m - this.m)<0.00001)) { //Tienen la misma pendiente
269
                                Y = otherLine.m*(this.x - otherLine.x)+otherLine.y;
270
                                if (Math.abs(this.y - Y)<0.00001){ //Las lineas son la misma
271
                                        X = otherLine.x;
272
                                        Y = otherLine.y;
273
                                } else {
274
                                        throw new ParallelLinesCannotBeResolvedException(this, otherLine);
275
                                }
276
                        } else { //Si la otra linea no es ni vertical ni horizontal
277
                                double mTimesX = this.m * this.x;
278
                                X = (mTimesX - this.y - otherLine.m * otherLine.x + otherLine.y) / (this.m - otherLine.m);
279
                                Y = this.m * X - mTimesX + this.y;
280
                        }
281
                }
282
                return new Point2D.Double(X, Y);
283
        }
284

    
285
        @Override
286
        public String toString() {
287
                return "Y - " + y + " = " + m + "*(X - " + x + ")";
288
        }
289
}
290

    
291
class NotEnoughSegmentsToClosePathException extends Exception {
292
        private static final long serialVersionUID = 95503944546535L;
293

    
294
        public NotEnoughSegmentsToClosePathException(ArrayList<LineSegment> segments) {
295
                super("Need at least 2 segments to close a path. I've got "
296
                                + segments.size() + ".");
297
        }
298
}
299

    
300
class ParallelLinesCannotBeResolvedException extends Exception {
301
        private static final long serialVersionUID = 8322556508820067641L;
302

    
303
        public ParallelLinesCannotBeResolvedException(LineEquation eq1,
304
                        LineEquation eq2) {
305
                super("Lines '" + eq1 + "' and '" + eq2
306
                                + "' are parallel and don't share any point!");
307
        }
308
}
309
//public class Line2DOffset {
310

    
311
//private static final double TOL = 1E-8;
312
//private static final double ANGLE_TOL = 0.01/180*Math.PI;
313

    
314
//public static GeneralPathX offsetLine(Shape p, double offset) {
315

    
316
//PathIterator pi = p.getPathIterator(null);
317
//double[] dataCoords = new double[6];
318
//Coordinate from = null, first = null;
319
//ArrayList<LineSegment> segments = new ArrayList<LineSegment>();
320
//GeneralPathX offsetSegments = new GeneralPathX();
321
//try {
322
//while (!pi.isDone()) {
323
//// while not done
324
//int type = pi.currentSegment(dataCoords);
325

    
326
//switch (type) {
327
//case PathIterator.SEG_MOVETO:
328
//from = new Coordinate(dataCoords[0], dataCoords[1]);
329
//first = from;
330
//break;
331

    
332
//case PathIterator.SEG_LINETO:
333

    
334
//// System.out.println("SEG_LINETO");
335
//Coordinate to = new Coordinate(dataCoords[0], dataCoords[1]);
336
//LineSegment line = new LineSegment(from, to);
337
//int size = segments.size();
338
//if (size>0) {
339
//LineSegment prev = segments.get(size-1);
340
//if (line.angle() == prev.angle()) {
341
//if (Math.abs(line.p0.x - prev.p1.x) < TOL &&
342
//Math.abs(line.p0.y - prev.p1.y) < TOL) {
343
//prev.p1 = line.p1;
344
//break;
345
//}
346
//}
347
//}
348
//from = to;
349
//segments.add(line);
350

    
351
//break;
352
//case PathIterator.SEG_CLOSE:
353
//line = new LineSegment(from, first);
354
//segments.add(line);
355
//from = first;
356
//try {
357
//offsetSegments.append(offsetAndConsumeClosedSegments(offset, segments), false);
358
//} catch (NotEnoughSegmentsToClosePathException e) {
359
//Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
360
//}
361
//break;
362

    
363
//} // end switch
364

    
365
//pi.next();
366
//}
367
//offsetSegments.append(offsetAndConsumeSegments(offset, segments), true);
368

    
369
//return offsetSegments;
370
//} catch (ParallelLinesCannotBeResolvedException e) {
371
//Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
372
//return new GeneralPathX(p);
373
//}
374
//}
375

    
376
//private static GeneralPathX offsetAndConsumeSegments(double offset, ArrayList<LineSegment> segments)  {
377
//Hashtable<LineSegment, LineEquation> offsetLines = new Hashtable<LineSegment, LineEquation>();
378
//int segmentCount = segments.size();
379
//// first calculate offset lines with the starting point
380
//for (int i = 0; i < segmentCount; i++) {
381
//LineSegment segment = segments.get(i);
382
//double theta = segment.angle();
383

    
384
//double xOffset = offset*Math.sin(theta);
385
//double yOffset = offset*Math.cos(theta);
386

    
387
//Coordinate p0 = segment.p0;
388
//double x0 = p0.x + xOffset;
389
//double y0 = p0.y - yOffset;
390

    
391
//Coordinate p1 = segment.p1;
392
//double x1 = p1.x + xOffset;
393
//double y1 = p1.y - yOffset;
394

    
395
//LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1, offset);
396
//offsetLines.put(segment, offsetLine);
397
//}
398

    
399
///*
400
//* let's now calculate the end point of each segment with
401
//* the point where each line crosses the next one.
402
//* this point will be the end point of the first line, and
403
//* the start point of its next one.
404
//*/
405
//Point2D pIni = null;
406
//Point2D pEnd = null;
407
//GeneralPathX gpx = new GeneralPathX();
408
//for (int i = 0; i < segmentCount; i++) {
409
//LineSegment segment = segments.get(0);
410
//LineEquation eq = offsetLines.get(segment);
411
//Point2D pAux = null;
412
//if (i < segmentCount -1) {
413
//try {
414
//pAux = eq.resolve(offsetLines.get(segments.get(1)));
415
//if (i == 0) {
416
//pIni = new Point2D.Double(eq.x, eq.y);
417
//} else {
418
//pIni = pEnd;
419
//}
420
//} catch (ParallelLinesCannotBeResolvedException e) {
421
//segments.remove(0);
422
//continue;
423
//}
424
//}
425

    
426

    
427
//if (pAux != null) {
428
//pEnd = pAux;
429
//} else {
430
//pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
431
//}
432

    
433
//gpx.append(new Line2D.Double(pIni, pEnd), true);
434
//segments.remove(0);
435
//}
436
//return gpx;
437
//}
438

    
439
//private static GeneralPathX offsetAndConsumeClosedSegments(double offset, ArrayList<LineSegment> segments) throws ParallelLinesCannotBeResolvedException, NotEnoughSegmentsToClosePathException {
440
//int segmentCount = segments.size();
441
//if (segmentCount > 1) {
442
//GeneralPathX openPath = offsetAndConsumeSegments(offset, segments);
443
//openPath.closePath();
444
//return openPath;
445
//}
446
//throw new NotEnoughSegmentsToClosePathException(segments);
447
//}
448
//}
449

    
450
//class LineEquation {
451
//double theta, x, y;
452
//double xEnd, yEnd; // just for simplicity of code
453
//double offset;
454

    
455
//public LineEquation(double theta, double x, double y, double xEnd, double yEnd, double offset) {
456
//this.theta = theta;
457
//this.x = x;
458
//this.y = y;
459
//this.xEnd = xEnd;
460
//this.yEnd = yEnd;
461
//this.offset = offset;
462
//}
463

    
464
//public Point2D resolve(LineEquation otherLine) throws ParallelLinesCannotBeResolvedException {
465
//double X;
466
//double Y;
467

    
468

    
469
///*
470
//* line1 (this):      y  -  y0 =  m*(x  - x0)
471
//* line2 (otherLine): y' - y'0 = m'*(x' - x'0)
472
//*/
473
//if (otherLine.theta == this.theta)
474
//throw new ParallelLinesCannotBeResolvedException(this, otherLine);
475

    
476
//if (Math.cos(theta) == 0) {
477

    
478
//X = otherLine.x + offset*Math.cos(otherLine.theta);
479
//Y = otherLine.y + offset*Math.sin(otherLine.theta);
480
//} else if (Math.cos(otherLine.theta) == 0) {
481
//X = x + offset*Math.cos(theta);
482
//Y = y + offset*Math.sin(theta);
483
//} else {
484
///*
485
//* m*(X - x0) + y0 = m'*(X - x'0) + y0'
486
//* X = (m*x0 - y0 - m'*x0' + y'0) / (m - m')
487
//*/
488
//double tanTheta = Math.tan(theta);
489
//double otherTanTheta = Math.tan(otherLine.theta);
490
//double thetaTimesX = tanTheta*this.x;
491
//X = (thetaTimesX - this.y - otherTanTheta*otherLine.x + otherLine.y) / (tanTheta - otherTanTheta);
492

    
493
///*
494
//* Y - y0 = m*(X - x0)
495
//* Y = m*X - m*x0 + y0
496
//*/
497
//Y = tanTheta*X - thetaTimesX + this.y;
498
//}
499
//return new Point2D.Double(X, Y);
500
//}
501

    
502
//@Override
503
//public String toString() {
504
//return "Y - "+y+" = "+theta+"*(X - "+x+")";
505
//}
506
//}
507

    
508
//class NotEnoughSegmentsToClosePathException extends Exception {
509
//private static final long serialVersionUID = 95503944546535L;
510
//public NotEnoughSegmentsToClosePathException(ArrayList<LineSegment> segments) {
511
//super("Need at least 2 segments to close a path. I've got "+segments.size()+".");
512
//}
513
//}
514

    
515
//class ParallelLinesCannotBeResolvedException extends Exception {
516
//private static final long serialVersionUID = 8322556508820067641L;
517

    
518
//public ParallelLinesCannotBeResolvedException(LineEquation eq1, LineEquation eq2) {
519
//super("Lines '"+eq1+"' and '"+eq2+"' are parallel and don't share any point!");
520
//}
521
//}