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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [java/] [awt/] [geom/] [Arc2D.java] - Blame information for rev 771

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 771 jeremybenn
/* 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 = Math.toRadians(a.getAngleStart());
778
      double extent = Math.toRadians(a.getAngleExtent());
779
 
780
      this.start = start;
781
      this.extent = extent;
782
 
783
      type = a.type;
784
      if (w < 0 || h < 0)
785
        limit = -1;
786
      else if (extent == 0)
787
        limit = type;
788
      else if (Math.abs(extent) <= Math.PI / 2.0)
789
        limit = type + 1;
790
      else if (Math.abs(extent) <= Math.PI)
791
        limit = type + 2;
792
      else if (Math.abs(extent) <= 3.0 * (Math.PI / 2.0))
793
        limit = type + 3;
794
      else
795
        limit = type + 4;
796
    }
797
 
798
    /**
799
     * Construct a new iterator over an ellipse.
800
     *
801
     * @param e the ellipse
802
     * @param xform the transform
803
     */
804
    public ArcIterator(Ellipse2D e, AffineTransform xform)
805
    {
806
      this.xform = xform;
807
      x = e.getX();
808
      y = e.getY();
809
      w = e.getWidth();
810
      h = e.getHeight();
811
      start = 0;
812
      extent = 2 * Math.PI;
813
      type = CHORD;
814
      limit = (w < 0 || h < 0) ? -1 : 5;
815
    }
816
 
817
    /**
818
     * Return the winding rule.
819
     *
820
     * @return {@link PathIterator#WIND_NON_ZERO}
821
     */
822
    public int getWindingRule()
823
    {
824
      return WIND_NON_ZERO;
825
    }
826
 
827
    /**
828
     * Test if the iteration is complete.
829
     *
830
     * @return true if more segments exist
831
     */
832
    public boolean isDone()
833
    {
834
      return current > limit;
835
    }
836
 
837
    /**
838
     * Advance the iterator.
839
     */
840
    public void next()
841
    {
842
      current++;
843
    }
844
 
845
    /**
846
     * Put the current segment into the array, and return the segment type.
847
     *
848
     * @param coords an array of 6 elements
849
     * @return the segment type
850
     * @throws NullPointerException if coords is null
851
     * @throws ArrayIndexOutOfBoundsException if coords is too small
852
     */
853
    public int currentSegment(float[] coords)
854
    {
855
      double[] double_coords = new double[6];
856
      int code = currentSegment(double_coords);
857
      for (int i = 0; i < 6; ++i)
858
        coords[i] = (float) double_coords[i];
859
      return code;
860
    }
861
 
862
    /**
863
     * Put the current segment into the array, and return the segment type.
864
     *
865
     * @param coords an array of 6 elements
866
     * @return the segment type
867
     * @throws NullPointerException if coords is null
868
     * @throws ArrayIndexOutOfBoundsException if coords is too small
869
     */
870
    public int currentSegment(double[] coords)
