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

Subversion Repositories openrisc

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 771 jeremybenn
/* BasicStroke.java --
2
   Copyright (C) 2002, 2003, 2004, 2005, 2006  Free Software Foundation, Inc.
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
 
39
package java.awt;
40
 
41
import gnu.java.awt.java2d.CubicSegment;
42
import gnu.java.awt.java2d.LineSegment;
43
import gnu.java.awt.java2d.QuadSegment;
44
import gnu.java.awt.java2d.Segment;
45
 
46
import java.awt.geom.FlatteningPathIterator;
47
import java.awt.geom.GeneralPath;
48
import java.awt.geom.PathIterator;
49
import java.awt.geom.Point2D;
50
import java.util.Arrays;
51
 
52
/**
53
 * A general purpose {@link Stroke} implementation that can represent a wide
54
 * variety of line styles for use with subclasses of {@link Graphics2D}.
55
 * <p>
56
 * The line cap and join styles can be set using the options illustrated
57
 * here:
58
 * <p>
59
 * <img src="doc-files/capjoin.png" width="350" height="180"
60
 * alt="Illustration of line cap and join styles" />
61
 * <p>
62
 * A dash array can be used to specify lines with alternating opaque and
63
 * transparent sections.
64
 */
65
public class BasicStroke implements Stroke
66
{
67
  /**
68
   * Indicates a mitered line join style. See the class overview for an
69
   * illustration.
70
   */
71
  public static final int JOIN_MITER = 0;
72
 
73
  /**
74
   * Indicates a rounded line join style. See the class overview for an
75
   * illustration.
76
   */
77
  public static final int JOIN_ROUND = 1;
78
 
79
  /**
80
   * Indicates a bevelled line join style. See the class overview for an
81
   * illustration.
82
   */
83
  public static final int JOIN_BEVEL = 2;
84
 
85
  /**
86
   * Indicates a flat line cap style. See the class overview for an
87
   * illustration.
88
   */
89
  public static final int CAP_BUTT = 0;
90
 
91
  /**
92
   * Indicates a rounded line cap style. See the class overview for an
93
   * illustration.
94
   */
95
  public static final int CAP_ROUND = 1;
96
 
97
  /**
98
   * Indicates a square line cap style. See the class overview for an
99
   * illustration.
100
   */
101
  public static final int CAP_SQUARE = 2;
102
 
103
  /** The stroke width. */
104
  private final float width;
105
 
106
  /** The line cap style. */
107
  private final int cap;
108
 
109
  /** The line join style. */
110
  private final int join;
111
 
112
  /** The miter limit. */
113
  private final float limit;
114
 
115
  /** The dash array. */
116
  private final float[] dash;
117
 
118
  /** The dash phase. */
119
  private final float phase;
120
 
121
  // The inner and outer paths of the stroke
122
  private Segment start, end;
123
 
124
  /**
125
   * Creates a new <code>BasicStroke</code> instance with the given attributes.
126
   *
127
   * @param width  the line width (>= 0.0f).
128
   * @param cap  the line cap style (one of {@link #CAP_BUTT},
129
   *             {@link #CAP_ROUND} or {@link #CAP_SQUARE}).
130
   * @param join  the line join style (one of {@link #JOIN_ROUND},
131
   *              {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}).
132
   * @param miterlimit  the limit to trim the miter join. The miterlimit must be
133
   * greater than or equal to 1.0f.
134
   * @param dash The array representing the dashing pattern. There must be at
135
   * least one non-zero entry.
136
   * @param dashPhase is negative and dash is not null.
137
   *
138
   * @throws IllegalArgumentException If one input parameter doesn't meet
139
   * its needs.
140
   */
141
  public BasicStroke(float width, int cap, int join, float miterlimit,
142
                     float[] dash, float dashPhase)
143
  {
144
    if (width < 0.0f )
145
      throw new IllegalArgumentException("width " + width + " < 0");
146
    else if (cap < CAP_BUTT || cap > CAP_SQUARE)
147
      throw new IllegalArgumentException("cap " + cap + " out of range ["
148
                                         + CAP_BUTT + ".." + CAP_SQUARE + "]");
149
    else if (miterlimit < 1.0f && join == JOIN_MITER)
150
      throw new IllegalArgumentException("miterlimit " + miterlimit
151
                                         + " < 1.0f while join == JOIN_MITER");
152
    else if (join < JOIN_MITER || join > JOIN_BEVEL)
153
      throw new IllegalArgumentException("join " + join + " out of range ["
154
                                         + JOIN_MITER + ".." + JOIN_BEVEL
155
                                         + "]");
156
    else if (dashPhase < 0.0f && dash != null)
157
      throw new IllegalArgumentException("dashPhase " + dashPhase
158
                                         + " < 0.0f while dash != null");
159
    else if (dash != null)
160
      if (dash.length == 0)
161
        throw new IllegalArgumentException("dash.length is 0");
162
      else
163
        {
164
          boolean allZero = true;
165
 
166
          for ( int i = 0; i < dash.length; ++i)
167
            {
168
              if (dash[i] != 0.0f)
169
                {
170
                  allZero = false;
171
                  break;
172
                }
173
            }
174
 
175
          if (allZero)
176
            throw new IllegalArgumentException("all dashes are 0.0f");
177
        }
178
 
179
    this.width = width;
180
    this.cap = cap;
181
    this.join = join;
182
    limit = miterlimit;
183
    this.dash = dash == null ? null : (float[]) dash.clone();
184
    phase = dashPhase;
185
  }
186
 
187
  /**
188
   * Creates a new <code>BasicStroke</code> instance with the given attributes.
189
   *
190
   * @param width  the line width (>= 0.0f).
191
   * @param cap  the line cap style (one of {@link #CAP_BUTT},
192
   *             {@link #CAP_ROUND} or {@link #CAP_SQUARE}).
193
   * @param join  the line join style (one of {@link #JOIN_ROUND},
194
   *              {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}).
195
   * @param miterlimit the limit to trim the miter join. The miterlimit must be
196
   * greater than or equal to 1.0f.
197
   *
198
   * @throws IllegalArgumentException If one input parameter doesn't meet
199
   * its needs.
200
   */
201
  public BasicStroke(float width, int cap, int join, float miterlimit)
202
  {
203
    this(width, cap, join, miterlimit, null, 0);
204
  }
205
 
206
  /**
207
   * Creates a new <code>BasicStroke</code> instance with the given attributes.
208
   * The miter limit defaults to <code>10.0</code>.
209
   *
210
   * @param width  the line width (>= 0.0f).
211
   * @param cap  the line cap style (one of {@link #CAP_BUTT},
212
   *             {@link #CAP_ROUND} or {@link #CAP_SQUARE}).
213
   * @param join  the line join style (one of {@link #JOIN_ROUND},
214
   *              {@link #JOIN_BEVEL}, or {@link #JOIN_MITER}).
215
   *
216
   * @throws IllegalArgumentException If one input parameter doesn't meet
217
   * its needs.
218
   */
219
  public BasicStroke(float width, int cap, int join)
220
  {
221
    this(width, cap, join, 10, null, 0);
222
  }
223
 
224
  /**
225
   * Creates a new <code>BasicStroke</code> instance with the given line
226
   * width.  The default values are:
227
   * <ul>
228
   * <li>line cap style: {@link #CAP_SQUARE};</li>
229
   * <li>line join style: {@link #JOIN_MITER};</li>
230
   * <li>miter limit: <code>10.0f</code>.
231
   * </ul>
232
   *
233
   * @param width  the line width (>= 0.0f).
234
   *
235
   * @throws IllegalArgumentException If <code>width</code> is negative.
236
   */
237
  public BasicStroke(float width)
238
  {
239
    this(width, CAP_SQUARE, JOIN_MITER, 10, null, 0);
240
  }
241
 
242
  /**
243
   * Creates a new <code>BasicStroke</code> instance.  The default values are:
244
   * <ul>
245
   * <li>line width: <code>1.0f</code>;</li>
246
   * <li>line cap style: {@link #CAP_SQUARE};</li>
247
   * <li>line join style: {@link #JOIN_MITER};</li>
248
   * <li>miter limit: <code>10.0f</code>.
249
   * </ul>
250
   */
251
  public BasicStroke()
252
  {
253
    this(1, CAP_SQUARE, JOIN_MITER, 10, null, 0);
254
  }
255
 
256
  /**
257
   * Creates a shape representing the stroked outline of the given shape.
258
   * THIS METHOD IS NOT YET IMPLEMENTED.
259
   *
260
   * @param s  the shape.
261
   */
262
  public Shape createStrokedShape(Shape s)
263
  {
264
    PathIterator pi = s.getPathIterator(null);
265
 
266
    if( dash == null )
267
      return solidStroke( pi );
268
 
269
    return dashedStroke( pi );
270
  }
271
 
272
  /**
273
   * Returns the line width.
274
   *
275
   * @return The line width.
276
   */
277
  public float getLineWidth()
278
  {
279
    return width;
280
  }
281
 
282
  /**
283
   * Returns a code indicating the line cap style (one of {@link #CAP_BUTT},
284
   * {@link #CAP_ROUND}, {@link #CAP_SQUARE}).
285
   *
286
   * @return A code indicating the line cap style.
287
   */
288
  public int getEndCap()
289
  {
290
    return cap;
291
  }
292
 
293
  /**
294
   * Returns a code indicating the line join style (one of {@link #JOIN_BEVEL},
295
   * {@link #JOIN_MITER} or {@link #JOIN_ROUND}).
296
   *
297
   * @return A code indicating the line join style.
298
   */
299
  public int getLineJoin()
300
  {
301
    return join;
302
  }
303
 
304
  /**
305
   * Returns the miter limit.
306
   *
307
   * @return The miter limit.
308
   */
309
  public float getMiterLimit()
310
  {
311
    return limit;
312
  }
313
 
314
  /**
315
   * Returns the dash array, which defines the length of alternate opaque and
316
   * transparent sections in lines drawn with this stroke.  If
317
   * <code>null</code>, a continuous line will be drawn.
318
   *
319
   * @return The dash array (possibly <code>null</code>).
320
   */
321
  public float[] getDashArray()
322
  {
323
    return dash;
324
  }
325
 
326
  /**
327
   * Returns the dash phase for the stroke.  This is the offset from the start
328
   * of a path at which the pattern defined by {@link #getDashArray()} is
329
   * rendered.
330
   *
331
   * @return The dash phase.
332
   */
333
  public float getDashPhase()
334
  {
335
    return phase;
336
  }
337
 
338
  /**
339
   * Returns the hash code for this object. The hash is calculated by
340
   * xoring the hash, cap, join, limit, dash array and phase values
341
   * (converted to <code>int</code> first with
342
   * <code>Float.floatToIntBits()</code> if the value is a
343
   * <code>float</code>).
344
   *
345
   * @return The hash code.
346
   */
347
  public int hashCode()
348
  {
349
    int hash = Float.floatToIntBits(width);
350
    hash ^= cap;
351
    hash ^= join;
352
    hash ^= Float.floatToIntBits(limit);
353
 
354
    if (dash != null)
355
      for (int i = 0; i < dash.length; i++)
356
        hash ^=  Float.floatToIntBits(dash[i]);
357
 
358
    hash ^= Float.floatToIntBits(phase);
359
 
360
    return hash;
361
  }
362
 
363
  /**
364
   * Compares this <code>BasicStroke</code> for equality with an arbitrary
365
   * object.  This method returns <code>true</code> if and only if:
366
   * <ul>
367
   * <li><code>o</code> is an instanceof <code>BasicStroke</code>;</li>
368
   * <li>this object has the same width, line cap style, line join style,
369
   * miter limit, dash array and dash phase as <code>o</code>.</li>
370
   * </ul>
371
   *
372
   * @param o  the object (<code>null</code> permitted).
373
   *
374
   * @return <code>true</code> if this stroke is equal to <code>o</code> and
375
   *         <code>false</code> otherwise.
376
   */
377
  public boolean equals(Object o)
378
  {
379
    if (! (o instanceof BasicStroke))
380
      return false;
381
    BasicStroke s = (BasicStroke) o;
382
    return width == s.width && cap == s.cap && join == s.join
383
      && limit == s.limit && Arrays.equals(dash, s.dash) && phase == s.phase;
384
  }
385
 
386
  private Shape solidStroke(PathIterator pi)
387
  {
388
    double[] coords = new double[6];
389
    double x, y, x0, y0;
390
    boolean pathOpen = false;
391
    GeneralPath output = new GeneralPath( );
392
    Segment[] p;
393
    x = x0 = y = y0 = 0;
394
 
395
    while( !pi.isDone() )
396
      {
397
        switch( pi.currentSegment(coords) )
398
          {
399
          case PathIterator.SEG_MOVETO:
400
            x0 = x = coords[0];
401
            y0 = y = coords[1];
402
            if( pathOpen )
403
              {
404
                capEnds();
405
                convertPath(output, start);
406
                start = end = null;
407
                pathOpen = false;
408
              }
409
            break;
410
 
411
          case PathIterator.SEG_LINETO:
412
            p = (new LineSegment(x, y, coords[0], coords[1])).
413
              getDisplacedSegments(width/2.0);
414
            if( !pathOpen )
415
              {
416
                start = p[0];
417
                end = p[1];
418
                pathOpen = true;
419
              }
420
            else
421
              addSegments(p);
422
 
423
            x = coords[0];
424
            y = coords[1];
425
            break;
426
 
427
          case PathIterator.SEG_QUADTO:
428
            p = (new QuadSegment(x, y, coords[0], coords[1], coords[2],
429
                                 coords[3])).getDisplacedSegments(width/2.0);
430
            if( !pathOpen )
431
              {
432
                start = p[0];
433
                end = p[1];
434
                pathOpen = true;
435
              }
436
            else
437
              addSegments(p);
438
 
439
            x = coords[2];
440
            y = coords[3];
441
            break;
442
 
443
          case PathIterator.SEG_CUBICTO:
444
            p = new CubicSegment(x, y, coords[0], coords[1],
445
                                 coords[2], coords[3],
446
                                 coords[4], coords[5]).getDisplacedSegments(width/2.0);
447
            if( !pathOpen )
448
              {
449
                start = p[0];
450
                end = p[1];
451
                pathOpen = true;
452
              }
453
            else
454
              addSegments(p);
455
 
456
            x = coords[4];
457
            y = coords[5];
458
            break;
459
 
460
          case PathIterator.SEG_CLOSE:
461
            if (x == x0 && y == y0)
462
              {
463
                joinSegments(new Segment[] { start.first, end.first });
464
              }
465
            else
466
              {
467
                p = (new LineSegment(x, y, x0, y0)).getDisplacedSegments(width / 2.0);
468
                addSegments(p);
469
              }
470
            convertPath(output, start);
471
            convertPath(output, end);
472
            start = end = null;
473
            pathOpen = false;
474
            output.setWindingRule(GeneralPath.WIND_EVEN_ODD);
475
            break;
476
          }
477
        pi.next();
478
      }
479
 
480
    if( pathOpen )
481
      {
482
        capEnds();
483
        convertPath(output, start);
484
      }
485
    return output;
486
  }
487
 
488
  private Shape dashedStroke(PathIterator pi)
489
  {
490
    // The choice of (flatnessSq == width / 3) is made to be consistent with
491
    // the flattening in CubicSegment.getDisplacedSegments
492
    FlatteningPathIterator flat = new FlatteningPathIterator(pi,
493
                                                             Math.sqrt(width / 3));
494
 
495
    // Holds the endpoint of the current segment (or piece of a segment)
496
    double[] coords = new double[2];
497
 
498
    // Holds end of the last segment
499
    double x, y, x0, y0;
500
    x = x0 = y = y0 = 0;
501
 
502
    // Various useful flags
503
    boolean pathOpen = false;
504
    boolean dashOn = true;
505
    boolean offsetting = (phase != 0);
506
 
507
    // How far we are into the current dash
508
    double distance = 0;
509
    int dashIndex = 0;
510
 
511
    // And variables to hold the final output
512
    GeneralPath output = new GeneralPath();
513
    Segment[] p;
514
 
515
    // Iterate over the FlatteningPathIterator
516
    while (! flat.isDone())
517
      {
518
        switch (flat.currentSegment(coords))
519
          {
520
          case PathIterator.SEG_MOVETO:
521
            x0 = x = coords[0];
522
            y0 = y = coords[1];
523
 
524
            if (pathOpen)
525
              {
526
                capEnds();
527
                convertPath(output, start);
528
                start = end = null;
529
                pathOpen = false;
530
              }
531
 
532
            break;
533
 
534
          case PathIterator.SEG_LINETO:
535
            boolean segmentConsumed = false;
536
 
537
            while (! segmentConsumed)
538
              {
539
                // Find the total remaining length of this segment
540
                double segLength = Math.sqrt((x - coords[0]) * (x - coords[0])
541
                                             + (y - coords[1])
542
                                             * (y - coords[1]));
543
                boolean spanBoundary = true;
544
                double[] segmentEnd = null;
545
 
546
                // The current segment fits entirely inside the current dash
547
                if ((offsetting && distance + segLength <= phase)
548
                    || distance + segLength <= dash[dashIndex])
549
                  {
550
                    spanBoundary = false;
551
                  }
552
 
553
                // Otherwise, we need to split the segment in two, as this
554
                // segment spans a dash boundry
555
                else
556
                  {
557
                    segmentEnd = (double[]) coords.clone();
558
 
559
                    // Calculate the remaining distance in this dash,
560
                    // and coordinates of the dash boundary
561
                    double reqLength;
562
                    if (offsetting)
563
                      reqLength = phase - distance;
564
                    else
565
                      reqLength = dash[dashIndex] - distance;
566
 
567
                    coords[0] = x + ((coords[0] - x) * reqLength / segLength);
568
                    coords[1] = y + ((coords[1] - y) * reqLength / segLength);
569
                  }
570
 
571
                if (offsetting || ! dashOn)
572
                  {
573
                    // Dash is off, or we are in offset - treat this as a
574
                    // moveTo
575
                    x0 = x = coords[0];
576
                    y0 = y = coords[1];
577
 
578
                    if (pathOpen)
579
                      {
580
                        capEnds();
581
                        convertPath(output, start);
582
                        start = end = null;
583
                        pathOpen = false;
584
                      }
585
                  }
586
                else
587
                  {
588
                    // Dash is on - treat this as a lineTo
589
                    p = (new LineSegment(x, y, coords[0], coords[1])).getDisplacedSegments(width / 2.0);
590
 
591
                    if (! pathOpen)
592
                      {
593
                        start = p[0];
594
                        end = p[1];
595
                        pathOpen = true;
596
                      }
597
                    else
598
                      addSegments(p);
599
 
600
                    x = coords[0];
601
                    y = coords[1];
602
                  }
603
 
604
                // Update variables depending on whether we spanned a
605
                // dash boundary or not
606
                if (! spanBoundary)
607
                  {
608
                    distance += segLength;
609
                    segmentConsumed = true;
610
                  }
611
                else
612
                  {
613
                    if (offsetting)
614
                      offsetting = false;
615
                    dashOn = ! dashOn;
616
                    distance = 0;
617
                    coords = segmentEnd;
618
 
619
                    if (dashIndex + 1 == dash.length)
620
                      dashIndex = 0;
621
                    else
622
                      dashIndex++;
623
 
624
                    // Since the value of segmentConsumed is still false,
625
                    // the next run of the while loop will complete the segment
626
                  }
627
              }
628
            break;
629
 
630
          // This is a flattened path, so we don't need to deal with curves
631
          }
632
        flat.next();
633
      }
634
 
635
    if (pathOpen)
636
      {
637
        capEnds();
638
        convertPath(output, start);
639
      }
640
    return output;
641
  }
642
 
643
  /**
644
   * Cap the ends of the path (joining the start and end list of segments)
645
   */
646
  private void capEnds()
647
  {
648
    Segment returnPath = end.last;
649
 
650
    end.reverseAll(); // reverse the path.
651
    end = null;
652
    capEnd(start, returnPath);
653
    start.last = returnPath.last;
654
    end = null;
655
 
656
    capEnd(start, start);
657
  }
658
 
659
  /**
660
   * Append the Segments in s to the GeneralPath p
661
   */
662
  private void convertPath(GeneralPath p, Segment s)
663
  {
664
    Segment v = s;
665
    p.moveTo((float)s.P1.getX(), (float)s.P1.getY());
666
 
667
    do
668
      {
669
        if(v instanceof LineSegment)
670
          p.lineTo((float)v.P2.getX(), (float)v.P2.getY());
671
        else if(v instanceof QuadSegment)
672
          p.quadTo((float)((QuadSegment)v).cp.getX(),
673
                   (float)((QuadSegment)v).cp.getY(),
674
                   (float)v.P2.getX(),
675
                   (float)v.P2.getY());
676
        else if(v instanceof CubicSegment)
677
          p.curveTo((float)((CubicSegment)v).cp1.getX(),
678
                    (float)((CubicSegment)v).cp1.getY(),
679
                    (float)((CubicSegment)v).cp2.getX(),
680
                    (float)((CubicSegment)v).cp2.getY(),
681
                    (float)v.P2.getX(),
682
                    (float)v.P2.getY());
683
        v = v.next;
684
      } while(v != s && v != null);
685
 
686
    p.closePath();
687
  }
688
 
689
  /**
690
   * Add the segments to start and end (the inner and outer edges of the stroke)
691
   */
692
  private void addSegments(Segment[] segments)
693
  {
694
    joinSegments(segments);
695
    start.add(segments[0]);
696
    end.add(segments[1]);
697
  }
698
 
699
  private void joinSegments(Segment[] segments)
700
  {
701
    double[] p0 = start.last.cp2();
702
    double[] p1 = new double[]{start.last.P2.getX(), start.last.P2.getY()};
703
    double[] p2 = new double[]{segments[0].first.P1.getX(), segments[0].first.P1.getY()};
704
    double[] p3 = segments[0].cp1();
705
    Point2D p;
706
 
707
    p = lineIntersection(p0[0],p0[1],p1[0],p1[1],
708
                                 p2[0],p2[1],p3[0],p3[1], false);
709
 
710
    double det = (p1[0] - p0[0])*(p3[1] - p2[1]) -
711
      (p3[0] - p2[0])*(p1[1] - p0[1]);
712
 
713
    if( det > 0 )
714
      {
715
        // start and segment[0] form the 'inner' part of a join,
716
        // connect the overlapping segments
717
        joinInnerSegments(start, segments[0], p);
718
        joinOuterSegments(end, segments[1], p);
719
      }
720
    else
721
      {
722
        // end and segment[1] form the 'inner' part
723
        joinInnerSegments(end, segments[1], p);
724
        joinOuterSegments(start, segments[0], p);
725
      }
726
  }
727
 
728
  /**
729
   * Make a cap between a and b segments,
730
   * where a-->b is the direction of iteration.
731
   */
732
  private void capEnd(Segment a, Segment b)
733
  {
734
    double[] p0, p1;
735
    double dx, dy, l;
736
    Point2D c1,c2;
737
 
738
    switch( cap )
739
      {
740
      case CAP_BUTT:
741
        a.add(new LineSegment(a.last.P2, b.P1));
742
        break;
743
 
744
      case CAP_SQUARE:
745
        p0 = a.last.cp2();
746
        p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
747
        dx = p1[0] - p0[0];
748
        dy = p1[1] - p0[1];
749
        l = Math.sqrt(dx * dx + dy * dy);
750
        dx = 0.5*width*dx/l;
751
        dy = 0.5*width*dy/l;
752
        c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy);
753
        c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy);
754
        a.add(new LineSegment(a.last.P2, c1));
755
        a.add(new LineSegment(c1, c2));
756
        a.add(new LineSegment(c2, b.P1));
757
        break;
758
 
759
      case CAP_ROUND:
760
        p0 = a.last.cp2();
761
        p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
762
        dx = p1[0] - p0[0];
763
        dy = p1[1] - p0[1];
764
        if (dx != 0 && dy != 0)
765
          {
766
            l = Math.sqrt(dx * dx + dy * dy);
767
            dx = (2.0/3.0)*width*dx/l;
768
            dy = (2.0/3.0)*width*dy/l;
769
          }
770
 
771
        c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy);
