Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_mapcontext / src / org / gvsig / fmap / mapcontext / rendering / symbols / styles / Line2DOffset.java @ 28218

History | View | Annotate | Download (16.4 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 org.gvsig.fmap.mapcontext.rendering.symbols.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.gvsig.fmap.geom.primitive.GeneralPathX;
51
import org.slf4j.Logger;
52
import org.slf4j.LoggerFactory;
53

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

    
65
public class Line2DOffset {
66
        final static private Logger logger = LoggerFactory.getLogger(Line2DOffset.class);
67

    
68
        public static GeneralPathX offsetLine(Shape p, double offset) {
69

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

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

    
90
                                case PathIterator.SEG_LINETO:
91

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

    
113
                                } // end switch
114

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

    
120
                        return offsetSegments;
121
                } catch (ParallelLinesCannotBeResolvedException e) {
122
                        logger.error(e.getMessage(), e);
123
                        return new GeneralPathX(p);
124
                }
125
        }
126

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

    
141
                        double xOffset = offset * Math.sin(theta);
142
                        double yOffset = offset * Math.cos(theta);
143

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

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

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

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

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

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

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

    
211
class LineEquation {
212
        double theta, m, x, y;
213

    
214
        double xEnd, yEnd; // just for simplicity of code
215

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

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

    
234
                double X;
235
                double Y;
236
                if(Math.abs(this.x - this.xEnd)<0.00001) { //Esta linea es vertical
237
//                        System.out.println("1 VERTICAL");
238
                        X = this.xEnd;
239
                        if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical
240
//                                System.out.println("    2 PERPENDICULAR");
241
                                if(Math.abs(this.x - otherLine.x)<0.00001){ //Son la misma linea, devolvemos el primer punto de la otra linea.
242
//                                        System.out.println("        MISMA LINEA");
243
                                        Y = otherLine.y;
244
                                } else { //No son la misma linea, entonces son paralelas, excepcion.
245
//                                        System.out.println("        CASCO POR 1");
246
                                        throw new ParallelLinesCannotBeResolvedException(this, otherLine);
247
                                }
248
                        } else if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal
249
//                                System.out.println("    2 HORIZONTAL");
250
                                Y = otherLine.y;
251
                        } else { //Si no
252
//                                System.out.println("    2 CUALQUIERA");
253
                                Y = otherLine.m*(X - otherLine.x)+otherLine.y;
254
                        }
255

    
256
                } else if (Math.abs(this.y - this.yEnd)<0.00001) { //Esta linea es horizontal
257
//                        System.out.println("1 HORIZONTAL");
258
                        Y = this.yEnd;
259
                        if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal
260
//                                System.out.println("    2 HORIZONTAL");
261
                                if(Math.abs(this.y - otherLine.y)<0.00001){ //Son la misma linea, devolvemos el primer punto de la otra linea.
262
//                                        System.out.println("        MISMA LINEA");
263
                                        X = otherLine.x;
264
                                } else { //No son la misma linea, entonces son paralelas, excepcion.
265
//                                        System.out.println("        CASCO POR 2");
266
                                        throw new ParallelLinesCannotBeResolvedException(this, otherLine);
267
                                }
268
                        } else if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical
269
//                                System.out.println("    2 VERTICAL");
270
                                X = otherLine.x;
271
                        } else { //Si no
272
//                                System.out.println("    2 CUALQUIERA");
273
                                X = (Y - otherLine.y)/otherLine.m +otherLine.x;
274
                        }
275
                } else { //Esta linea no es ni vertical ni horizontal
276
//                        System.out.println("1 CUALQUIERA");
277
                        if (Math.abs(otherLine.y - otherLine.yEnd)<0.00001) { //La otra linea es horizontal
278
//                                System.out.println("    2 HORIZONTAL");
279
                                Y = otherLine.y;
280
                                X = (Y - this.y)/this.m +this.x;
281
                        } else if (Math.abs(otherLine.x - otherLine.xEnd)<0.00001){//La otra linea es vertical
282
//                                System.out.println("    2 VERTICAL");
283
                                X = otherLine.x;
284
                                Y = this.m*(X - this.x)+this.y;
285
                        } else if ((Math.abs(otherLine.m - this.m)<0.00001)) { //Tienen la misma pendiente
286
//                                System.out.println("    MISMA PENDIENTE");
287
                                Y = otherLine.m*(this.x - otherLine.x)+otherLine.y;
288
                                if (Math.abs(this.y - Y)<0.00001){ //Las lineas son la misma
289
//                                        System.out.println("        MISMA LINEA");
290
                                        X = otherLine.x;
291
                                        Y = otherLine.y;
292
                                } else {
293
//                                        System.out.println("        CASCO POR 3");
294
                                        throw new ParallelLinesCannotBeResolvedException(this, otherLine);
295
                                }
296
                        } else {
297
//                                System.out.println("    AMBAS CUALESQUIERA");
298
                                double mTimesX = this.m * this.x;
299
                                X = (mTimesX - this.y - otherLine.m * otherLine.x + otherLine.y) / (this.m - otherLine.m);
300
                                Y = this.m * X - mTimesX + this.y;
301
                        }
302
                }