871
    {
872
      double rx = w / 2;
873
      double ry = h / 2;
874
      double xmid = x + rx;
875
      double ymid = y + ry;
876
 
877
      if (current > limit)
878
        throw new NoSuchElementException("arc iterator out of bounds");
879
 
880
      if (current == 0)
881
        {
882
          coords[0] = xmid + rx * Math.cos(start);
883
          coords[1] = ymid - ry * Math.sin(start);
884
          if (xform != null)
885
            xform.transform(coords, 0, coords, 0, 1);
886
          return SEG_MOVETO;
887
        }
888
 
889
      if (type != OPEN && current == limit)
890
        return SEG_CLOSE;
891
 
892
      if ((current == limit - 1) && (type == PIE))
893
        {
894
          coords[0] = xmid;
895
          coords[1] = ymid;
896
          if (xform != null)
897
            xform.transform(coords, 0, coords, 0, 1);
898
          return SEG_LINETO;
899
        }
900
 
901
      // note that this produces a cubic approximation of the arc segment,
902
      // not a true ellipsoid. there's no ellipsoid path segment code,
903
      // unfortunately. the cubic approximation looks about right, though.
904
      double kappa = (Math.sqrt(2.0) - 1.0) * (4.0 / 3.0);
905
      double quad = (Math.PI / 2.0);
906
 
907
      double curr_begin;
908
      double curr_extent;
909
      if (extent > 0)
910
        {
911
          curr_begin = start + (current - 1) * quad;
912
          curr_extent = Math.min((start + extent) - curr_begin, quad);
913
        }
914
      else
915
        {
916
          curr_begin = start - (current - 1) * quad;
917
          curr_extent = Math.max((start + extent) - curr_begin, -quad);
918
        }
919
 
920
      double portion_of_a_quadrant = Math.abs(curr_extent / quad);
921
 
922
      double x0 = xmid + rx * Math.cos(curr_begin);
923
      double y0 = ymid - ry * Math.sin(curr_begin);
924
 
925
      double x1 = xmid + rx * Math.cos(curr_begin + curr_extent);
926
      double y1 = ymid - ry * Math.sin(curr_begin + curr_extent);
927
 
928
      AffineTransform trans = new AffineTransform();
929
      double[] cvec = new double[2];
930
      double len = kappa * portion_of_a_quadrant;
931
      double angle = curr_begin;
932
 
933
      // in a hypothetical "first quadrant" setting, our first control
934
      // vector would be sticking up, from [1,0] to [1,kappa].
935
      //
936
      // let us recall however that in java2d, y coords are upside down
937
      // from what one would consider "normal" first quadrant rules, so we
938
      // will *subtract* the y value of this control vector from our first
939
      // point.
940
      cvec[0] = 0;
941
      if (extent > 0)
942
        cvec[1] = len;
943
      else
944
        cvec[1] = -len;
945
 
946
      trans.scale(rx, ry);
947
      trans.rotate(angle);
948
      trans.transform(cvec, 0, cvec, 0, 1);
949
      coords[0] = x0 + cvec[0];
950
      coords[1] = y0 - cvec[1];
951
 
952
      // control vector #2 would, ideally, be sticking out and to the
953
      // right, in a first quadrant arc segment. again, subtraction of y.
954
      cvec[0] = 0;
955
      if (extent > 0)
956
        cvec[1] = -len;
957
      else
958
        cvec[1] = len;
959
 
960
      trans.rotate(curr_extent);
961
      trans.transform(cvec, 0, cvec, 0, 1);
962
      coords[2] = x1 + cvec[0];
963
      coords[3] = y1 - cvec[1];
964
 
965
      // end point
966
      coords[4] = x1;
967
      coords[5] = y1;
968
 
969
      if (xform != null)
970
        xform.transform(coords, 0, coords, 0, 3);
971
 
972
      return SEG_CUBICTO;
973
    }
974
  } // class ArcIterator
975
 
976
  /**
977
   * This class implements an arc in double precision.
978
   *
979
   * @author Eric Blake (ebb9@email.byu.edu)
980
   * @since 1.2
981
   */
982
  public static class Double extends Arc2D