772
        c2 = new Point2D.Double(b.P1.getX() + dx, b.P1.getY() + dy);
773
        a.add(new CubicSegment(a.last.P2, c1, c2, b.P1));
774
        break;
775
      }
776
    a.add(b);
777
  }
778
 
779
  /**
780
   * Returns the intersection of two lines, or null if there isn't one.
781
   * @param infinite - true if the lines should be regarded as infinite, false
782
   * if the intersection must be within the given segments.
783
   * @return a Point2D or null.
784
   */
785
  private Point2D lineIntersection(double X1, double Y1,
786
                                   double X2, double Y2,
787
                                   double X3, double Y3,
788
                                   double X4, double Y4,
789
                                   boolean infinite)
790
  {
791
    double x1 = X1;
792
    double y1 = Y1;
793
    double rx = X2 - x1;
794
    double ry = Y2 - y1;
795
 
796
    double x2 = X3;
797
    double y2 = Y3;
798
    double sx = X4 - x2;
799
    double sy = Y4 - y2;
800
 
801
    double determinant = sx * ry - sy * rx;
802
    double nom = (sx * (y2 - y1) + sy * (x1 - x2));
803
 
804
    // lines can be considered parallel.
805
    if (Math.abs(determinant) < 1E-6)
806
      return null;
807
 
808
    nom = nom / determinant;
809
 
810
    // check if lines are within the bounds
811
    if(!infinite && (nom > 1.0 || nom < 0.0))
812
      return null;
813
 
814
    return new Point2D.Double(x1 + nom * rx, y1 + nom * ry);
815
  }
