OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [libjava/] [classpath/] [java/] [awt/] [geom/] [Arc2D.java] - Blame information for rev 14

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 14 jlechner
/* Arc2D.java -- represents an arc in 2-D space
2
   Copyright (C) 2002, 2003, 2004 Free Software Foundation
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
package java.awt.geom;
39
 
40
import java.util.NoSuchElementException;
41
 
42
 
43
/**
44
 * This class represents all arcs (segments of an ellipse in 2-D space). The
45
 * arcs are defined by starting angle and extent (arc length) in degrees, as
46
 * opposed to radians (like the rest of Java), and can be open, chorded, or
47
 * wedge shaped. The angles are skewed according to the ellipse, so that 45
48
 * degrees always points to the upper right corner (positive x, negative y)
49
 * of the bounding rectangle. A positive extent draws a counterclockwise arc,
50
 * and while the angle can be any value, the path iterator only traverses the
51
 * first 360 degrees. Storage is up to the subclasses.
52
 *
53
 * @author Eric Blake (ebb9@email.byu.edu)
54
 * @author Sven de Marothy (sven@physto.se)
55
 * @since 1.2
56
 */
57
public abstract class Arc2D extends RectangularShape
58
{
59
  /**
60
   * An open arc, with no segment connecting the endpoints. This type of
61
   * arc still contains the same points as a chorded version.
62
   */
63
  public static final int OPEN = 0;
64
 
65
  /**
66
   * A closed arc with a single segment connecting the endpoints (a chord).
67
   */
68
  public static final int CHORD = 1;
69
 
70
  /**
71
   * A closed arc with two segments, one from each endpoint, meeting at the
72
   * center of the ellipse.
73
   */
74
  public static final int PIE = 2;
75
 
76
  /** The closure type of this arc.  This is package-private to avoid an
77
   * accessor method.  */
78
  int type;
79
 
80
  /**
81
   * Create a new arc, with the specified closure type.
82
   *
83
   * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}.
84
   * @throws IllegalArgumentException if type is invalid
85
   */
86
  protected Arc2D(int type)
87
  {
88
    if (type < OPEN || type > PIE)
89
      throw new IllegalArgumentException();
90
    this.type = type;
91
  }
92
 
93
  /**
94
   * Get the starting angle of the arc in degrees.
95
   *
96
   * @return the starting angle
97
   * @see #setAngleStart(double)
98
   */
99
  public abstract double getAngleStart();
100
 
101
  /**
102
   * Get the extent angle of the arc in degrees.
103
   *
104
   * @return the extent angle
105
   * @see #setAngleExtent(double)
106
   */
107
  public abstract double getAngleExtent();
108
 
109
  /**
110
   * Return the closure type of the arc.
111
   *
112
   * @return the closure type
113
   * @see #OPEN
114
   * @see #CHORD
115
   * @see #PIE
116
   * @see #setArcType(int)
117
   */
118
  public int getArcType()
119
  {
120
    return type;
121
  }
122
 
123
  /**
124
   * Returns the starting point of the arc.
125
   *
126
   * @return the start point
127
   */
128
  public Point2D getStartPoint()
129
  {
130
    double angle = Math.toRadians(getAngleStart());
131
    double rx = getWidth() / 2;
132
    double ry = getHeight() / 2;
133
    double x = getX() + rx + rx * Math.cos(angle);
134
    double y = getY() + ry - ry * Math.sin(angle);
135
    return new Point2D.Double(x, y);
136
  }
137
 
138
  /**
139
   * Returns the ending point of the arc.
140
   *
141
   * @return the end point
142
   */
143
  public Point2D getEndPoint()
144
  {
145
    double angle = Math.toRadians(getAngleStart() + getAngleExtent());
146
    double rx = getWidth() / 2;
147
    double ry = getHeight() / 2;
148
    double x = getX() + rx + rx * Math.cos(angle);
149
    double y = getY() + ry - ry * Math.sin(angle);
150
    return new Point2D.Double(x, y);
151
  }
152
 
153
  /**
154
   * Set the parameters of the arc. The angles are in degrees, and a positive
155
   * extent sweeps counterclockwise (from the positive x-axis to the negative
156
   * y-axis).
157
   *
158
   * @param x the new x coordinate of the upper left of the bounding box
159
   * @param y the new y coordinate of the upper left of the bounding box
160
   * @param w the new width of the bounding box
161
   * @param h the new height of the bounding box
162
   * @param start the start angle, in degrees
163
   * @param extent the arc extent, in degrees
164
   * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
165
   * @throws IllegalArgumentException if type is invalid
166
   */
167
  public abstract void setArc(double x, double y, double w, double h,
168
                              double start, double extent, int type);
169
 
170
  /**
171
   * Set the parameters of the arc. The angles are in degrees, and a positive
172
   * extent sweeps counterclockwise (from the positive x-axis to the negative
173
   * y-axis).
174
   *
175
   * @param p the upper left point of the bounding box
176
   * @param d the dimensions of the bounding box
177
   * @param start the start angle, in degrees
178
   * @param extent the arc extent, in degrees
179
   * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
180
   * @throws IllegalArgumentException if type is invalid
181
   * @throws NullPointerException if p or d is null
182
   */
183
  public void setArc(Point2D p, Dimension2D d, double start, double extent,
184
                     int type)
185
  {
186
    setArc(p.getX(), p.getY(), d.getWidth(), d.getHeight(), start, extent, type);
187
  }
188
 
189
  /**
190
   * Set the parameters of the arc. The angles are in degrees, and a positive
191
   * extent sweeps counterclockwise (from the positive x-axis to the negative
192
   * y-axis).
193
   *
194
   * @param r the new bounding box
195
   * @param start the start angle, in degrees
196
   * @param extent the arc extent, in degrees
197
   * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
198
   * @throws IllegalArgumentException if type is invalid
199
   * @throws NullPointerException if r is null
200
   */
201
  public void setArc(Rectangle2D r, double start, double extent, int type)
202
  {
203
    setArc(r.getX(), r.getY(), r.getWidth(), r.getHeight(), start, extent, type);
204
  }
205
 
206
  /**
207
   * Set the parameters of the arc from the given one.
208
   *
209
   * @param a the arc to copy
210
   * @throws NullPointerException if a is null
211
   */
212
  public void setArc(Arc2D a)
213
  {
214
    setArc(a.getX(), a.getY(), a.getWidth(), a.getHeight(), a.getAngleStart(),
215
           a.getAngleExtent(), a.getArcType());
216
  }
217
 
218
  /**
219
   * Set the parameters of the arc. The angles are in degrees, and a positive
220
   * extent sweeps counterclockwise (from the positive x-axis to the negative
221
   * y-axis). This controls the center point and radius, so the arc will be
222
   * circular.
223
   *
224
   * @param x the x coordinate of the center of the circle
225
   * @param y the y coordinate of the center of the circle
226
   * @param r the radius of the circle
227
   * @param start the start angle, in degrees
228
   * @param extent the arc extent, in degrees
229
   * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
230
   * @throws IllegalArgumentException if type is invalid
231
   */
232
  public void setArcByCenter(double x, double y, double r, double start,
233
                             double extent, int type)
234
  {
235
    setArc(x - r, y - r, r + r, r + r, start, extent, type);
236
  }
237
 
238
  /**
239
   * Sets the parameters of the arc by finding the tangents of two lines, and
240
   * using the specified radius. The arc will be circular, will begin on the
241
   * tangent point of the line extending from p1 to p2, and will end on the
242
   * tangent point of the line extending from p2 to p3.
243
   *
244
   * XXX What happens if the points are colinear, or the radius negative?
245
   *
246
   * @param p1 the first point
247
   * @param p2 the tangent line intersection point
248
   * @param p3 the third point
249
   * @param r the radius of the arc
250
   * @throws NullPointerException if any point is null
251
   */
252
  public void setArcByTangent(Point2D p1, Point2D p2, Point2D p3, double r)
253
  {
254
    if ((p2.getX() - p1.getX()) * (p3.getY() - p1.getY())
255
        - (p3.getX() - p1.getX()) * (p2.getY() - p1.getY()) > 0)
256
      {
257
        Point2D p = p3;
258
        p3 = p1;
259
        p1 = p;
260
      }
261
 
262
    // normalized tangent vectors
263
    double dx1 = (p1.getX() - p2.getX()) / p1.distance(p2);
264
    double dy1 = (p1.getY() - p2.getY()) / p1.distance(p2);
265
    double dx2 = (p2.getX() - p3.getX()) / p3.distance(p2);
266
    double dy2 = (p2.getY() - p3.getY()) / p3.distance(p2);
267
    double theta1 = Math.atan2(dx1, dy1);
268
    double theta2 = Math.atan2(dx2, dy2);
269
 
270
    double dx = r * Math.cos(theta2) - r * Math.cos(theta1);
271
    double dy = -r * Math.sin(theta2) + r * Math.sin(theta1);
272
 
273
    if (theta1 < 0)
274
      theta1 += 2 * Math.PI;
275
    if (theta2 < 0)
276
      theta2 += 2 * Math.PI;
277
    if (theta2 < theta1)
278
      theta2 += 2 * Math.PI;
279
 
280
    // Vectors of the lines, not normalized, note we change 
281
    // the direction of line 2.
282
    dx1 = p1.getX() - p2.getX();
283
    dy1 = p1.getY() - p2.getY();
284
    dx2 = p3.getX() - p2.getX();
285
    dy2 = p3.getY() - p2.getY();
286
 
287
    // Calculate the tangent point to the second line
288
    double t2 = -(dx1 * dy - dy1 * dx) / (dx2 * dy1 - dx1 * dy2);
289
    double x2 = t2 * (p3.getX() - p2.getX()) + p2.getX();
290
    double y2 = t2 * (p3.getY() - p2.getY()) + p2.getY();
291
 
292
    // calculate the center point
293
    double x = x2 - r * Math.cos(theta2);
294
    double y = y2 + r * Math.sin(theta2);
295
 
296
    setArc(x - r, y - r, 2 * r, 2 * r, Math.toDegrees(theta1),
297
           Math.toDegrees(theta2 - theta1), getArcType());
298
  }
299
 
300
  /**
301
   * Set the start, in degrees.
302
   *
303
   * @param start the new start angle
304
   * @see #getAngleStart()
305
   */
306
  public abstract void setAngleStart(double start);
307
 
308
  /**
309
   * Set the extent, in degrees.
310
   *
311
   * @param extent the new extent angle
312
   * @see #getAngleExtent()
313
   */
314
  public abstract void setAngleExtent(double extent);
315
 
316
  /**
317
   * Sets the starting angle to the angle of the given point relative to
318
   * the center of the arc. The extent remains constant; in other words,
319
   * this rotates the arc.
320
   *
321
   * @param p the new start point
322
   * @throws NullPointerException if p is null
323
   * @see #getStartPoint()
324
   * @see #getAngleStart()
325
   */
326
  public void setAngleStart(Point2D p)
327
  {
328
    // Normalize.
329
    double x = p.getX() - (getX() + getWidth() / 2);
330
    double y = p.getY() - (getY() + getHeight() / 2);
331
    setAngleStart(Math.toDegrees(Math.atan2(-y, x)));
332
  }
333
 
334
  /**
335
   * Sets the starting and extent angles to those of the given points
336
   * relative to the center of the arc. The arc will be non-empty, and will
337
   * extend counterclockwise.
338
   *
339
   * @param x1 the first x coordinate
340
   * @param y1 the first y coordinate
341
   * @param x2 the second x coordinate
342
   * @param y2 the second y coordinate
343
   * @see #setAngleStart(Point2D)
344
   */
345
  public void setAngles(double x1, double y1, double x2, double y2)
346
  {
347
    // Normalize the points.
348
    double mx = getX();
349
    double my = getY();
350
    double mw = getWidth();
351
    double mh = getHeight();
352
    x1 = x1 - (mx + mw / 2);
353
    y1 = y1 - (my + mh / 2);
354
    x2 = x2 - (mx + mw / 2);
355
    y2 = y2 - (my + mh / 2);
356
    double start = Math.toDegrees(Math.atan2(-y1, x1));
357
    double extent = Math.toDegrees(Math.atan2(-y2, x2)) - start;
358
    if (extent < 0)
359
      extent += 360;
360
    setAngleStart(start);
361
    setAngleExtent(extent);
362
  }
363
 
364
  /**
365
   * Sets the starting and extent angles to those of the given points
366
   * relative to the center of the arc. The arc will be non-empty, and will
367
   * extend counterclockwise.
368
   *
369
   * @param p1 the first point
370
   * @param p2 the second point
371
   * @throws NullPointerException if either point is null
372
   * @see #setAngleStart(Point2D)
373
   */
374
  public void setAngles(Point2D p1, Point2D p2)
375
  {
376
    setAngles(p1.getX(), p1.getY(), p2.getX(), p2.getY());
377
  }
378
 
379
  /**
380
   * Set the closure type of this arc.
381
   *
382
   * @param type one of {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
383
   * @throws IllegalArgumentException if type is invalid
384
   * @see #getArcType()
385
   */
386
  public void setArcType(int type)
387
  {
388
    if (type < OPEN || type > PIE)
389
      throw new IllegalArgumentException();
390
    this.type = type;
391
  }
392
 
393
  /**
394
   * Sets the location and bounds of the ellipse of which this arc is a part.
395
   *
396
   * @param x the new x coordinate
397
   * @param y the new y coordinate
398
   * @param w the new width
399
   * @param h the new height
400
   * @see #getFrame()
401
   */
402
  public void setFrame(double x, double y, double w, double h)
403
  {
404
    setArc(x, y, w, h, getAngleStart(), getAngleExtent(), type);
405
  }
406
 
407
  /**
408
   * Gets the bounds of the arc. This is much tighter than
409
   * <code>getBounds</code>, as it takes into consideration the start and
410
   * end angles, and the center point of a pie wedge, rather than just the
411
   * overall ellipse.
412
   *
413
   * @return the bounds of the arc
414
   * @see #getBounds()
415
   */
416
  public Rectangle2D getBounds2D()
417
  {
418
    double extent = getAngleExtent();
419
    if (Math.abs(extent) >= 360)
420
      return makeBounds(getX(), getY(), getWidth(), getHeight());
421
 
422
    // Find the minimal bounding box.  This determined by its extrema,
423
    // which are the center, the endpoints of the arc, and any local
424
    // maximum contained by the arc.
425
    double rX = getWidth() / 2;
426
    double rY = getHeight() / 2;
427
    double centerX = getX() + rX;
428
    double centerY = getY() + rY;
429
 
430
    Point2D p1 = getStartPoint();
431
    Rectangle2D result = makeBounds(p1.getX(), p1.getY(), 0, 0);
432
    result.add(getEndPoint());
433
 
434
    if (type == PIE)
435
      result.add(centerX, centerY);
436
    if (containsAngle(0))
437
      result.add(centerX + rX, centerY);
438
    if (containsAngle(90))
439
      result.add(centerX, centerY - rY);
440
    if (containsAngle(180))
441
      result.add(centerX - rX, centerY);
442
    if (containsAngle(270))
443
      result.add(centerX, centerY + rY);
444
 
445
    return result;
446
  }
447
 
448
  /**
449
   * Construct a bounding box in a precision appropriate for the subclass.
450
   *
451
   * @param x the x coordinate
452
   * @param y the y coordinate
453
   * @param w the width
454
   * @param h the height
455
   * @return the rectangle for use in getBounds2D
456
   */
457
  protected abstract Rectangle2D makeBounds(double x, double y, double w,
458
                                            double h);
459
 
460
  /**
461
   * Tests if the given angle, in degrees, is included in the arc.
462
   * All angles are normalized to be between 0 and 360 degrees.
463
   *
464
   * @param a the angle to test
465
   * @return true if it is contained
466
   */
467
  public boolean containsAngle(double a)
468
  {
469
    double start = getAngleStart();
470
    double extent = getAngleExtent();
471
    double end = start + extent;
472
 
473
    if (extent == 0)
474
      return false;
475
 
476
    if (extent >= 360 || extent <= -360)
477
      return true;
478
 
479
    if (extent < 0)
480
      {
481
        end = start;
482
        start += extent;
483
      }
484
 
485
    start %= 360;
486
    while (start < 0)
487
      start += 360;
488
 
489
    end %= 360;
490
    while (end < start)
491
      end += 360;
492
 
493
    a %= 360;
494
    while (a < start)
495
      a += 360;
496
 
497
    return a >= start && a < end; // starting angle included, ending angle not
498
  }
499
 
500
  /**
501
   * Determines if the arc contains the given point. If the bounding box
502
   * is empty, then this will return false.
503
   *
504
   * The area considered 'inside' an arc of type OPEN is the same as the
505
   * area inside an equivalent filled CHORD-type arc. The area considered
506
   * 'inside' a CHORD-type arc is the same as the filled area.
507
   *
508
   * @param x the x coordinate to test
509
   * @param y the y coordinate to test
510
   * @return true if the point is inside the arc
511
   */
512
  public boolean contains(double x, double y)
513
  {
514
    double w = getWidth();
515
    double h = getHeight();
516
    double extent = getAngleExtent();
517
    if (w <= 0 || h <= 0 || extent == 0)
518
      return false;
519
 
520
    double mx = getX() + w / 2;
521
    double my = getY() + h / 2;
522
    double dx = (x - mx) * 2 / w;
523
    double dy = (y - my) * 2 / h;
524
    if ((dx * dx + dy * dy) >= 1.0)
525
      return false;
526
 
527
    double angle = Math.toDegrees(Math.atan2(-dy, dx));
528
    if (getArcType() == PIE)
529
      return containsAngle(angle);
530
 
531
    double a1 = Math.toRadians(getAngleStart());
532
    double a2 = Math.toRadians(getAngleStart() + extent);
533
    double x1 = mx + getWidth() * Math.cos(a1) / 2;
534
    double y1 = my - getHeight() * Math.sin(a1) / 2;
535
    double x2 = mx + getWidth() * Math.cos(a2) / 2;
536
    double y2 = my - getHeight() * Math.sin(a2) / 2;
537
    double sgn = ((x2 - x1) * (my - y1) - (mx - x1) * (y2 - y1)) * ((x2 - x1) * (y
538
                 - y1) - (x - x1) * (y2 - y1));
539
 
540
    if (Math.abs(extent) > 180)
541
      {
542
        if (containsAngle(angle))
543
          return true;
544
        return sgn > 0;
545
      }
546
    else
547
      {
548
        if (! containsAngle(angle))
549
          return false;
550
        return sgn < 0;
551
      }
552
  }
553
 
554
  /**
555
   * Tests if a given rectangle intersects the area of the arc.
556
   *
557
   * For a definition of the 'inside' area, see the contains() method.
558
   * @see #contains(double, double)
559
   *
560
   * @param x the x coordinate of the rectangle
561
   * @param y the y coordinate of the rectangle
562
   * @param w the width of the rectangle
563
   * @param h the height of the rectangle
564
   * @return true if the two shapes share common points
565
   */
566
  public boolean intersects(double x, double y, double w, double h)
567
  {
568
    double extent = getAngleExtent();
569
    if (extent == 0)
570
      return false;
571
 
572
    if (contains(x, y) || contains(x, y + h) || contains(x + w, y)
573
        || contains(x + w, y + h))
574
      return true;
575
 
576
    Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
577
 
578
    double a = getWidth() / 2.0;
579
    double b = getHeight() / 2.0;
580
 
581
    double mx = getX() + a;
582
    double my = getY() + b;
583
    double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart()));