303

    
304
//                System.out.println("DEVOLVEMOS X = "+X+" Y = "+Y);
305
                return new Point2D.Double(X, Y);
306

    
307
        }
308

    
309
        public String toString() {
310
                return "Y - " + y + " = " + m + "*(X - " + x + ")";
311
        }
312
}
313

    
314
class NotEnoughSegmentsToClosePathException extends Exception {
315
        private static final long serialVersionUID = 95503944546535L;
316

    
317
        public NotEnoughSegmentsToClosePathException(ArrayList segments) {
318
                super("Need at least 2 segments to close a path. I've got "
319
                                + segments.size() + ".");
320
        }
321
}
322

    
323
class ParallelLinesCannotBeResolvedException extends Exception {
324
        private static final long serialVersionUID = 8322556508820067641L;
325

    
326
        public ParallelLinesCannotBeResolvedException(LineEquation eq1,
327
                        LineEquation eq2) {
328
                super("Lines '" + eq1 + "' and '" + eq2
329
                                + "' are parallel and don't share any point!");
330
        }
331
}
332
//public class Line2DOffset {
333

    
334
//private static final double TOL = 1E-8;
335
//private static final double ANGLE_TOL = 0.01/180*Math.PI;
336

    
337
//public static GeneralPathX offsetLine(Shape p, double offset) {
338

    
339
//PathIterator pi = p.getPathIterator(null);
340
//double[] dataCoords = new double[6];
341
//Coordinate from = null, first = null;
342
//ArrayList<LineSegment> segments = new ArrayList<LineSegment>();
343
//GeneralPathX offsetSegments = new GeneralPathX();
344
//try {
345
//while (!pi.isDone()) {
346
//// while not done
347
//int type = pi.currentSegment(dataCoords);
348

    
349
//switch (type) {
350
//case PathIterator.SEG_MOVETO:
351
//from = new Coordinate(dataCoords[0], dataCoords[1]);
352
//first = from;
353
//break;
354

    
355
//case PathIterator.SEG_LINETO:
356

    
357
//// System.out.println("SEG_LINETO");
358
//Coordinate to = new Coordinate(dataCoords[0], dataCoords[1]);
359
//LineSegment line = new LineSegment(from, to);
360
//int size = segments.size();
361
//if (size>0) {
362
//LineSegment prev = segments.get(size-1);
363
//if (line.angle() == prev.angle()) {
364
//if (Math.abs(line.p0.x - prev.p1.x) < TOL &&
365
//Math.abs(line.p0.y - prev.p1.y) < TOL) {
366
//prev.p1 = line.p1;
367
//break;
368
//}
369
//}
370
//}
371
//from = to;
372
//segments.add(line);
373

    
374
//break;
375
//case PathIterator.SEG_CLOSE:
376
//line = new LineSegment(from, first);
377
//segments.add(line);
378
//from = first;
379
//try {
380
//offsetSegments.append(offsetAndConsumeClosedSegments(offset, segments), false);
381
//} catch (NotEnoughSegmentsToClosePathException e) {
382
//Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
383
//}
384
//break;
385

    
386
//} // end switch
387

    
388
//pi.next();
389
//}
390
//offsetSegments.append(offsetAndConsumeSegments(offset, segments), true);
391

    
392
//return offsetSegments;
393
//} catch (ParallelLinesCannotBeResolvedException e) {
394
//Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
395
//return new GeneralPathX(p);
396
//}
397
//}
398

    
399
//private static GeneralPathX offsetAndConsumeSegments(double offset, ArrayList<LineSegment> segments)  {
400
//Hashtable<LineSegment, LineEquation> offsetLines = new Hashtable<LineSegment, LineEquation>();
401
//int segmentCount = segments.size();
402
//// first calculate offset lines with the starting point
403
//for (int i = 0; i < segmentCount; i++) {
404
//LineSegment segment = segments.get(i);
405
//double theta = segment.angle();
406

    
407
//double xOffset = offset*Math.sin(theta);
408
//double yOffset = offset*Math.cos(theta);
409

    
410
//Coordinate p0 = segment.p0;
411
//double x0 = p0.x + xOffset;
412
//double y0 = p0.y - yOffset;
413

    
414
//Coordinate p1 = segment.p1;
415
//double x1 = p1.x + xOffset;
416
//double y1 = p1.y - yOffset;
417

    
418
//LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1, offset);
419
//offsetLines.put(segment, offsetLine);
420
//}
421

    
422
///*
423
//* let's now calculate the end point of each segment with
424
//* the point where each line crosses the next one.
425
//* this point will be the end point of the first line, and
426
//* the start point of its next one.
427
//*/
428
//Point2D pIni = null;
429
//Point2D pEnd = null;
430
//GeneralPathX gpx = new GeneralPathX();
431
//for (int i = 0; i < segmentCount; i++) {
432
//LineSegment segment = segments.get(0);
433
//LineEquation eq = offsetLines.get(segment);
434
//Point2D pAux = null;
435
//if (i < segmentCount -1) {
436
//try {
437
//pAux = eq.resolve(offsetLines.get(segments.get(1)));
438
//if (i == 0) {
439
//pIni = new Point2D.Double(eq.x, eq.y);
440
//} else {
441
//pIni = pEnd;
442
//}
443
//} catch (ParallelLinesCannotBeResolvedException e) {
444
//segments.remove(0);
445
//continue;
446
//}
447
//}
448

    
449

    
450
//if (pAux != null) {
451
//pEnd = pAux;
452
//} else {
453
//pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
454
//}
455

    
456
//gpx.append(new Line2D.Double(pIni, pEnd), true);
457
//segments.remove(0);
458
//}
459
//return gpx;
460
//}
461

    
462
//private static GeneralPathX offsetAndConsumeClosedSegments(double offset, ArrayList<LineSegment> segments) throws ParallelLinesCannotBeResolvedException, NotEnoughSegmentsToClosePathException {
463
//int segmentCount = segments.size();
464
//if (segmentCount > 1) {
465
//GeneralPathX openPath = offsetAndConsumeSegments(offset, segments);
466
//openPath.closePath();
467
//return openPath;
468
//}
469
//throw new NotEnoughSegmentsToClosePathException(segments);
470
//}
471
//}
472

    
473
//class LineEquation {
474
//double theta, x, y;
475
//double xEnd, yEnd; // just for simplicity of code
476
//double offset;
477

    
478
//public LineEquation(double theta, double x, double y, double xEnd, double yEnd, double offset) {
479
//this.theta = theta;
480
//this.x = x;
481
//this.y = y;
482
//this.xEnd = xEnd;
483
//this.yEnd = yEnd;
484
//this.offset = offset;
485
//}
486

    
487
//public Point2D resolve(LineEquation otherLine) throws ParallelLinesCannotBeResolvedException {
488
//double X;
489
//double Y;
490

    
491

    
492
///*
493
//* line1 (this):      y  -  y0 =  m*(x  - x0)
494
//* line2 (otherLine): y' - y'0 = m'*(x' - x'0)
495
//*/
496
//if (otherLine.theta == this.theta)
497
//throw new ParallelLinesCannotBeResolvedException(this, otherLine);
498

    
499
//if (Math.cos(theta) == 0) {
500

    
501
//X = otherLine.x + offset*Math.cos(otherLine.theta);
502
//Y = otherLine.y + offset*Math.sin(otherLine.theta);
503
//} else if (Math.cos(otherLine.theta) == 0) {
504
//X = x + offset*Math.cos(theta);
505
//Y = y + offset*Math.sin(theta);
506
//} else {
507
///*
508
//* m*(X - x0) + y0 = m'*(X - x'0) + y0'
509
//* X = (m*x0 - y0 - m'*x0' + y'0) / (m - m')
510
//*/
511
//double tanTheta = Math.tan(theta);
512
//double otherTanTheta = Math.tan(otherLine.theta);
513
//double thetaTimesX = tanTheta*this.x;
514
//X = (thetaTimesX - this.y - otherTanTheta*otherLine.x + otherLine.y) / (tanTheta - otherTanTheta);
515

    
516
///*
517
//* Y - y0 = m*(X - x0)
518
//* Y = m*X - m*x0 + y0
519
//*/
520
//Y = tanTheta*X - thetaTimesX + this.y;
521
//}
522
//return new Point2D.Double(X, Y);
523
//}
524

    
525
//@Override
526
//public String toString() {
527
//return "Y - "+y+" = "+theta+"*(X - "+x+")";
528
//}
529
//}
530

    
531
//class NotEnoughSegmentsToClosePathException extends Exception {
532
//private static final long serialVersionUID = 95503944546535L;
533
//public NotEnoughSegmentsToClosePathException(ArrayList<LineSegment> segments) {
534
//super("Need at least 2 segments to close a path. I've got "+segments.size()+".");
535
//}
536
//}
537

    
538
//class ParallelLinesCannotBeResolvedException extends Exception {
539
//private static final long serialVersionUID = 8322556508820067641L;
540

    
541
//public ParallelLinesCannotBeResolvedException(LineEquation eq1, LineEquation eq2) {
542
//super("Lines '"+eq1+"' and '"+eq2+"' are parallel and don't share any point!");
543
//}
544
//}