Statistics
| Revision:

root / branches / v2_0_0_prep / libFMap_mapcontext / src / org / gvsig / fmap / mapcontext / rendering / symbols / styles / Line2DOffset.java @ 21200

History | View | Annotate | Download (13.2 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.apache.log4j.Logger;
51
import org.gvsig.fmap.geom.primitive.GeneralPathX;
52

    
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
                try {
77
                        while (!pi.isDone()) {
78
                                // while not done
79
                                int type = pi.currentSegment(dataCoords);
80

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

    
87
                                case PathIterator.SEG_LINETO:
88

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

    
108
                                } // end switch
109

    
110
                                pi.next();
111
                        }
112
                        offsetSegments.append(offsetAndConsumeSegments(offset, segments),
113
                                        true);
114

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

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

    
135
                        double xOffset = offset * Math.sin(theta);
136
                        double yOffset = offset * Math.cos(theta);
137

    
138
                        Coordinate p0 = segment.p0;
139
                        double x0 = p0.x + xOffset;
140
                        double y0 = p0.y - yOffset;
141

    
142
                        Coordinate p1 = segment.p1;
143
                        double x1 = p1.x + xOffset;
144
                        double y1 = p1.y - yOffset;
145

    
146
                        LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1);
147
                        offsetLines.put(segment, offsetLine);
148
                }
149

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

    
167
                        if (i < segmentCount - 1) {
168
                                pEnd = eq.resolve(offsetLines.get(segments.get(1)));
169
                        } else {
170
                                pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
171
                        }
172

    
173
                        gpx.append(new Line2D.Double(pIni, pEnd), true);
174
                        segments.remove(0);
175
                }
176
                return gpx;
177
        }
178

    
179
        private static GeneralPathX offsetAndConsumeClosedSegments(double offset,
180
                        ArrayList<LineSegment> segments)
181
        throws ParallelLinesCannotBeResolvedException,
182
        NotEnoughSegmentsToClosePathException {
183
                int segmentCount = segments.size();
184
                if (segmentCount > 1) {
185
                        GeneralPathX openPath = offsetAndConsumeSegments(offset, segments);
186
                        openPath.closePath();
187
                        return openPath;
188
                }
189
                throw new NotEnoughSegmentsToClosePathException(segments);
190
        }