584
    double y1 = my - b * Math.sin(Math.toRadians(getAngleStart()));
585
    double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent));
586
    double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent));
587
 
588
    if (getArcType() != CHORD)
589
      {
590
        // check intersections against the pie radii
591
        if (rect.intersectsLine(mx, my, x1, y1))
592
          return true;
593
        if (rect.intersectsLine(mx, my, x2, y2))
594
          return true;
595
      }
596
    else// check the chord
597
    if (rect.intersectsLine(x1, y1, x2, y2))
598
      return true;
599
 
600
    // Check the Arc segment against the four edges
601
    double dx;
602
 
603
    // Check the Arc segment against the four edges
604
    double dy;
605
    dy = y - my;
606
    dx = a * Math.sqrt(1 - ((dy * dy) / (b * b)));
607
    if (! java.lang.Double.isNaN(dx))
608
      {
609
        if (mx + dx >= x && mx + dx <= x + w
610
            && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
611
          return true;
612
        if (mx - dx >= x && mx - dx <= x + w
613
            && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
614
          return true;
615
      }
616
    dy = (y + h) - my;
617
    dx = a * Math.sqrt(1 - ((dy * dy) / (b * b)));
618
    if (! java.lang.Double.isNaN(dx))
619
      {
620
        if (mx + dx >= x && mx + dx <= x + w
621
            && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
622
          return true;
623
        if (mx - dx >= x && mx - dx <= x + w
624
            && containsAngle(Math.toDegrees(Math.atan2(-dy, -dx))))
625
          return true;
626
      }
627
    dx = x - mx;
628
    dy = b * Math.sqrt(1 - ((dx * dx) / (a * a)));
629
    if (! java.lang.Double.isNaN(dy))
630
      {
631
        if (my + dy >= y && my + dy <= y + h
632
            && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
633
          return true;
634
        if (my - dy >= y && my - dy <= y + h
635
            && containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
636
          return true;
637
      }
638
 
639
    dx = (x + w) - mx;
640
    dy = b * Math.sqrt(1 - ((dx * dx) / (a * a)));
641
    if (! java.lang.Double.isNaN(dy))
642
      {
643
        if (my + dy >= y && my + dy <= y + h
644
            && containsAngle(Math.toDegrees(Math.atan2(-dy, dx))))
645
          return true;
646
        if (my - dy >= y && my - dy <= y + h
647
            && containsAngle(Math.toDegrees(Math.atan2(dy, dx))))
648
          return true;
649
      }
650
 
651
    // Check whether the arc is contained within the box
652
    if (rect.contains(mx, my))
653
      return true;
654
 
655
    return false;
656
  }
657
 
658
  /**
659
   * Tests if a given rectangle is contained in the area of the arc.
660
   *
661
   * @param x the x coordinate of the rectangle
662
   * @param y the y coordinate of the rectangle
663
   * @param w the width of the rectangle
664
   * @param h the height of the rectangle
665
   * @return true if the arc contains the rectangle
666
   */
667
  public boolean contains(double x, double y, double w, double h)
668
  {
669
    double extent = getAngleExtent();
670
    if (extent == 0)
671
      return false;
672
 
673
    if (! (contains(x, y) && contains(x, y + h) && contains(x + w, y)
674
        && contains(x + w, y + h)))
675
      return false;
676
 
677
    Rectangle2D rect = new Rectangle2D.Double(x, y, w, h);
678
 
679
    double a = getWidth() / 2.0;
680
    double b = getHeight() / 2.0;
681
 
682
    double mx = getX() + a;
683
    double my = getY() + b;
684
    double x1 = mx + a * Math.cos(Math.toRadians(getAngleStart()));
685
    double y1 = my - b * Math.sin(Math.toRadians(getAngleStart()));
686
    double x2 = mx + a * Math.cos(Math.toRadians(getAngleStart() + extent));
687
    double y2 = my - b * Math.sin(Math.toRadians(getAngleStart() + extent));
688
    if (getArcType() != CHORD)
689
      {
690
        // check intersections against the pie radii
691
        if (rect.intersectsLine(mx, my, x1, y1))
692
          return false;
693
 
694
        if (rect.intersectsLine(mx, my, x2, y2))
695
          return false;
696
      }
697
    else if (rect.intersectsLine(x1, y1, x2, y2))
698
      return false;
699
    return true;
700
  }
701
 
702
  /**
703
   * Tests if a given rectangle is contained in the area of the arc.
704
   *
705
   * @param r the rectangle
706
   * @return true if the arc contains the rectangle
707
   */
708
  public boolean contains(Rectangle2D r)
709
  {
710
    return contains(r.getX(), r.getY(), r.getWidth(), r.getHeight());
711
  }
712
 
713
  /**
714
   * Returns an iterator over this arc, with an optional transformation.
715
   * This iterator is threadsafe, so future modifications to the arc do not
716
   * affect the iteration.
717
   *
718
   * @param at the transformation, or null
719
   * @return a path iterator
720
   */
721
  public PathIterator getPathIterator(AffineTransform at)
722
  {
723
    return new ArcIterator(this, at);
724
  }
725
 
726
  /**
727
   * This class is used to iterate over an arc. Since ellipses are a subclass
728
   * of arcs, this is used by Ellipse2D as well.
729
   *
730
   * @author Eric Blake (ebb9@email.byu.edu)
731
   */
732
  static final class ArcIterator implements PathIterator
733
  {
734
    /** The current iteration. */
735
    private int current;
736
 
737
    /** The last iteration. */
738
    private final int limit;
739
 
740
    /** The optional transformation. */
741
    private final AffineTransform xform;
742
 
743
    /** The x coordinate of the bounding box. */
744
    private final double x;
745
 
746
    /** The y coordinate of the bounding box. */
747
    private final double y;
748
 
749
    /** The width of the bounding box. */
750
    private final double w;
751
 
752
    /** The height of the bounding box. */
753
    private final double h;
754
 
755
    /** The start angle, in radians (not degrees). */
756
    private final double start;
757
 
758
    /** The extent angle, in radians (not degrees). */
759
    private final double extent;
760
 
761
    /** The arc closure type. */
762
    private final int type;
763
 
764
    /**
765
     * Construct a new iterator over an arc.
766
     *
767
     * @param a the arc
768
     * @param xform the transform
769
     */
770
    public ArcIterator(Arc2D a, AffineTransform xform)
771
    {
772
      this.xform = xform;
773
      x = a.getX();
774
      y = a.getY();
775
      w = a.getWidth();
776
      h = a.getHeight();
777
      double start = a.getAngleStart() * (Math.PI / 180);
778
      double extent = a.getAngleExtent() * (Math.PI / 180);
779
 
780
      if (extent < 0)
781
        {
782
          extent = -extent;
783
          start = 2 * Math.PI - extent + start;
784
        }
785
      this.start = start;
786
      this.extent = extent;
787
 
788
      type = a.type;
789
      if (w < 0 || h < 0)
790
        limit = -1;
791
      else if (extent == 0)
792
        limit = type;
793
      else if (extent <= Math.PI / 2.0)
794
        limit = type + 1;
795
      else if (extent <= Math.PI)
796
        limit = type + 2;
797
      else if (extent <= 3.0 * (Math.PI / 2.0))
798
        limit = type + 3;
799
      else
800
        limit = type + 4;
801
    }
802
 
803
    /**
804
     * Construct a new iterator over an ellipse.
805
     *
806
     * @param e the ellipse
807
     * @param xform the transform
808
     */
809
    public ArcIterator(Ellipse2D e, AffineTransform xform)
810
    {
811
      this.xform = xform;
812
      x = e.getX();
813
      y = e.getY();
814
      w = e.getWidth();
815
      h = e.getHeight();
816
      start = 0;
817
      extent = 2 * Math.PI;
818
      type = CHORD;
819
      limit = (w < 0 || h < 0) ? -1 : 5;
820
    }
821
 
822
    /**
823
     * Return the winding rule.
824
     *
825
     * @return {@link PathIterator#WIND_NON_ZERO}
826
     */
827
    public int getWindingRule()
828
    {
829
      return WIND_NON_ZERO;
830
    }
831
 
832
    /**
833
     * Test if the iteration is complete.
834
     *
835
     * @return true if more segments exist
836
     */
837
    public boolean isDone()
838
    {
839
      return current > limit;
840
    }
841
 
842
    /**
843
     * Advance the iterator.
844
     */
845
    public void next()
846
    {
847
      current++;
848
    }
849
 
850
    /**
851
     * Put the current segment into the array, and return the segment type.
852
     *
853
     * @param coords an array of 6 elements
854
     * @return the segment type
855
     * @throws NullPointerException if coords is null
856
     * @throws ArrayIndexOutOfBoundsException if coords is too small
857
     */
858
    public int currentSegment(float[] coords)
859
    {
860
      double[] double_coords = new double[6];
861
      int code = currentSegment(double_coords);
862
      for (int i = 0; i < 6; ++i)
863
        coords[i] = (float) double_coords[i];
864
      return code;
865
    }
866
 
867
    /**
868
     * Put the current segment into the array, and return the segment type.
869
     *
870
     * @param coords an array of 6 elements
871
     * @return the segment type
872
     * @throws NullPointerException if coords is null
873
     * @throws ArrayIndexOutOfBoundsException if coords is too small
874
     */
875
    public int currentSegment(double[] coords)
876
    {
877
      double rx = w / 2;
878
      double ry = h / 2;
879
      double xmid = x + rx;
880
      double ymid = y + ry;
881
 
882
      if (current > limit)
883
        throw new NoSuchElementException("arc iterator out of bounds");
884
 
885
      if (current == 0)
886
        {
887
          coords[0] = xmid + rx * Math.cos(start);
888
          coords[1] = ymid - ry * Math.sin(start);
889
          if (xform != null)
890
            xform.transform(coords, 0, coords, 0, 1);
891
          return SEG_MOVETO;
892
        }
893
 
894
      if (type != OPEN && current == limit)
895
        return SEG_CLOSE;
896
 
897
      if ((current == limit - 1) && (type == PIE))
898
        {
899
          coords[0] = xmid;
900
          coords[1] = ymid;
901
          if (xform != null)
902
            xform.transform(coords, 0, coords, 0, 1);
903
          return SEG_LINETO;
904
        }
905
 
906
      // note that this produces a cubic approximation of the arc segment,
907
      // not a true ellipsoid. there's no ellipsoid path segment code,
908
      // unfortunately. the cubic approximation looks about right, though.
909
      double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0);
910
      double quad = (Math.PI / 2.0);
911
 
912
      double curr_begin = start + (current - 1) * quad;
913
      double curr_extent = Math.min((start + extent) - curr_begin, quad);
914
      double portion_of_a_quadrant = curr_extent / quad;
915
 
916
      double x0 = xmid + rx * Math.cos(curr_begin);
917
      double y0 = ymid - ry * Math.sin(curr_begin);
918
 
919
      double x1 = xmid + rx * Math.cos(curr_begin + curr_extent);
920
      double y1 = ymid - ry * Math.sin(curr_begin + curr_extent);
921
 
922
      AffineTransform trans = new AffineTransform();
923
      double[] cvec = new double[2];
924
      double len = kappa * portion_of_a_quadrant;
925
      double angle = curr_begin;
926
 
927
      // in a hypothetical "first quadrant" setting, our first control
928
      // vector would be sticking up, from [1,0] to [1,kappa].
929
      //
930
      // let us recall however that in java2d, y coords are upside down
931
      // from what one would consider "normal" first quadrant rules, so we
932
      // will *subtract* the y value of this control vector from our first
933
      // point.
934
      cvec[0] = 0;
935
      cvec[1] = len;
936
      trans.scale(rx, ry);
937
      trans.rotate(angle);
938
      trans.transform(cvec, 0, cvec, 0, 1);
939
      coords[0] = x0 + cvec[0];
940
      coords[1] = y0 - cvec[1];
941
 
942
      // control vector #2 would, ideally, be sticking out and to the
943
      // right, in a first quadrant arc segment. again, subtraction of y.
944
      cvec[0] = 0;
945
      cvec[1] = -len;
946
      trans.rotate(curr_extent);
947
      trans.transform(cvec, 0, cvec, 0, 1);
948
      coords[2] = x1 + cvec[0];
949
      coords[3] = y1 - cvec[1];
950
 
951
      // end point
952
      coords[4] = x1;
953
      coords[5] = y1;
954
 
955
      if (xform != null)
956
        xform.transform(coords, 0, coords, 0, 3);
957
 
958
      return SEG_CUBICTO;
959
    }
960
  } // class ArcIterator
961
 
962
  /**
963
   * This class implements an arc in double precision.
964
   *
965
   * @author Eric Blake (ebb9@email.byu.edu)
966
   * @since 1.2
967
   */
968
  public static class Double extends Arc2D
969
  {
970
    /** The x coordinate of the box bounding the ellipse of this arc. */
971
    public double x;
972
 
973
    /** The y coordinate of the box bounding the ellipse of this arc. */
974
    public double y;
975
 
976
    /** The width of the box bounding the ellipse of this arc. */
977
    public double width;
978
 
979
    /** The height of the box bounding the ellipse of this arc. */
980
    public double height;
981
 
982
    /** The start angle of this arc, in degrees. */
983
    public double start;
984
 
985
    /** The extent angle of this arc, in degrees. */
986
    public double extent;
987
 
988
    /**
989
     * Create a new, open arc at (0,0) with 0 extent.
990
     */
991
    public Double()
992
    {
993
      super(OPEN);
994
    }
995
 
996
    /**
997
     * Create a new arc of the given type at (0,0) with 0 extent.
998
     *
999
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1000
     * @throws IllegalArgumentException if type is invalid
1001
     */
1002
    public Double(int type)
1003
    {
1004
      super(type);
1005
    }
1006
 
1007
    /**
1008
     * Create a new arc with the given dimensions.
1009
     *
1010
     * @param x the x coordinate
1011
     * @param y the y coordinate
1012
     * @param w the width
1013
     * @param h the height
1014
     * @param start the start angle, in degrees
1015
     * @param extent the extent, in degrees
1016
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1017
     * @throws IllegalArgumentException if type is invalid
1018
     */
1019
    public Double(double x, double y, double w, double h, double start,
1020
                  double extent, int type)
1021
    {
1022
      super(type);
1023
      this.x = x;
1024
      this.y = y;
1025
      width = w;
1026
      height = h;
1027
      this.start = start;
1028
      this.extent = extent;
1029
    }
1030
 
1031
    /**
1032
     * Create a new arc with the given dimensions.
1033
     *
1034
     * @param r the bounding box
1035
     * @param start the start angle, in degrees
1036
     * @param extent the extent, in degrees
1037
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1038
     * @throws IllegalArgumentException if type is invalid
1039
     * @throws NullPointerException if r is null
1040
     */
1041
    public Double(Rectangle2D r, double start, double extent, int type)
1042
    {
1043
      super(type);
1044
      x = r.getX();
1045
      y = r.getY();
1046
      width = r.getWidth();
1047
      height = r.getHeight();
1048
      this.start = start;
1049
      this.extent = extent;
1050
    }
1051
 
1052
    /**
1053
     * Return the x coordinate of the bounding box.
1054
     *
1055
     * @return the value of x
1056
     */
1057
    public double getX()
1058
    {
1059
      return x;
1060
    }
1061
 
1062
    /**
1063
     * Return the y coordinate of the bounding box.
1064
     *
1065
     * @return the value of y
1066
     */
1067
    public double getY()
1068
    {
1069
      return y;
1070
    }
1071
 
1072
    /**
1073
     * Return the width of the bounding box.
1074
     *
1075
     * @return the value of width
1076
     */
1077
    public double getWidth()
1078
    {
1079
      return width;
1080
    }
1081
 
1082
    /**
1083
     * Return the height of the bounding box.
1084
     *
1085
     * @return the value of height
1086
     */
1087
    public double getHeight()
1088
    {
1089
      return height;
1090
    }
1091
 
1092
    /**
1093
     * Return the start angle of the arc, in degrees.
1094
     *
1095
     * @return the value of start
1096
     */
1097
    public double getAngleStart()
1098
    {
1099
      return start;
1100
    }
1101
 
1102
    /**
1103
     * Return the extent of the arc, in degrees.
1104
     *
1105
     * @return the value of extent
1106
     */
1107
    public double getAngleExtent()
1108
    {
1109
      return extent;
1110
    }
1111
 
1112
    /**
1113
     * Tests if the arc contains points.
1114
     *
1115
     * @return true if the arc has no interior
1116
     */
1117
    public boolean isEmpty()
1118
    {
1119
      return width <= 0 || height <= 0;
1120
    }
1121
 
1122
    /**
1123
     * Sets the arc to the given dimensions.
1124
     *
1125
     * @param x the x coordinate
1126
     * @param y the y coordinate
1127
     * @param w the width
1128
     * @param h the height
1129
     * @param start the start angle, in degrees
1130
     * @param extent the extent, in degrees
1131
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1132
     * @throws IllegalArgumentException if type is invalid
1133
     */
1134
    public void setArc(double x, double y, double w, double h, double start,
1135
                       double extent, int type)
1136
    {
1137
      this.x = x;
1138
      this.y = y;
1139
      width = w;
1140
      height = h;
1141
      this.start = start;
1142
      this.extent = extent;
1143
      setArcType(type);
1144
    }
1145
 
1146
    /**
1147
     * Sets the start angle of the arc.
1148
     *
1149
     * @param start the new start angle
1150
     */
1151
    public void setAngleStart(double start)
1152
    {
1153
      this.start = start;
1154
    }
1155
 
1156
    /**
1157
     * Sets the extent angle of the arc.
1158
     *
1159
     * @param extent the new extent angle
1160
     */
1161
    public void setAngleExtent(double extent)
1162
    {
1163
      this.extent = extent;
1164
    }
1165
 
1166
    /**
1167
     * Creates a tight bounding box given dimensions that more precise than
1168
     * the bounding box of the ellipse.
1169
     *
1170
     * @param x the x coordinate
1171
     * @param y the y coordinate
1172
     * @param w the width
1173
     * @param h the height
1174
     */
1175
    protected Rectangle2D makeBounds(double x, double y, double w, double h)
1176
    {
1177
      return new Rectangle2D.Double(x, y, w, h);
1178
    }
1179
  } // class Double
1180
 
1181
  /**
1182
   * This class implements an arc in float precision.
1183
   *
1184
   * @author Eric Blake (ebb9@email.byu.edu)
1185
   * @since 1.2
1186
   */
1187
  public static class Float extends Arc2D
1188
  {
1189
    /** The x coordinate of the box bounding the ellipse of this arc. */
1190
    public float x;
1191
 
1192
    /** The y coordinate of the box bounding the ellipse of this arc. */
1193
    public float y;
1194
 
1195
    /** The width of the box bounding the ellipse of this arc. */
1196
    public float width;
1197
 
1198
    /** The height of the box bounding the ellipse of this arc. */
1199
    public float height;
1200
 
1201
    /** The start angle of this arc, in degrees. */
1202
    public float start;
1203
 
1204
    /** The extent angle of this arc, in degrees. */
1205
    public float extent;
1206
 
1207
    /**
1208
     * Create a new, open arc at (0,0) with 0 extent.
1209
     */
1210
    public Float()
1211
    {
1212
      super(OPEN);
1213
    }
1214
 
1215
    /**
1216
     * Create a new arc of the given type at (0,0) with 0 extent.
1217
     *
1218
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1219
     * @throws IllegalArgumentException if type is invalid
1220
     */
1221
    public Float(int type)
1222
    {
1223
      super(type);
1224
    }
1225
 
1226
    /**
1227
     * Create a new arc with the given dimensions.
1228
     *
1229
     * @param x the x coordinate
1230
     * @param y the y coordinate
1231
     * @param w the width
1232
     * @param h the height
1233
     * @param start the start angle, in degrees
1234
     * @param extent the extent, in degrees
1235
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1236
     * @throws IllegalArgumentException if type is invalid
1237
     */
1238
    public Float(float x, float y, float w, float h, float start,
1239
                 float extent, int type)
1240
    {
1241
      super(type);
1242
      this.x = x;
1243
      this.y = y;
1244
      width = w;
1245
      height = h;
1246
      this.start = start;
1247
      this.extent = extent;
1248
    }
1249
 
1250
    /**
1251
     * Create a new arc with the given dimensions.
1252
     *
1253
     * @param r the bounding box
1254
     * @param start the start angle, in degrees
1255
     * @param extent the extent, in degrees
1256
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1257
     * @throws IllegalArgumentException if type is invalid
1258
     * @throws NullPointerException if r is null
1259
     */
1260
    public Float(Rectangle2D r, float start, float extent, int type)
1261
    {
1262
      super(type);
1263
      x = (float) r.getX();
1264
      y = (float) r.getY();
1265
      width = (float) r.getWidth();
1266
      height = (float) r.getHeight();
1267
      this.start = start;
1268
      this.extent = (float) extent;
1269
    }
1270
 
1271
    /**
1272
     * Return the x coordinate of the bounding box.
1273
     *
1274
     * @return the value of x
1275
     */
1276
    public double getX()
1277
    {
1278
      return x;
1279
    }
1280
 
1281
    /**
1282
     * Return the y coordinate of the bounding box.
1283
     *
1284
     * @return the value of y
1285
     */
1286
    public double getY()
1287
    {
1288
      return y;
1289
    }
1290
 
1291
    /**
1292
     * Return the width of the bounding box.
1293
     *
1294
     * @return the value of width
1295
     */
1296
    public double getWidth()
1297
    {
1298
      return width;
1299
    }
1300
 
1301
    /**
1302
     * Return the height of the bounding box.
1303
     *
1304
     * @return the value of height
1305
     */
1306
    public double getHeight()
1307
    {
1308
      return height;
1309
    }
1310
 
1311
    /**
1312
     * Return the start angle of the arc, in degrees.
1313
     *
1314
     * @return the value of start
1315
     */
1316
    public double getAngleStart()
1317
    {
1318
      return start;
1319
    }
1320
 
1321
    /**
1322
     * Return the extent of the arc, in degrees.
1323
     *
1324
     * @return the value of extent
1325
     */
1326
    public double getAngleExtent()
1327
    {
1328
      return extent;
1329
    }
1330
 
1331
    /**
1332
     * Tests if the arc contains points.
1333
     *
1334
     * @return true if the arc has no interior
1335
     */
1336
    public boolean isEmpty()
1337
    {
1338
      return width <= 0 || height <= 0;
1339
    }
1340
 
1341
    /**
1342
     * Sets the arc to the given dimensions.
1343
     *
1344
     * @param x the x coordinate
1345
     * @param y the y coordinate
1346
     * @param w the width
1347
     * @param h the height
1348
     * @param start the start angle, in degrees
1349
     * @param extent the extent, in degrees
1350
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1351
     * @throws IllegalArgumentException if type is invalid
1352
     */
1353
    public void setArc(double x, double y, double w, double h, double start,
1354
                       double extent, int type)
1355
    {
1356
      this.x = (float) x;
1357
      this.y = (float) y;
1358
      width = (float) w;
1359
      height = (float) h;
1360
      this.start = (float) start;
1361
      this.extent = (float) extent;
1362
      setArcType(type);
1363
    }
1364
 
1365
    /**
1366
     * Sets the start angle of the arc.
1367
     *
1368
     * @param start the new start angle
1369
     */
1370
    public void setAngleStart(double start)
1371
    {
1372
      this.start = (float) start;
1373
    }
1374
 
1375
    /**
1376
     * Sets the extent angle of the arc.
1377
     *
1378
     * @param extent the new extent angle
1379
     */
1380
    public void setAngleExtent(double extent)
1381
    {
1382
      this.extent = (float) extent;
1383
    }
1384
 
1385
    /**
1386
     * Creates a tight bounding box given dimensions that more precise than
1387
     * the bounding box of the ellipse.
1388
     *
1389
     * @param x the x coordinate
1390
     * @param y the y coordinate
1391
     * @param w the width
1392
     * @param h the height
1393
     */
1394
    protected Rectangle2D makeBounds(double x, double y, double w, double h)
1395
    {
1396
      return new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h);
1397
    }
1398
  } // class Float
1399
} // class Arc2D

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.