Statistics
| Revision:

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&nbsp;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
}