root / trunk / libraries / libTopology / src / org / geotools / referencefork / geometry / ShapeUtilities.java @ 22875
History | View | Annotate | Download (25.1 KB)
1 |
/*
|
---|---|
2 |
* GeoTools - OpenSource mapping toolkit
|
3 |
* http://geotools.org
|
4 |
* (C) 2003-2006, Geotools Project Managment Committee (PMC)
|
5 |
* (C) 2001, Institut de Recherche pour le Développement
|
6 |
*
|
7 |
* This library is free software; you can redistribute it and/or
|
8 |
* modify it under the terms of the GNU Lesser General Public
|
9 |
* License as published by the Free Software Foundation; either
|
10 |
* version 2.1 of the License, or (at your option) any later version.
|
11 |
*
|
12 |
* This library is distributed in the hope that it will be useful,
|
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15 |
* Lesser General Public License for more details.
|
16 |
*/
|
17 |
package org.geotools.referencefork.geometry; |
18 |
|
19 |
import java.awt.Shape; |
20 |
import java.awt.geom.CubicCurve2D; |
21 |
import java.awt.geom.Ellipse2D; |
22 |
import java.awt.geom.Line2D; |
23 |
import java.awt.geom.PathIterator; |
24 |
import java.awt.geom.Point2D; |
25 |
import java.awt.geom.QuadCurve2D; |
26 |
import java.awt.geom.Rectangle2D; |
27 |
|
28 |
|
29 |
/**
|
30 |
* Static utilities methods. Those methods operate on geometric
|
31 |
* shapes from the {@code java.awt.geom} package.
|
32 |
*
|
33 |
* @since 2.0
|
34 |
* @source $URL: http://svn.geotools.org/geotools/trunk/gt/modules/library/referencing/src/main/java/org/geotools/resources/geometry/ShapeUtilities.java $
|
35 |
* @version $Id: ShapeUtilities.java 27983 2007-11-22 10:25:39Z desruisseaux $
|
36 |
* @author Martin Desruisseaux
|
37 |
*/
|
38 |
public final class ShapeUtilities { |
39 |
/**
|
40 |
* Valeur limite pour détecter si des points sont
|
41 |
* colinéaires ou si des coordonnées sont identiques.
|
42 |
*/
|
43 |
private static final double EPS = 1E-6; |
44 |
|
45 |
/**
|
46 |
* Constante pour les calculs de paraboles. Cette constante indique que l'axe des
|
47 |
* <var>x</var> de la parabole doit être parallèle à la droite joignant les points
|
48 |
* P0 et P2.
|
49 |
*/
|
50 |
public static final int PARALLEL = 0; |
51 |
|
52 |
/**
|
53 |
* Constante pour les calculs de paraboles. Cette constante indique que l'axe des
|
54 |
* <var>x</var> de la parabole doit être horizontale, quelle que soit la pente de
|
55 |
* la droite joignant les points P0 et P2.
|
56 |
*/
|
57 |
public static final int HORIZONTAL = 1; |
58 |
|
59 |
/**
|
60 |
* Interdit la création d'objets de cette classe.
|
61 |
*/
|
62 |
private ShapeUtilities() {
|
63 |
} |
64 |
|
65 |
/**
|
66 |
* Retourne le point d'intersection de deux segments de droites.
|
67 |
* Cette méthode ne prolonge pas les segments de droites à l'infini.
|
68 |
* Si les deux segments ne s'interceptent pas (soit par ce qu'ils sont
|
69 |
* parallèles, ou soit parce qu'ils ne se prolongent pas assez loin
|
70 |
* pour se toucher), alors cette méthode retourne {@code null}.
|
71 |
*
|
72 |
* @param a Première ligne.
|
73 |
* @param b Deuxième ligne.
|
74 |
* @return Si une intersection fut trouvée, les coordonnées de cette
|
75 |
* intersection. Si aucune intersection n'a été trouvée, alors
|
76 |
* cette méthode retourne {@code null}.
|
77 |
*/
|
78 |
public static Point2D intersectionPoint(final Line2D a, final Line2D b) { |
79 |
return intersectionPoint(a.getX1(), a.getY1(), a.getX2(), a.getY2(),
|
80 |
b.getX1(), b.getY1(), b.getX2(), b.getY2()); |
81 |
} |
82 |
|
83 |
/**
|
84 |
* Retourne le point d'intersection de deux segments de droites.
|
85 |
* Cette méthode ne prolonge pas les segments de droites à l'infini.
|
86 |
* Si les deux segments ne s'interceptent pas (soit par ce qu'ils sont
|
87 |
* parallèles, ou soit parce qu'ils ne se prolongent pas assez loin
|
88 |
* pour se toucher), alors cette méthode retourne {@code null}.
|
89 |
*
|
90 |
* @return Si une intersection fut trouvée, les coordonnées de cette
|
91 |
* intersection. Si aucune intersection n'a été trouvée, alors
|
92 |
* cette méthode retourne {@code null}.
|
93 |
*/
|
94 |
public static Point2D intersectionPoint(final double ax1, final double ay1, double ax2, double ay2, |
95 |
final double bx1, final double by1, double bx2, double by2) |
96 |
{ |
97 |
ax2 -= ax1; |
98 |
ay2 -= ay1; |
99 |
bx2 -= bx1; |
100 |
by2 -= by1; |
101 |
double x = ay2*bx2;
|
102 |
double y = ax2*by2;
|
103 |
/*
|
104 |
* Les x et y calculés précédemment ne sont que des valeurs temporaires. Si et
|
105 |
* seulement si les deux droites sont parallèles, alors x==y. Ensuite seulement,
|
106 |
* la paire (x,y) ci-dessous sera les véritables coordonnées du point d'intersection.
|
107 |
*/
|
108 |
x = ((by1-ay1)*(ax2*bx2)+x*ax1-y*bx1)/(x-y); |
109 |
y = Math.abs(bx2) > Math.abs(ax2) ? |
110 |
(by2/bx2)*(x-bx1)+by1 : |
111 |
(ay2/ax2)*(x-ax1)+ay1; |
112 |
/*
|
113 |
* Les expressions '!=0' ci-dessous sont importantes afin d'éviter des problèmes
|
114 |
* d'erreurs d'arrondissement lorsqu'un segment est vertical ou horizontal. Les
|
115 |
* '!' qui suivent sont importants pour un fonctionnement correct avec NaN.
|
116 |
*/
|
117 |
if (ax2!=0 && !(ax2<0 ? (x<=ax1 && x>=ax1+ax2) : (x>=ax1 && x<=ax1+ax2))) return null; |
118 |
if (bx2!=0 && !(bx2<0 ? (x<=bx1 && x>=bx1+bx2) : (x>=bx1 && x<=bx1+bx2))) return null; |
119 |
if (ay2!=0 && !(ay2<0 ? (y<=ay1 && y>=ay1+ay2) : (y>=ay1 && y<=ay1+ay2))) return null; |
120 |
if (by2!=0 && !(by2<0 ? (y<=by1 && y>=by1+by2) : (y>=by1 && y<=by1+by2))) return null; |
121 |
return new Point2D.Double(x,y); |
122 |
} |
123 |
|
124 |
/**
|
125 |
* Retourne le point sur le segment de droite {@code line} qui se trouve le
|
126 |
* plus près du point {@code point} spécifié. Appellons {@code result}
|
127 |
* le point retourné par cette méthode. Il est garanti que {@code result}
|
128 |
* répond aux conditions suivantes (aux erreurs d'arrondissements près):
|
129 |
*
|
130 |
* <ul>
|
131 |
* <li>{@code result} est un point du segment de droite {@code line}.
|
132 |
* Il ne trouve pas au delà des points extrêmes P1 et P2 de ce segment.</li>
|
133 |
* <li>La distance entre les points {@code result} et {@code point}
|
134 |
* est la plus courte distance possible pour les points qui respectent la
|
135 |
* condition précédente. Cette distance peut être calculée par
|
136 |
* {@code point.distance(result)}.</li>
|
137 |
* </ul>
|
138 |
*
|
139 |
* @see #colinearPoint(Line2D, Point2D, double)
|
140 |
*/
|
141 |
public static Point2D nearestColinearPoint(final Line2D segment, final Point2D point) { |
142 |
return nearestColinearPoint(segment.getX1(), segment.getY1(),
|
143 |
segment.getX2(), segment.getY2(), |
144 |
point.getX(), point.getY()); |
145 |
} |
146 |
|
147 |
/**
|
148 |
* Retourne le point sur le segment de droite {@code (x1,y1)-(x2,y2)}
|
149 |
* qui se trouve le plus près du point {@code (x,y)} spécifié. Appellons
|
150 |
* {@code result} le point retourné par cette méthode. Il est garanti
|
151 |
* que {@code result} répond aux conditions suivantes (aux erreurs
|
152 |
* d'arrondissements près):
|
153 |
*
|
154 |
* <ul>
|
155 |
* <li>{@code result} est un point du segment de droite
|
156 |
* {@code (x1,y1)-(x2,y2)}. Il ne trouve pas au delà des points
|
157 |
* extrêmes {@code (x1,y1)} et {@code (x2,y2)} de ce segment.</li>
|
158 |
* <li>La distance entre les points {@code result} et {@code (x,y)}
|
159 |
* est la plus courte distance possible pour les points qui respectent la
|
160 |
* condition précédente. Cette distance peut être calculée par
|
161 |
* <code>new Point2D.Double(x,y).distance(result)</code>.</li>
|
162 |
* </ul>
|
163 |
*
|
164 |
* @see #colinearPoint(double,double , double,double , double,double , double)
|
165 |
*/
|
166 |
public static Point2D nearestColinearPoint(final double x1, final double y1, |
167 |
final double x2, final double y2, |
168 |
double x, double y) |
169 |
{ |
170 |
final double slope = (y2-y1)/(x2-x1); |
171 |
if (!Double.isInfinite(slope)) { |
172 |
final double y0 = (y2-slope*x2); |
173 |
x = ((y-y0)*slope+x)/(slope*slope+1);
|
174 |
y = x*slope+y0; |
175 |
} |
176 |
else x=x2;
|
177 |
|
178 |
if (x1<=x2) {
|
179 |
if (x<x1) x=x1;
|
180 |
if (x>x2) x=x2;
|
181 |
} else {
|
182 |
if (x>x1) x=x1;
|
183 |
if (x<x2) x=x2;
|
184 |
} |
185 |
|
186 |
if (y1<=y2) {
|
187 |
if (y<y1) y=y1;
|
188 |
if (y>y2) y=y2;
|
189 |
} else {
|
190 |
if (y>y1) y=y1;
|
191 |
if (y<y2) y=y2;
|
192 |
} |
193 |
return new Point2D.Double(x,y); |
194 |
} |
195 |
|
196 |
/**
|
197 |
* Retourne le point sur le segment de droite {@code line} qui se trouve à la
|
198 |
* distance {@code distance} spécifiée du point {@code point}. Appellons
|
199 |
* {@code result} le point retourné par cette méthode. Si {@code result}
|
200 |
* est non-nul, alors il est garanti qu'il répond aux conditions suivantes (aux
|
201 |
* erreurs d'arrondissements près):
|
202 |
*
|
203 |
* <ul>
|
204 |
* <li>{@code result} est un point du segment de droite {@code line}.
|
205 |
* Il ne trouve pas au delà des points extrêmes P1 et P2 de ce segment.</li>
|
206 |
* <li>La distance entre les points {@code result} et {@code point}
|
207 |
* est exactement {@code distance} (aux erreurs d'arrondissements près).
|
208 |
* Cette distance peut être calculée par {@code point.distance(result)}.</li>
|
209 |
* </ul>
|
210 |
*
|
211 |
* Si aucun point ne peut répondre à ces conditions, alors cette méthode retourne
|
212 |
* {@code null}. Si deux points peuvent répondre à ces conditions, alors par
|
213 |
* convention cette méthode retourne le point le plus près du point {@code line.getP1()}.
|
214 |
*
|
215 |
* @see #nearestColinearPoint(Line2D, Point2D)
|
216 |
*/
|
217 |
public static Point2D colinearPoint(Line2D line, Point2D point, double distance) { |
218 |
return colinearPoint(line.getX1(), line.getY1(), line.getX2(), line.getY2(),
|
219 |
point.getX(), point.getY(), distance); |
220 |
} |
221 |
|
222 |
/**
|
223 |
* Retourne le point sur le segment de droite {@code (x1,y1)-(x2,y2)}
|
224 |
* qui se trouve à la distance {@code distance} spécifiée du point
|
225 |
* {@code point}. Appellons {@code result} le point retourné par
|
226 |
* cette méthode. Si {@code result} est non-nul, alors il est garantit
|
227 |
* qu'il répond aux conditions suivantes (aux erreurs d'arrondissements près):
|
228 |
*
|
229 |
* <ul>
|
230 |
* <li>{@code result} est un point du segment de droite {@code (x1,y1)-(x2,y2)}.
|
231 |
* Il ne trouve pas au delà des points extrêmes {@code (x1,y1)} et
|
232 |
* {@code (x2,y2)} de ce segment.</li>
|
233 |
* <li>La distance entre les points {@code result} et {@code point}
|
234 |
* est exactement {@code distance} (aux erreurs d'arrondissements près).
|
235 |
* Cette distance peut être calculée par {@code point.distance(result)}.</li>
|
236 |
* </ul>
|
237 |
*
|
238 |
* Si aucun point ne peut répondre à ces conditions, alors cette méthode retourne
|
239 |
* {@code null}. Si deux points peuvent répondre à ces conditions, alors par
|
240 |
* convention cette méthode retourne le point le plus près du point {@code (x1,y1)}.
|
241 |
*
|
242 |
* @see #nearestColinearPoint(double,double , double,double , double,double)
|
243 |
*/
|
244 |
public static Point2D colinearPoint(double x1, double y1, double x2, double y2, |
245 |
double x, double y, double distance) |
246 |
{ |
247 |
final double ox1=x1; |
248 |
final double oy1=y1; |
249 |
final double ox2=x2; |
250 |
final double oy2=y2; |
251 |
distance *= distance; |
252 |
if (x1==x2) {
|
253 |
double dy = x1-x;
|
254 |
dy = Math.sqrt(distance-dy*dy);
|
255 |
y1 = y-dy; |
256 |
y2 = y+dy; |
257 |
} else if (y1==y2) { |
258 |
double dx = y1-y;
|
259 |
dx = Math.sqrt(distance-dx*dx);
|
260 |
x1 = x-dx; |
261 |
x2 = x+dx; |
262 |
} else {
|
263 |
final double m = (y1-y2)/(x2-x1); |
264 |
final double y0 = (y2-y)+m*(x2-x); |
265 |
final double B = m*y0; |
266 |
final double A = m*m+1; |
267 |
final double C = Math.sqrt(B*B + A*(distance-y0*y0)); |
268 |
x1 = (B+C)/A; |
269 |
x2 = (B-C)/A; |
270 |
y1 = y + y0-m*x1; |
271 |
y2 = y + y0-m*x2; |
272 |
x1 += x; |
273 |
x2 += x; |
274 |
} |
275 |
boolean in1, in2;
|
276 |
if (oy1 > oy2) {
|
277 |
in1 = y1<=oy1 && y1>=oy2; |
278 |
in2 = y2<=oy1 && y2>=oy2; |
279 |
} else {
|
280 |
in1 = y1>=oy1 && y1<=oy2; |
281 |
in2 = y2>=oy1 && y2<=oy2; |
282 |
} |
283 |
if (ox1 > ox2)
|
284 |
{ |
285 |
in1 &= x1<=ox1 && x1>=ox2; |
286 |
in2 &= x2<=ox1 && x2>=ox2; |
287 |
} else {
|
288 |
in1 &= x1>=ox1 && x1<=ox2; |
289 |
in2 &= x2>=ox1 && x2<=ox2; |
290 |
} |
291 |
if (!in1 && !in2) return null; |
292 |
if (!in1) return new Point2D.Double(x2,y2); |
293 |
if (!in2) return new Point2D.Double(x1,y1); |
294 |
x = x1-ox1; |
295 |
y = y1-oy1; |
296 |
final double d1 = x*x+y*y; |
297 |
x = x2-ox1; |
298 |
y = y2-oy1; |
299 |
final double d2 = x*x+y*y; |
300 |
if (d1>d2) return new Point2D.Double(x2,y2); |
301 |
else return new Point2D.Double(x1,y1); |
302 |
} |
303 |
|
304 |
/**
|
305 |
* Retourne une courbe quadratique passant par les trois points spécifiés. Il peut exister une infinité de courbes
|
306 |
* quadratiques passant par trois points. On peut voir les choses en disant qu'une courbe quadratique correspond à
|
307 |
* une parabole produite par une équation de la forme <code>y=ax²+bx+c</code>, mais que l'axe des <var>x</var> de
|
308 |
* cette équation n'est pas nécessairement horizontal. La direction de cet axe des <var>x</var> dépend du paramètre
|
309 |
* {@code orientation} spécifié à cette méthode. La valeur {@link #HORIZONTAL} signifie que l'axe des <var>x</var>
|
310 |
* de la parabole sera toujours horizontal. La courbe quadratique produite ressemblera alors à une parabole classique
|
311 |
* telle qu'on en voit dans les ouvrages de mathématiques élémentaires. La valeur {@link #PARALLEL} indique plutôt que
|
312 |
* l'axe des <var>x</var> de la parabole doit être parallèle à la droite joignant les points {@code P0} et
|
313 |
* {@code P2}. Ce dernier type produira le même résultat que {@link #HORIZONTAL} si {@code P0.y==P2.y}.
|
314 |
*
|
315 |
* @param P0 Premier point de la courbe quadratique.
|
316 |
* @param P1 Point par lequel la courbe quadratique doit passer. Il n'est pas obligatoire que ce point soit situé
|
317 |
* entre {@code P0} et {@code P1}. Toutefois, il ne doit pas être colinéaire avec {@code P0}
|
318 |
* et {@code P1}.
|
319 |
* @param P2 Dernier point de la courbe quadratique.
|
320 |
* @param orientation Orientation de l'axe des <var>x</var> de la parabole: {@link #PARALLEL} ou {@link #HORIZONTAL}.
|
321 |
* @return Une courbe quadratique passant par les trois points spécifiés. La courbe commencera au point {@code P0}
|
322 |
* et se terminera au point {@code P2}. Si deux points ont des coordonnées presque identiques, ou si les
|
323 |
* trois points sont colinéaires, alors cette méthode retourne {@code null}.
|
324 |
* @throws IllegalArgumentException si l'argument {@code orientation} n'est pas une des constantes valides.
|
325 |
*/
|
326 |
public static QuadCurve2D fitParabol(final Point2D P0, final Point2D P1, final Point2D P2, |
327 |
final int orientation) throws IllegalArgumentException |
328 |
{ |
329 |
return fitParabol(P0.getX(), P0.getY(),
|
330 |
P1.getX(), P1.getY(), |
331 |
P2.getX(), P2.getY(), orientation); |
332 |
} |
333 |
|
334 |
/**
|
335 |
* Retourne une courbe quadratique passant par les trois points spécifiés. Il peut exister une infinité de courbes
|
336 |
* quadratiques passant par trois points. On peut voir les choses en disant qu'une courbe quadratique correspond à
|
337 |
* une parabole produite par une équation de la forme <code>y=ax²+bx+c</code>, mais que l'axe des <var>x</var> de
|
338 |
* cette équation n'est pas nécessairement horizontal. La direction de cet axe des <var>x</var> dépend du paramètre
|
339 |
* {@code orientation} spécifié à cette méthode. La valeur {@link #HORIZONTAL} signifie que l'axe des <var>x</var>
|
340 |
* de la parabole sera toujours horizontal. La courbe quadratique produite ressemblera alors à une parabole classique
|
341 |
* telle qu'on en voit dans les ouvrages de mathématiques élémentaires. La valeur {@link #PARALLEL} indique plutôt que
|
342 |
* l'axe des <var>x</var> de la parabole doit être parallèle à la droite joignant les points {@code (x0,y0)} et
|
343 |
* {@code (x2,y2)}. Ce dernier type produira le même résultat que {@link #HORIZONTAL} si {@code y0==y2}.
|
344 |
*
|
345 |
* @param orientation Orientation de l'axe des <var>x</var> de la parabole: {@link #PARALLEL} ou {@link #HORIZONTAL}.
|
346 |
* @return Une courbe quadratique passant par les trois points spécifiés. La courbe commencera au point {@code (x0,y0)}
|
347 |
* et se terminera au point {@code (x2,y2)}. Si deux points ont des coordonnées presque identiques, ou si les
|
348 |
* trois points sont colinéaires, alors cette méthode retourne {@code null}.
|
349 |
* @throws IllegalArgumentException si l'argument {@code orientation} n'est pas une des constantes valides.
|
350 |
*/
|
351 |
public static QuadCurve2D fitParabol(final double x0, final double y0, |
352 |
final double x1, final double y1, |
353 |
final double x2, final double y2, |
354 |
final int orientation) throws IllegalArgumentException |
355 |
{ |
356 |
final Point2D p=parabolicControlPoint(x0, y0, x1, y1, x2, y2, orientation, null); |
357 |
return (p!=null) ? new QuadCurve2D.Double(x0, y0, p.getX(), p.getY(), x2, y2) : null; |
358 |
} |
359 |
|
360 |
/**
|
361 |
* Retourne le point de contrôle d'une courbe quadratique passant par les trois points spécifiés.
|
362 |
* Il peut exister une infinité de courbes quadratiques passant par trois points. On peut voir
|
363 |
* les choses en disant qu'une courbe quadratique correspond à une parabole produite par une
|
364 |
* équation de la forme <code>y=ax²+bx+c</code>, mais que l'axe des <var>x</var> de cette
|
365 |
* équation n'est pas nécessairement horizontal. La direction de cet axe des <var>x</var> dépend
|
366 |
* du paramètre {@code orientation} spécifié à cette méthode. La valeur {@link #HORIZONTAL}
|
367 |
* signifie que l'axe des <var>x</var> de la parabole sera toujours horizontal. La courbe
|
368 |
* quadratique produite ressemblera alors à une parabole classique telle qu'on en voit dans les
|
369 |
* ouvrages de mathématiques élémentaires. La valeur {@link #PARALLEL} indique plutôt que l'axe
|
370 |
* des <var>x</var> de la parabole doit être parallèle à la droite joignant les points
|
371 |
* {@code (x0,y0)} et {@code (x2,y2)}. Ce dernier type produira le même résultat que
|
372 |
* {@link #HORIZONTAL} si {@code y0==y2}.
|
373 |
*
|
374 |
* @param orientation Orientation de l'axe des <var>x</var> de la parabole: {@link #PARALLEL}
|
375 |
* ou {@link #HORIZONTAL}.
|
376 |
* @return Le point de contrôle d'une courbe quadratique passant par les trois points spécifiés.
|
377 |
* La courbe commencera au point {@code (x0,y0)} et se terminera au point {@code (x2,y2)}.
|
378 |
* Si deux points ont des coordonnées presque identiques, ou si les trois points sont
|
379 |
* colinéaires, alors cette méthode retourne {@code null}.
|
380 |
* @throws IllegalArgumentException si l'argument {@code orientation} n'est pas une des
|
381 |
* constantes valides.
|
382 |
*/
|
383 |
public static Point2D parabolicControlPoint(final double x0, final double y0, |
384 |
double x1, double y1, |
385 |
double x2, double y2, |
386 |
final int orientation, final Point2D dest) |
387 |
throws IllegalArgumentException |
388 |
{ |
389 |
/*
|
390 |
* Applique une translation de façon à ce que (x0,y0)
|
391 |
* devienne l'origine du système d'axes. Il ne faudra
|
392 |
* plus utiliser (x0,y0) avant la fin de ce code.
|
393 |
*/
|
394 |
x1 -= x0; |
395 |
y1 -= y0; |
396 |
x2 -= x0; |
397 |
y2 -= y0; |
398 |
switch (orientation) {
|
399 |
case PARALLEL: {
|
400 |
/*
|
401 |
* Applique une rotation de façon à ce que (x2,y2)
|
402 |
* tombe sur l'axe des x, c'est-à-dire que y2=0.
|
403 |
*/
|
404 |
final double rx2 = x2; |
405 |
final double ry2 = y2; |
406 |
x2 = Math.hypot(x2,y2);
|
407 |
y2 = (x1*rx2 + y1*ry2)/x2; // use 'y2' as a temporary variable for 'x1'
|
408 |
y1 = (y1*rx2 - x1*ry2)/x2; |
409 |
x1 = y2; |
410 |
y2 = 0;
|
411 |
/*
|
412 |
* Calcule maintenant les coordonnées du point
|
413 |
* de contrôle selon le nouveau système d'axes.
|
414 |
*/
|
415 |
final double x = 0.5; // Really "x/x2" |
416 |
final double y = (y1*x*x2) / (x1*(x2-x1)); // Really "y/y2" |
417 |
final double check = Math.abs(y); |
418 |
if (!(check <= 1/EPS)) return null; // Deux points ont les mêmes coordonnées. |
419 |
if (!(check >= EPS)) return null; // Les trois points sont colinéaires. |
420 |
/*
|
421 |
* Applique une rotation inverse puis une translation pour
|
422 |
* ramener le système d'axe dans sa position d'origine.
|
423 |
*/
|
424 |
x1 = (x*rx2 - y*ry2) + x0; |
425 |
y1 = (y*rx2 + x*ry2) + y0; |
426 |
break;
|
427 |
} |
428 |
case HORIZONTAL: {
|
429 |
final double a = (y2 - y1*x2/x1)/(x2-x1); // Really "a*x2" |
430 |
final double check = Math.abs(a); |
431 |
if (!(check <= 1/EPS)) return null; // Deux points ont les mêmes coordonnées. |
432 |
if (!(check >= EPS)) return null; // Les trois points sont colinéaires. |
433 |
final double b = y2/x2 - a; |
434 |
x1 = (1 + b/(2*a))*x2 - y2/(2*a); |
435 |
y1 = y0 + b*x1; |
436 |
x1 += x0; |
437 |
break;
|
438 |
} |
439 |
default: throw new IllegalArgumentException(); |
440 |
} |
441 |
if (dest!=null) { |
442 |
dest.setLocation(x1,y1); |
443 |
return dest;
|
444 |
} else {
|
445 |
return new Point2D.Double(x1,y1); |
446 |
} |
447 |
} |
448 |
|
449 |
/**
|
450 |
* Retourne un cercle qui passe par
|
451 |
* chacun des trois points spécifiés.
|
452 |
*/
|
453 |
public static Ellipse2D fitCircle(final Point2D P1, final Point2D P2, final Point2D P3) |
454 |
{ |
455 |
final Point2D center = circleCentre(P1.getX(), P1.getY(), |
456 |
P2.getX(), P2.getY(), |
457 |
P3.getX(), P3.getY()); |
458 |
final double radius = center.distance(P2); |
459 |
return new Ellipse2D.Double(center.getX()-radius, |
460 |
center.getY()-radius, |
461 |
2*radius, 2*radius); |
462 |
} |
463 |
|
464 |
/**
|
465 |
* Retourne la coordonnée centrale d'un cercle passant
|
466 |
* pas les trois points spécifiés. La distance entre
|
467 |
* le point retourné et n'importe quel des points
|
468 |
* (<var>x</var><sub>1</sub>,<var>y</var><sub>1</sub>),
|
469 |
* (<var>x</var><sub>2</sub>,<var>y</var><sub>2</sub>),
|
470 |
* (<var>x</var><sub>3</sub>,<var>y</var><sub>3</sub>)
|
471 |
* sera constante; ce sera le rayon d'un cercle centré
|
472 |
* au point retourné et passant par les trois points
|
473 |
* spécifiés.
|
474 |
*/
|
475 |
public static Point2D circleCentre(double x1, double y1, |
476 |
double x2, double y2, |
477 |
double x3, double y3) |
478 |
{ |
479 |
x2 -= x1; |
480 |
x3 -= x1; |
481 |
y2 -= y1; |
482 |
y3 -= y1; |
483 |
final double sq2 = (x2*x2 + y2*y2); |
484 |
final double sq3 = (x3*x3 + y3*y3); |
485 |
final double x = (y2*sq3 - y3*sq2) / (y2*x3 - y3*x2); |
486 |
return new Point2D.Double(x1+0.5*x, y1+0.5*(sq2-x*x2)/y2); |
487 |
} |
488 |
|
489 |
/**
|
490 |
* Tente de remplacer la forme géométrique {@code path} par une des formes standards
|
491 |
* de Java2D. Par exemple, si {@code path} ne contient qu'un simple segment de droite
|
492 |
* ou une courbe quadratique, alors cette méthode retournera un objet {@link Line2D} ou
|
493 |
* {@link QuadCurve2D} respectivement.
|
494 |
*
|
495 |
* @param path Forme géométrique à simplifier (généralement un objet {@link GeneralPath}).
|
496 |
* @return Forme géométrique standard, ou {@code path} si aucun remplacement n'est proposé.
|
497 |
*/
|
498 |
public static Shape toPrimitive(final Shape path) { |
499 |
final float[] buffer=new float[6]; |
500 |
final PathIterator it=path.getPathIterator(null); |
501 |
if (!it.isDone() && it.currentSegment(buffer)==PathIterator.SEG_MOVETO && !it.isDone()) { |
502 |
final float x1 = buffer[0]; |
503 |
final float y1 = buffer[1]; |
504 |
final int code=it.currentSegment(buffer); |
505 |
if (it.isDone()) {
|
506 |
switch (code) {
|
507 |
case PathIterator.SEG_LINETO: return new Line2D.Float(x1,y1, buffer[0],buffer[1]); |
508 |
case PathIterator.SEG_QUADTO: return new QuadCurve2D.Float(x1,y1, buffer[0],buffer[1], buffer[2],buffer[3]); |
509 |
case PathIterator.SEG_CUBICTO: return new CubicCurve2D.Float(x1,y1, buffer[0],buffer[1], buffer[2],buffer[3], buffer[4],buffer[5]); |
510 |
} |
511 |
} |
512 |
} |
513 |
return path;
|
514 |
} |
515 |
|
516 |
/**
|
517 |
* Returns a suggested value for the {@code flatness} argument in
|
518 |
* {@link Shape#getPathIterator(AffineTransform,double)} for the specified shape.
|
519 |
*/
|
520 |
public static double getFlatness(final Shape shape) { |
521 |
final Rectangle2D bounds = shape.getBounds2D(); |
522 |
final double dx = bounds.getWidth(); |
523 |
final double dy = bounds.getHeight(); |
524 |
return Math.max(0.025*Math.min(dx, dy), |
525 |
0.001*Math.max(dx, dy)); |
526 |
} |
527 |
} |