983
  {
984
    /** The x coordinate of the box bounding the ellipse of this arc. */
985
    public double x;
986
 
987
    /** The y coordinate of the box bounding the ellipse of this arc. */
988
    public double y;
989
 
990
    /** The width of the box bounding the ellipse of this arc. */
991
    public double width;
992
 
993
    /** The height of the box bounding the ellipse of this arc. */
994
    public double height;
995
 
996
    /** The start angle of this arc, in degrees. */
997
    public double start;
998
 
999
    /** The extent angle of this arc, in degrees. */
1000
    public double extent;
1001
 
1002
    /**
1003
     * Create a new, open arc at (0,0) with 0 extent.
1004
     */
1005
    public Double()
1006
    {
1007
      super(OPEN);
1008
    }
1009
 
1010
    /**
1011
     * Create a new arc of the given type at (0,0) with 0 extent.
1012
     *
1013
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1014
     * @throws IllegalArgumentException if type is invalid
1015
     */
1016
    public Double(int type)
1017
    {
1018
      super(type);
1019
    }
1020
 
1021
    /**
1022
     * Create a new arc with the given dimensions.
1023
     *
1024
     * @param x the x coordinate
1025
     * @param y the y coordinate
1026
     * @param w the width
1027
     * @param h the height
1028
     * @param start the start angle, in degrees
1029
     * @param extent the extent, in degrees
1030
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1031
     * @throws IllegalArgumentException if type is invalid
1032
     */
1033
    public Double(double x, double y, double w, double h, double start,
1034
                  double extent, int type)
1035
    {
1036
      super(type);
1037
      this.x = x;
1038
      this.y = y;
1039
      width = w;
1040
      height = h;
1041
      this.start = start;
1042
      this.extent = extent;
1043
    }
1044
 
1045
    /**
1046
     * Create a new arc with the given dimensions.
1047
     *
1048
     * @param r the bounding box
1049
     * @param start the start angle, in degrees
1050
     * @param extent the extent, in degrees
1051
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1052
     * @throws IllegalArgumentException if type is invalid
1053
     * @throws NullPointerException if r is null
1054
     */
1055
    public Double(Rectangle2D r, double start, double extent, int type)
1056
    {
1057
      super(type);
1058
      x = r.getX();
1059
      y = r.getY();
1060
      width = r.getWidth();
1061
      height = r.getHeight();
1062
      this.start = start;
1063
      this.extent = extent;
1064
    }
1065
 
1066
    /**
1067
     * Return the x coordinate of the bounding box.
1068
     *
1069
     * @return the value of x
1070
     */
1071
    public double getX()
1072
    {
1073
      return x;
1074
    }
1075
 
1076
    /**
1077
     * Return the y coordinate of the bounding box.
1078
     *
1079
     * @return the value of y
1080
     */
1081
    public double getY()
1082
    {
1083
      return y;
1084
    }
1085
 
1086
    /**
1087
     * Return the width of the bounding box.
1088
     *
1089
     * @return the value of width
1090
     */
1091
    public double getWidth()
1092
    {
1093
      return width;
1094
    }
1095
 
1096
    /**
1097
     * Return the height of the bounding box.
1098
     *
1099
     * @return the value of height
1100
     */
1101
    public double getHeight()
1102
    {
1103
      return height;
1104
    }
1105
 
1106
    /**
1107
     * Return the start angle of the arc, in degrees.
1108
     *
1109
     * @return the value of start
1110
     */
1111
    public double getAngleStart()
1112
    {
1113
      return start;
1114
    }
1115
 
1116
    /**
1117
     * Return the extent of the arc, in degrees.
1118
     *
1119
     * @return the value of extent
1120
     */
1121
    public double getAngleExtent()
1122
    {
1123
      return extent;
1124
    }
1125
 
1126
    /**
1127
     * Tests if the arc contains points.
1128
     *
1129
     * @return true if the arc has no interior
1130
     */
1131
    public boolean isEmpty()
1132
    {
1133
      return width <= 0 || height <= 0;
1134
    }
1135
 
1136
    /**
1137
     * Sets the arc to the given dimensions.
1138
     *
1139
     * @param x the x coordinate
1140
     * @param y the y coordinate
1141
     * @param w the width
1142
     * @param h the height
1143
     * @param start the start angle, in degrees
1144
     * @param extent the extent, in degrees
1145
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1146
     * @throws IllegalArgumentException if type is invalid
1147
     */
1148
    public void setArc(double x, double y, double w, double h, double start,
1149
                       double extent, int type)
1150
    {
1151
      this.x = x;
1152
      this.y = y;
1153
      width = w;
1154
      height = h;
1155
      this.start = start;
1156
      this.extent = extent;
1157
      setArcType(type);
1158
    }
1159
 
1160
    /**
1161
     * Sets the start angle of the arc.
1162
     *
1163
     * @param start the new start angle
1164
     */
1165
    public void setAngleStart(double start)
1166
    {
1167
      this.start = start;
1168
    }
1169
 
1170
    /**
1171
     * Sets the extent angle of the arc.
1172
     *
1173
     * @param extent the new extent angle
1174
     */
1175
    public void setAngleExtent(double extent)
1176
    {
1177
      this.extent = extent;
1178
    }
1179
 
1180
    /**
1181
     * Creates a tight bounding box given dimensions that more precise than
1182
     * the bounding box of the ellipse.
1183
     *
1184
     * @param x the x coordinate
1185
     * @param y the y coordinate
1186
     * @param w the width
1187
     * @param h the height
1188
     */
1189
    protected Rectangle2D makeBounds(double x, double y, double w, double h)
1190
    {
1191
      return new Rectangle2D.Double(x, y, w, h);
1192
    }
1193
  } // class Double
1194
 
1195
  /**
1196
   * This class implements an arc in float precision.
1197
   *
1198
   * @author Eric Blake (ebb9@email.byu.edu)
1199
   * @since 1.2
1200
   */
1201
  public static class Float extends Arc2D