191
}
192

    
193
class LineEquation {
194
        double theta, x, y;
195

    
196
        double xEnd, yEnd; // just for simplicity of code
197

    
198
        public LineEquation(double theta, double x, double y, double xEnd,
199
                        double yEnd) {
200
                this.theta = Math.tan(theta);
201
                this.x = x;
202
                this.y = y;
203
                this.xEnd = xEnd;
204
                this.yEnd = yEnd;
205
        }
206

    
207
        public Point2D resolve(LineEquation otherLine)
208
        throws ParallelLinesCannotBeResolvedException {
209
                /*
210
                 * line1 (this): y - y0 = m*(x - x0) line2 (otherLine): y' - y'0 =
211
                 * m'*(x' - x'0)
212
                 */
213
                if (otherLine.theta == this.theta) {
214
                        if (this.xEnd == otherLine.x && this.yEnd == otherLine.y) {
215
                                return new Point2D.Double(otherLine.x, otherLine.y);
216
                        }
217
                        throw new ParallelLinesCannotBeResolvedException(this, otherLine);
218
                }
219

    
220
                /*
221
                 * m*(X - x0) + y0 = m'*(X - x'0) + y0' X = (m*x0 - y0 - m'*x0' + y'0) /
222
                 * (m - m')
223
                 */
224
                double thetaTimesX = this.theta * this.x;
225
                double X = (thetaTimesX - this.y - otherLine.theta * otherLine.x + otherLine.y)
226
                / (this.theta - otherLine.theta);
227

    
228
                /*
229
                 * Y - y0 = m*(X - x0) Y = m*X - m*x0 + y0
230
                 */
231
                double Y = this.theta * X - thetaTimesX + this.y;
232
                return new Point2D.Double(X, Y);
233
        }
234

    
235
        @Override
236
        public String toString() {
237
                return "Y - " + y + " = " + theta + "*(X - " + x + ")";
238
        }
239
}
240

    
241
class NotEnoughSegmentsToClosePathException extends Exception {
242
        private static final long serialVersionUID = 95503944546535L;
243

    
244
        public NotEnoughSegmentsToClosePathException(ArrayList<LineSegment> segments) {
245
                super("Need at least 2 segments to close a path. I've got "
246
                                + segments.size() + ".");
247
        }
248
}
249

    
250
class ParallelLinesCannotBeResolvedException extends Exception {
251
        private static final long serialVersionUID = 8322556508820067641L;
252

    
253
        public ParallelLinesCannotBeResolvedException(LineEquation eq1,
254
                        LineEquation eq2) {
255
                super("Lines '" + eq1 + "' and '" + eq2
256
                                + "' are parallel and don't share any point!");
257
        }
258
}
259
//public class Line2DOffset {
260

    
261
//private static final double TOL = 1E-8;
262
//private static final double ANGLE_TOL = 0.01/180*Math.PI;
263

    
264
//public static GeneralPathX offsetLine(Shape p, double offset) {
265

    
266
//PathIterator pi = p.getPathIterator(null);
267
//double[] dataCoords = new double[6];
268
//Coordinate from = null, first = null;
269
//ArrayList<LineSegment> segments = new ArrayList<LineSegment>();
270
//GeneralPathX offsetSegments = new GeneralPathX();
271
//try {
272
//while (!pi.isDone()) {
273
//// while not done
274
//int type = pi.currentSegment(dataCoords);
275

    
276
//switch (type) {
277
//case PathIterator.SEG_MOVETO:
278
//from = new Coordinate(dataCoords[0], dataCoords[1]);
279
//first = from;
280
//break;
281

    
282
//case PathIterator.SEG_LINETO:
283

    
284
//// System.out.println("SEG_LINETO");
285
//Coordinate to = new Coordinate(dataCoords[0], dataCoords[1]);
286
//LineSegment line = new LineSegment(from, to);
287
//int size = segments.size();
288
//if (size>0) {
289
//LineSegment prev = segments.get(size-1);
290
//if (line.angle() == prev.angle()) {
291
//if (Math.abs(line.p0.x - prev.p1.x) < TOL &&
292
//Math.abs(line.p0.y - prev.p1.y) < TOL) {
293
//prev.p1 = line.p1;
294
//break;
295
//}
296
//}
297
//}
298
//from = to;
299
//segments.add(line);
300

    
301
//break;
302
//case PathIterator.SEG_CLOSE:
303
//line = new LineSegment(from, first);
304
//segments.add(line);
305
//from = first;
306
//try {
307
//offsetSegments.append(offsetAndConsumeClosedSegments(offset, segments), false);
308
//} catch (NotEnoughSegmentsToClosePathException e) {
309
//Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
310
//}
311
//break;
312

    
313
//} // end switch
314

    
315
//pi.next();
316
//}
317
//offsetSegments.append(offsetAndConsumeSegments(offset, segments), true);
318

    
319
//return offsetSegments;
320
//} catch (ParallelLinesCannotBeResolvedException e) {
321
//Logger.getLogger(Line2DOffset.class).error(e.getMessage(), e);
322
//return new GeneralPathX(p);
323
//}
324
//}
325

    
326
//private static GeneralPathX offsetAndConsumeSegments(double offset, ArrayList<LineSegment> segments)  {
327
//Hashtable<LineSegment, LineEquation> offsetLines = new Hashtable<LineSegment, LineEquation>();
328
//int segmentCount = segments.size();
329
//// first calculate offset lines with the starting point
330
//for (int i = 0; i < segmentCount; i++) {
331
//LineSegment segment = segments.get(i);
332
//double theta = segment.angle();
333

    
334
//double xOffset = offset*Math.sin(theta);
335
//double yOffset = offset*Math.cos(theta);
336

    
337
//Coordinate p0 = segment.p0;
338
//double x0 = p0.x + xOffset;
339
//double y0 = p0.y - yOffset;
340

    
341
//Coordinate p1 = segment.p1;
342
//double x1 = p1.x + xOffset;
343
//double y1 = p1.y - yOffset;
344

    
345
//LineEquation offsetLine = new LineEquation(theta, x0, y0, x1, y1, offset);
346
//offsetLines.put(segment, offsetLine);
347
//}
348

    
349
///*
350
//* let's now calculate the end point of each segment with
351
//* the point where each line crosses the next one.
352
//* this point will be the end point of the first line, and
353
//* the start point of its next one.
354
//*/
355
//Point2D pIni = null;
356
//Point2D pEnd = null;
357
//GeneralPathX gpx = new GeneralPathX();
358
//for (int i = 0; i < segmentCount; i++) {
359
//LineSegment segment = segments.get(0);
360
//LineEquation eq = offsetLines.get(segment);
361
//Point2D pAux = null;
362
//if (i < segmentCount -1) {
363
//try {
364
//pAux = eq.resolve(offsetLines.get(segments.get(1)));
365
//if (i == 0) {
366
//pIni = new Point2D.Double(eq.x, eq.y);
367
//} else {
368
//pIni = pEnd;
369
//}
370
//} catch (ParallelLinesCannotBeResolvedException e) {
371
//segments.remove(0);
372
//continue;
373
//}
374
//}
375

    
376

    
377
//if (pAux != null) {
378
//pEnd = pAux;
379
//} else {
380
//pEnd = new Point2D.Double(eq.xEnd, eq.yEnd);
381
//}
382

    
383
//gpx.append(new Line2D.Double(pIni, pEnd), true);
384
//segments.remove(0);
385
//}
386
//return gpx;
387
//}
388

    
389
//private static GeneralPathX offsetAndConsumeClosedSegments(double offset, ArrayList<LineSegment> segments) throws ParallelLinesCannotBeResolvedException, NotEnoughSegmentsToClosePathException {
390
//int segmentCount = segments.size();
391
//if (segmentCount > 1) {
392
//GeneralPathX openPath = offsetAndConsumeSegments(offset, segments);
393
//openPath.closePath();
394
//return openPath;
395
//}
396
//throw new NotEnoughSegmentsToClosePathException(segments);
397
//}
398
//}
399

    
400
//class LineEquation {
401
//double theta, x, y;
402
//double xEnd, yEnd; // just for simplicity of code
403
//double offset;
404

    
405
//public LineEquation(double theta, double x, double y, double xEnd, double yEnd, double offset) {
406
//this.theta = theta;
407
//this.x = x;
408
//this.y = y;
409
//this.xEnd = xEnd;
410
//this.yEnd = yEnd;
411
//this.offset = offset;
412
//}
413

    
414
//public Point2D resolve(LineEquation otherLine) throws ParallelLinesCannotBeResolvedException {
415
//double X;
416
//double Y;
417

    
418

    
419
///*
420
//* line1 (this):      y  -  y0 =  m*(x  - x0)
421
//* line2 (otherLine): y' - y'0 = m'*(x' - x'0)
422
//*/
423
//if (otherLine.theta == this.theta)
424
//throw new ParallelLinesCannotBeResolvedException(this, otherLine);
425

    
426
//if (Math.cos(theta) == 0) {
427

    
428
//X = otherLine.x + offset*Math.cos(otherLine.theta);
429
//Y = otherLine.y + offset*Math.sin(otherLine.theta);
430
//} else if (Math.cos(otherLine.theta) == 0) {
431
//X = x + offset*Math.cos(theta);
432
//Y = y + offset*Math.sin(theta);
433
//} else {
434
///*
435
//* m*(X - x0) + y0 = m'*(X - x'0) + y0'
436
//* X = (m*x0 - y0 - m'*x0' + y'0) / (m - m')
437
//*/
438
//double tanTheta = Math.tan(theta);
439
//double otherTanTheta = Math.tan(otherLine.theta);
440
//double thetaTimesX = tanTheta*this.x;
441
//X = (thetaTimesX - this.y - otherTanTheta*otherLine.x + otherLine.y) / (tanTheta - otherTanTheta);
442

    
443
///*
444
//* Y - y0 = m*(X - x0)
445
//* Y = m*X - m*x0 + y0
446
//*/
447
//Y = tanTheta*X - thetaTimesX + this.y;
448
//}
449
//return new Point2D.Double(X, Y);
450
//}
451

    
452
//@Override
453
//public String toString() {
454
//return "Y - "+y+" = "+theta+"*(X - "+x+")";
455
//}
456
//}
457

    
458
//class NotEnoughSegmentsToClosePathException extends Exception {
459
//private static final long serialVersionUID = 95503944546535L;
460
//public NotEnoughSegmentsToClosePathException(ArrayList<LineSegment> segments) {
461
//super("Need at least 2 segments to close a path. I've got "+segments.size()+".");
462
//}
463
//}
464

    
465
//class ParallelLinesCannotBeResolvedException extends Exception {
466
//private static final long serialVersionUID = 8322556508820067641L;
467

    
468
//public ParallelLinesCannotBeResolvedException(LineEquation eq1, LineEquation eq2) {
469
//super("Lines '"+eq1+"' and '"+eq2+"' are parallel and don't share any point!");
470
//}
471
//}