816
 
817
  /**
818
   * Join a and b segments, where a-->b is the direction of iteration.
819
   *
820
   * insideP is the inside intersection point of the join, needed for
821
   * calculating miter lengths.
822
   */
823
  private void joinOuterSegments(Segment a, Segment b, Point2D insideP)
824
  {
825
    double[] p0, p1;
826
    double dx, dy, l;
827
    Point2D c1,c2;
828
 
829
    switch( join )
830
      {
831
      case JOIN_MITER:
832
        p0 = a.last.cp2();
833
        p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
834
        double[] p2 = new double[]{b.P1.getX(), b.P1.getY()};
835
        double[] p3 = b.cp1();
836
        Point2D p = lineIntersection(p0[0],p0[1],p1[0],p1[1],p2[0],p2[1],p3[0],p3[1], true);
837
        if( p == null || insideP == null )
838
          a.add(new LineSegment(a.last.P2, b.P1));
839
        else if((p.distance(insideP)/width) < limit)
840
          {
841
            a.add(new LineSegment(a.last.P2, p));
842
            a.add(new LineSegment(p, b.P1));
843
          }
844
        else
845
          {
846
            // outside miter limit, do a bevel join.
847
            a.add(new LineSegment(a.last.P2, b.P1));
848
          }
849
        break;
850
 
851
      case JOIN_ROUND:
852
        p0 = a.last.cp2();
853
        p1 = new double[]{a.last.P2.getX(), a.last.P2.getY()};
854
        dx = p1[0] - p0[0];
855
        dy = p1[1] - p0[1];
856
        l = Math.sqrt(dx * dx + dy * dy);
857
        dx = 0.5*width*dx/l;
858
        dy = 0.5*width*dy/l;
859
        c1 = new Point2D.Double(p1[0] + dx, p1[1] + dy);
860
 
861
        p0 = new double[]{b.P1.getX(), b.P1.getY()};
862
        p1 = b.cp1();
863
 
864
        dx = p0[0] - p1[0]; // backwards direction.
865
        dy = p0[1] - p1[1];
866
        l = Math.sqrt(dx * dx + dy * dy);
867
        dx = 0.5*width*dx/l;
868
        dy = 0.5*width*dy/l;
869
        c2 = new Point2D.Double(p0[0] + dx, p0[1] + dy);
870
        a.add(new CubicSegment(a.last.P2, c1, c2, b.P1));
871
        break;
872
 
873
      case JOIN_BEVEL:
874
        a.add(new LineSegment(a.last.P2, b.P1));
875
        break;
876
      }
877
  }
878
 
879
  /**
880
   * Join a and b segments, removing any overlap
881
   */
882
  private void joinInnerSegments(Segment a, Segment b, Point2D p)
883
  {
884
    double[] p0 = a.last.cp2();
885
    double[] p1 = new double[] { a.last.P2.getX(), a.last.P2.getY() };
886
    double[] p2 = new double[] { b.P1.getX(), b.P1.getY() };
887
    double[] p3 = b.cp1();
888
 
889
    if (p == null)
890
      {
891
        // Dodgy.
892
        a.add(new LineSegment(a.last.P2, b.P1));
893
        p = new Point2D.Double((b.P1.getX() + a.last.P2.getX()) / 2.0,
894
                               (b.P1.getY() + a.last.P2.getY()) / 2.0);
895
      }
896
    else
897
      // This assumes segments a and b are single segments, which is
898
      // incorrect - if they are a linked list of segments (ie, passed in
899
      // from a flattening operation), this produces strange results!!
900
      a.last.P2 = b.P1 = p;
901
  }
902
}

powered by: WebSVN 2.1.0

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