1202
  {
1203
    /** The x coordinate of the box bounding the ellipse of this arc. */
1204
    public float x;
1205
 
1206
    /** The y coordinate of the box bounding the ellipse of this arc. */
1207
    public float y;
1208
 
1209
    /** The width of the box bounding the ellipse of this arc. */
1210
    public float width;
1211
 
1212
    /** The height of the box bounding the ellipse of this arc. */
1213
    public float height;
1214
 
1215
    /** The start angle of this arc, in degrees. */
1216
    public float start;
1217
 
1218
    /** The extent angle of this arc, in degrees. */
1219
    public float extent;
1220
 
1221
    /**
1222
     * Create a new, open arc at (0,0) with 0 extent.
1223
     */
1224
    public Float()
1225
    {
1226
      super(OPEN);
1227
    }
1228
 
1229
    /**
1230
     * Create a new arc of the given type at (0,0) with 0 extent.
1231
     *
1232
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1233
     * @throws IllegalArgumentException if type is invalid
1234
     */
1235
    public Float(int type)
1236
    {
1237
      super(type);
1238
    }
1239
 
1240
    /**
1241
     * Create a new arc with the given dimensions.
1242
     *
1243
     * @param x the x coordinate
1244
     * @param y the y coordinate
1245
     * @param w the width
1246
     * @param h the height
1247
     * @param start the start angle, in degrees
1248
     * @param extent the extent, in degrees
1249
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1250
     * @throws IllegalArgumentException if type is invalid
1251
     */
1252
    public Float(float x, float y, float w, float h, float start,
1253
                 float extent, int type)
1254
    {
1255
      super(type);
1256
      this.x = x;
1257
      this.y = y;
1258
      width = w;
1259
      height = h;
1260
      this.start = start;
1261
      this.extent = extent;
1262
    }
1263
 
1264
    /**
1265
     * Create a new arc with the given dimensions.
1266
     *
1267
     * @param r the bounding box
1268
     * @param start the start angle, in degrees
1269
     * @param extent the extent, in degrees
1270
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1271
     * @throws IllegalArgumentException if type is invalid
1272
     * @throws NullPointerException if r is null
1273
     */
1274
    public Float(Rectangle2D r, float start, float extent, int type)
1275
    {
1276
      super(type);
1277
      x = (float) r.getX();
1278
      y = (float) r.getY();
1279
      width = (float) r.getWidth();
1280
      height = (float) r.getHeight();
1281
      this.start = start;
1282
      this.extent = extent;
1283
    }
1284
 
1285
    /**
1286
     * Return the x coordinate of the bounding box.
1287
     *
1288
     * @return the value of x
1289
     */
1290
    public double getX()
1291
    {
1292
      return x;
1293
    }
1294
 
1295
    /**
1296
     * Return the y coordinate of the bounding box.
1297
     *
1298
     * @return the value of y
1299
     */
1300
    public double getY()
1301
    {
1302
      return y;
1303
    }
1304
 
1305
    /**
1306
     * Return the width of the bounding box.
1307
     *
1308
     * @return the value of width
1309
     */
1310
    public double getWidth()
1311
    {
1312
      return width;
1313
    }
1314
 
1315
    /**
1316
     * Return the height of the bounding box.
1317
     *
1318
     * @return the value of height
1319
     */
1320
    public double getHeight()
1321
    {
1322
      return height;
1323
    }
1324
 
1325
    /**
1326
     * Return the start angle of the arc, in degrees.
1327
     *
1328
     * @return the value of start
1329
     */
1330
    public double getAngleStart()
1331
    {
1332
      return start;
1333
    }
1334
 
1335
    /**
1336
     * Return the extent of the arc, in degrees.
1337
     *
1338
     * @return the value of extent
1339
     */
1340
    public double getAngleExtent()
1341
    {
1342
      return extent;
1343
    }
1344
 
1345
    /**
1346
     * Tests if the arc contains points.
1347
     *
1348
     * @return true if the arc has no interior
1349
     */
1350
    public boolean isEmpty()
1351
    {
1352
      return width <= 0 || height <= 0;
1353
    }
1354
 
1355
    /**
1356
     * Sets the arc to the given dimensions.
1357
     *
1358
     * @param x the x coordinate
1359
     * @param y the y coordinate
1360
     * @param w the width
1361
     * @param h the height
1362
     * @param start the start angle, in degrees
1363
     * @param extent the extent, in degrees
1364
     * @param type the arc type: {@link #OPEN}, {@link #CHORD}, or {@link #PIE}
1365
     * @throws IllegalArgumentException if type is invalid
1366
     */
1367
    public void setArc(double x, double y, double w, double h, double start,
1368
                       double extent, int type)
1369
    {
1370
      this.x = (float) x;
1371
      this.y = (float) y;
1372
      width = (float) w;
1373
      height = (float) h;
1374
      this.start = (float) start;
1375
      this.extent = (float) extent;
1376
      setArcType(type);
1377
    }
1378
 
1379
    /**
1380
     * Sets the start angle of the arc.
1381
     *
1382
     * @param start the new start angle
1383
     */
1384
    public void setAngleStart(double start)
1385
    {
1386
      this.start = (float) start;
1387
    }
1388
 
1389
    /**
1390
     * Sets the extent angle of the arc.
1391
     *
1392
     * @param extent the new extent angle
1393
     */
1394
    public void setAngleExtent(double extent)
1395
    {
1396
      this.extent = (float) extent;
1397
    }
1398
 
1399
    /**
1400
     * Creates a tight bounding box given dimensions that more precise than
1401
     * the bounding box of the ellipse.
1402
     *
1403
     * @param x the x coordinate
1404
     * @param y the y coordinate
1405
     * @param w the width
1406
     * @param h the height
1407
     */
1408
    protected Rectangle2D makeBounds(double x, double y, double w, double h)
1409
    {
1410
      return new Rectangle2D.Float((float) x, (float) y, (float) w, (float) h);
1411
    }
1412
  } // class Float
1413
} // class Arc2D

powered by: WebSVN 2.1.0

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