001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.gvt;
020:
021: import java.awt.Graphics2D;
022: import java.awt.Shape;
023: import java.awt.geom.AffineTransform;
024: import java.awt.geom.Arc2D;
025: import java.awt.geom.PathIterator;
026: import java.awt.geom.Point2D;
027: import java.awt.geom.Rectangle2D;
028: import java.util.List;
029: import java.util.ArrayList;
030:
031: import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
032: import org.apache.batik.ext.awt.geom.ExtendedPathIterator;
033: import org.apache.batik.ext.awt.geom.ExtendedShape;
034: import org.apache.batik.ext.awt.geom.ShapeExtender;
035:
036: /**
037: * A shape painter that can be used to paint markers on a shape.
038: *
039: * @author <a href="mailto:vincent.hardy@eng.sun.com">Vincent Hardy</a>
040: * @version $Id: MarkerShapePainter.java 504084 2007-02-06 11:24:46Z dvholten $
041: */
042: public class MarkerShapePainter implements ShapePainter {
043:
044: /**
045: * The Shape to be painted.
046: */
047: protected ExtendedShape extShape;
048:
049: /**
050: * Start Marker
051: */
052: protected Marker startMarker;
053:
054: /**
055: * Middle Marker
056: */
057: protected Marker middleMarker;
058:
059: /**
060: * End Marker
061: */
062: protected Marker endMarker;
063:
064: /**
065: * Start marker proxy.
066: */
067: private ProxyGraphicsNode startMarkerProxy;
068:
069: /**
070: * Middle marker proxy.
071: */
072: private ProxyGraphicsNode[] middleMarkerProxies;
073:
074: /**
075: * End marker proxy.
076: */
077: private ProxyGraphicsNode endMarkerProxy;
078:
079: /**
080: * Contains the various marker proxies.
081: */
082: private CompositeGraphicsNode markerGroup;
083:
084: /**
085: * Internal Cache: Primitive bounds
086: */
087: private Rectangle2D dPrimitiveBounds;
088:
089: /**
090: * Internal Cache: Geometry bounds
091: */
092: private Rectangle2D dGeometryBounds;
093:
094: /**
095: * Constructs a new <tt>MarkerShapePainter</tt> that can be used to markers
096: * on top of a shape.
097: *
098: * @param shape Shape to be painted by this painter.
099: * Should not be null
100: */
101: public MarkerShapePainter(Shape shape) {
102: if (shape == null) {
103: throw new IllegalArgumentException();
104: }
105: if (shape instanceof ExtendedShape) {
106: this .extShape = (ExtendedShape) shape;
107: } else {
108: this .extShape = new ShapeExtender(shape);
109: }
110: }
111:
112: /**
113: * Paints the specified shape using the specified Graphics2D.
114: *
115: * @param g2d the Graphics2D to use
116: */
117: public void paint(Graphics2D g2d) {
118: if (markerGroup == null) {
119: buildMarkerGroup();
120: }
121: if (markerGroup.getChildren().size() > 0) {
122: markerGroup.paint(g2d);
123: }
124: }
125:
126: /**
127: * Returns the area painted by this shape painter.
128: */
129: public Shape getPaintedArea() {
130: if (markerGroup == null) {
131: buildMarkerGroup();
132: }
133: return markerGroup.getOutline();
134: }
135:
136: /**
137: * Returns the bounds of the area painted by this shape painter
138: */
139: public Rectangle2D getPaintedBounds2D() {
140: if (markerGroup == null) {
141: buildMarkerGroup();
142: }
143: return markerGroup.getPrimitiveBounds();
144: }
145:
146: /**
147: * Returns true if pt is in the area painted by this shape painter
148: */
149: public boolean inPaintedArea(Point2D pt) {
150: if (markerGroup == null) {
151: buildMarkerGroup();
152: }
153: GraphicsNode gn = markerGroup.nodeHitAt(pt);
154: return (gn != null);
155: }
156:
157: /**
158: * Returns the area covered by this shape painter (even if not painted).
159: * This is always null for Markers.
160: */
161: public Shape getSensitiveArea() {
162: return null;
163: }
164:
165: /**
166: * Returns the bounds of the area covered by this shape painte
167: * (even if not painted). This is always null for Markers.
168: */
169: public Rectangle2D getSensitiveBounds2D() {
170: return null;
171: }
172:
173: /**
174: * Returns true if pt is in the sensitive area.
175: * This is always false for Markers.
176: */
177: public boolean inSensitiveArea(Point2D pt) {
178: return false;
179: }
180:
181: /**
182: * Sets the Shape this shape painter is associated with.
183: *
184: * @param shape new shape this painter should be associated with.
185: * Should not be null.
186: */
187: public void setShape(Shape shape) {
188: if (shape == null) {
189: throw new IllegalArgumentException();
190: }
191: if (shape instanceof ExtendedShape) {
192: this .extShape = (ExtendedShape) shape;
193: } else {
194: this .extShape = new ShapeExtender(shape);
195: }
196:
197: this .startMarkerProxy = null;
198: this .middleMarkerProxies = null;
199: this .endMarkerProxy = null;
200: this .markerGroup = null;
201: }
202:
203: /**
204: * Gets the Shape this shape painter is associated with as an
205: * Extended Shape.
206: *
207: * @return shape associated with this painter */
208: public ExtendedShape getExtShape() {
209: return extShape;
210: }
211:
212: /**
213: * Gets the Shape this shape painter is associated with.
214: *
215: * @return shape associated with this painter
216: */
217: public Shape getShape() {
218: return extShape;
219: }
220:
221: /**
222: * Returns the marker that shall be drawn at the first vertex of the given
223: * shape.
224: */
225: public Marker getStartMarker() {
226: return startMarker;
227: }
228:
229: /**
230: * Sets the marker that shall be drawn at the first vertex of the given
231: * shape.
232: *
233: * @param startMarker the start marker
234: */
235: public void setStartMarker(Marker startMarker) {
236: this .startMarker = startMarker;
237: this .startMarkerProxy = null;
238: this .markerGroup = null;
239: }
240:
241: /**
242: * Returns the marker that shall be drawn at every other vertex (not the
243: * first or the last one) of the given shape.
244: */
245: public Marker getMiddleMarker() {
246: return middleMarker;
247: }
248:
249: /**
250: * Sets the marker that shall be drawn at every other vertex (not the first
251: * or the last one) of the given shape.
252: *
253: * @param middleMarker the middle marker
254: */
255: public void setMiddleMarker(Marker middleMarker) {
256: this .middleMarker = middleMarker;
257: this .middleMarkerProxies = null;
258: this .markerGroup = null;
259: }
260:
261: /**
262: * Returns the marker that shall be drawn at the last vertex of the given
263: * shape.
264: */
265: public Marker getEndMarker() {
266: return endMarker;
267: }
268:
269: /**
270: * Sets the marker that shall be drawn at the last vertex of the given
271: * shape.
272: *
273: * @param endMarker the end marker
274: */
275: public void setEndMarker(Marker endMarker) {
276: this .endMarker = endMarker;
277: this .endMarkerProxy = null;
278: this .markerGroup = null;
279: }
280:
281: // ---------------------------------------------------------------------
282: // Internal methods to build GraphicsNode according to the Marker
283: // ---------------------------------------------------------------------
284:
285: /**
286: * Builds a new marker group with the current set of markers.
287: */
288: protected void buildMarkerGroup() {
289: if (startMarker != null && startMarkerProxy == null) {
290: startMarkerProxy = buildStartMarkerProxy();
291: }
292:
293: if (middleMarker != null && middleMarkerProxies == null) {
294: middleMarkerProxies = buildMiddleMarkerProxies();
295: }
296:
297: if (endMarker != null && endMarkerProxy == null) {
298: endMarkerProxy = buildEndMarkerProxy();
299: }
300:
301: CompositeGraphicsNode group = new CompositeGraphicsNode();
302: List children = group.getChildren();
303: if (startMarkerProxy != null) {
304: children.add(startMarkerProxy);
305: }
306:
307: if (middleMarkerProxies != null) {
308: for (int i = 0; i < middleMarkerProxies.length; i++) {
309: children.add(middleMarkerProxies[i]);
310: }
311: }
312:
313: if (endMarkerProxy != null) {
314: children.add(endMarkerProxy);
315: }
316:
317: markerGroup = group;
318: }
319:
320: /**
321: * Builds a proxy <tt>GraphicsNode</tt> for the input <tt>Marker</tt> to be
322: * drawn at the start position
323: */
324: protected ProxyGraphicsNode buildStartMarkerProxy() {
325:
326: ExtendedPathIterator iter = getExtShape()
327: .getExtendedPathIterator();
328:
329: // Get initial point on the path
330: double[] coords = new double[7];
331: int segType = 0;
332:
333: if (iter.isDone()) {
334: return null;
335: }
336:
337: segType = iter.currentSegment(coords);
338: if (segType != ExtendedPathIterator.SEG_MOVETO) {
339: return null;
340: }
341: iter.next();
342:
343: Point2D markerPosition = new Point2D.Double(coords[0],
344: coords[1]);
345:
346: // If the marker's orient property is NaN,
347: // the slope needs to be computed
348: double rotation = startMarker.getOrient();
349: if (Double.isNaN(rotation)) {
350: if (!iter.isDone()) {
351: double[] next = new double[7];
352: int nextSegType = 0;
353: nextSegType = iter.currentSegment(next);
354: if (nextSegType == PathIterator.SEG_CLOSE) {
355: nextSegType = PathIterator.SEG_LINETO;
356: next[0] = coords[0];
357: next[1] = coords[1];
358: }
359: rotation = computeRotation(null, 0, // no previous seg.
360: coords, segType, // segment ending on start point
361: next, nextSegType); // segment out of start point
362:
363: }
364: }
365:
366: // Now, compute the marker's proxy transform
367: AffineTransform markerTxf = computeMarkerTransform(startMarker,
368: markerPosition, rotation);
369:
370: ProxyGraphicsNode gn = new ProxyGraphicsNode();
371:
372: gn.setSource(startMarker.getMarkerNode());
373: gn.setTransform(markerTxf);
374:
375: return gn;
376: }
377:
378: /**
379: * Builds a proxy <tt>GraphicsNode</tt> for the input <tt>Marker</tt> to be
380: * drawn at the end position.
381: */
382: protected ProxyGraphicsNode buildEndMarkerProxy() {
383:
384: ExtendedPathIterator iter = getExtShape()
385: .getExtendedPathIterator();
386:
387: int nPoints = 0;
388:
389: // Get first point, in case the last segment on the
390: // path is a close
391: if (iter.isDone()) {
392: return null;
393: }
394:
395: double[] coords = new double[7];
396: double[] moveTo = new double[2];
397: int segType = 0;
398: segType = iter.currentSegment(coords);
399: if (segType != ExtendedPathIterator.SEG_MOVETO) {
400: return null;
401: }
402: nPoints++;
403: moveTo[0] = coords[0];
404: moveTo[1] = coords[1];
405:
406: iter.next();
407:
408: // Now, get the last two points on the path
409: double[] lastButOne = new double[7];
410: double[] last = { coords[0], coords[1], coords[2], coords[3],
411: coords[4], coords[5], coords[6] };
412: double[] tmp = null;
413: int lastSegType = segType;
414: int lastButOneSegType = 0;
415:
416: while (!iter.isDone()) {
417: tmp = lastButOne;
418: lastButOne = last;
419: last = tmp;
420: lastButOneSegType = lastSegType;
421:
422: lastSegType = iter.currentSegment(last);
423:
424: if (lastSegType == PathIterator.SEG_MOVETO) {
425: moveTo[0] = last[0];
426: moveTo[1] = last[1];
427: } else if (lastSegType == PathIterator.SEG_CLOSE) {
428: lastSegType = PathIterator.SEG_LINETO;
429: last[0] = moveTo[0];
430: last[1] = moveTo[1];
431: }
432:
433: iter.next();
434: nPoints++;
435: }
436:
437: if (nPoints < 2) {
438: return null;
439: }
440:
441: // Turn the last segment into a position
442: Point2D markerPosition = getSegmentTerminatingPoint(last,
443: lastSegType);
444:
445: // If the marker's orient property is NaN,
446: // the slope needs to be computed
447: double rotation = endMarker.getOrient();
448: if (Double.isNaN(rotation)) {
449: rotation = computeRotation(lastButOne, lastButOneSegType,
450: last, lastSegType, null, 0);
451: }
452:
453: // Now, compute the marker's proxy transform
454: AffineTransform markerTxf = computeMarkerTransform(endMarker,
455: markerPosition, rotation);
456:
457: ProxyGraphicsNode gn = new ProxyGraphicsNode();
458:
459: gn.setSource(endMarker.getMarkerNode());
460: gn.setTransform(markerTxf);
461:
462: return gn;
463: }
464:
465: /**
466: * Builds a proxy <tt>GraphicsNode</tt> for the input
467: * <tt>Marker</tt> to be drawn at the middle positions
468: */
469: protected ProxyGraphicsNode[] buildMiddleMarkerProxies() {
470:
471: ExtendedPathIterator iter = getExtShape()
472: .getExtendedPathIterator();
473:
474: double[] prev = new double[7];
475: double[] curr = new double[7];
476: double[] next = new double[7], tmp = null;
477: int prevSegType = 0, currSegType = 0, nextSegType = 0;
478:
479: // Get the first three points on the path
480: if (iter.isDone()) {
481: return null;
482: }
483:
484: prevSegType = iter.currentSegment(prev);
485: double[] moveTo = new double[2];
486:
487: if (prevSegType != PathIterator.SEG_MOVETO) {
488: return null;
489: }
490:
491: moveTo[0] = prev[0];
492: moveTo[1] = prev[1];
493: iter.next();
494:
495: if (iter.isDone()) {
496: return null;
497: }
498:
499: currSegType = iter.currentSegment(curr);
500:
501: if (currSegType == PathIterator.SEG_MOVETO) {
502: moveTo[0] = curr[0];
503: moveTo[1] = curr[1];
504: } else if (currSegType == PathIterator.SEG_CLOSE) {
505: currSegType = PathIterator.SEG_LINETO;
506: curr[0] = moveTo[0];
507: curr[1] = moveTo[1];
508: }
509:
510: iter.next();
511:
512: List proxies = new ArrayList();
513: while (!iter.isDone()) {
514: nextSegType = iter.currentSegment(next);
515:
516: if (nextSegType == PathIterator.SEG_MOVETO) {
517: moveTo[0] = next[0];
518: moveTo[1] = next[1];
519: } else if (nextSegType == PathIterator.SEG_CLOSE) {
520: nextSegType = PathIterator.SEG_LINETO;
521: next[0] = moveTo[0];
522: next[1] = moveTo[1];
523: }
524:
525: proxies.add(createMiddleMarker(prev, prevSegType, curr,
526: currSegType, next, nextSegType));
527:
528: tmp = prev;
529: prev = curr;
530: prevSegType = currSegType;
531: curr = next;
532: currSegType = nextSegType;
533: next = tmp;
534:
535: iter.next();
536: }
537:
538: ProxyGraphicsNode[] gn = new ProxyGraphicsNode[proxies.size()];
539: proxies.toArray(gn);
540:
541: return gn;
542: }
543:
544: /**
545: * Creates a ProxyGraphicsNode for a middle marker.
546: */
547: private ProxyGraphicsNode createMiddleMarker(double[] prev,
548: int prevSegType, double[] curr, int currSegType,
549: double[] next, int nextSegType) {
550:
551: // Turn the curr segment into a position
552: Point2D markerPosition = getSegmentTerminatingPoint(curr,
553: currSegType);
554:
555: // If the marker's orient property is NaN,
556: // the slope needs to be computed
557: double rotation = middleMarker.getOrient();
558: if (Double.isNaN(rotation)) {
559: rotation = computeRotation(prev, prevSegType, curr,
560: currSegType, next, nextSegType);
561: }
562:
563: // Now, compute the marker's proxy transform
564: AffineTransform markerTxf = computeMarkerTransform(
565: middleMarker, markerPosition, rotation);
566:
567: ProxyGraphicsNode gn = new ProxyGraphicsNode();
568:
569: gn.setSource(middleMarker.getMarkerNode());
570: gn.setTransform(markerTxf);
571:
572: return gn;
573: }
574:
575: /**
576: * Returns the rotation according to the specified parameters in degrees.
577: */
578: private double computeRotation(double[] prev, int prevSegType,
579: double[] curr, int currSegType, double[] next,
580: int nextSegType) {
581:
582: // Compute in slope, i.e., the slope of the segment
583: // going into the current point
584: double[] inSlope = computeInSlope(prev, prevSegType, curr,
585: currSegType);
586:
587: // Compute out slope, i.e., the slope of the segment
588: // going out of the current point
589: double[] outSlope = computeOutSlope(curr, currSegType, next,
590: nextSegType);
591:
592: if (inSlope == null) {
593: inSlope = outSlope;
594: }
595:
596: if (outSlope == null) {
597: outSlope = inSlope;
598: }
599:
600: if (inSlope == null) {
601: return 0;
602: }
603:
604: double dx = inSlope[0] + outSlope[0];
605: double dy = inSlope[1] + outSlope[1];
606:
607: if (dx == 0 && dy == 0) {
608: // The two vectors are exact opposites. There is no way to
609: // know which direction to go (+90 or -90). Choose +90
610: return Math.toDegrees(Math.atan2(inSlope[1], inSlope[0])) + 90;
611: } else {
612: return Math.toDegrees(Math.atan2(dy, dx));
613: }
614: }
615:
616: /**
617: * Returns dx/dy for the in slope.
618: */
619: private double[] computeInSlope(double[] prev, int prevSegType,
620: double[] curr, int currSegType) {
621:
622: // Compute point into which the slope runs
623: Point2D currEndPoint = getSegmentTerminatingPoint(curr,
624: currSegType);
625:
626: double dx = 0;
627: double dy = 0;
628:
629: switch (currSegType) {
630: case PathIterator.SEG_LINETO: {
631: // This is equivalent to a line from the previous segment's
632: // terminating point and the current end point.
633: Point2D prevEndPoint = getSegmentTerminatingPoint(prev,
634: prevSegType);
635: dx = currEndPoint.getX() - prevEndPoint.getX();
636: dy = currEndPoint.getY() - prevEndPoint.getY();
637: }
638: break;
639: case PathIterator.SEG_QUADTO:
640: // If the current segment is a line, quad or cubic curve.
641: // the slope is about equal to that of the line from the
642: // last control point and the curEndPoint
643: dx = currEndPoint.getX() - curr[0];
644: dy = currEndPoint.getY() - curr[1];
645: break;
646: case PathIterator.SEG_CUBICTO:
647: // If the current segment is a quad or cubic curve.
648: // the slope is about equal to that of the line from the
649: // last control point and the curEndPoint
650: dx = currEndPoint.getX() - curr[2];
651: dy = currEndPoint.getY() - curr[3];
652: break;
653: case ExtendedPathIterator.SEG_ARCTO: {
654: // If the current segment is an ARCTO then we build the
655: // arc and ask for it's end angle and get the tangent there.
656: Point2D prevEndPoint = getSegmentTerminatingPoint(prev,
657: prevSegType);
658: boolean large = (curr[3] != 0.);
659: boolean goLeft = (curr[4] != 0.);
660: Arc2D arc = ExtendedGeneralPath.computeArc(prevEndPoint
661: .getX(), prevEndPoint.getY(), curr[0], curr[1],
662: curr[2], large, goLeft, curr[5], curr[6]);
663: double theta = arc.getAngleStart() + arc.getAngleExtent();
664: theta = Math.toRadians(theta);
665: dx = -arc.getWidth() / 2.0 * Math.sin(theta);
666: dy = arc.getHeight() / 2.0 * Math.cos(theta);
667:
668: // System.out.println("In Theta: " + Math.toDegrees(theta) +
669: // " Dx/Dy: " + dx + "/" + dy);
670: if (curr[2] != 0) {
671: double ang = Math.toRadians(-curr[2]);
672: double sinA = Math.sin(ang);
673: double cosA = Math.cos(ang);
674: double tdx = dx * cosA - dy * sinA;
675: double tdy = dx * sinA + dy * cosA;
676: dx = tdx;
677: dy = tdy;
678: }
679: // System.out.println(" Rotate: " + curr[2] +
680: // " Dx/Dy: " + dx + "/" + dy);
681: if (goLeft) {
682: dx = -dx;
683: } else {
684: dy = -dy;
685: }
686: // System.out.println(" GoLeft? " + goLeft +
687: // " Dx/Dy: " + dx + "/" + dy);
688: }
689: break;
690: case PathIterator.SEG_CLOSE:
691: // Should not have any close at this point
692: throw new Error("should not have SEG_CLOSE here");
693: case PathIterator.SEG_MOVETO:
694: // Cannot compute the slope
695: default:
696: return null;
697: }
698:
699: if (dx == 0 && dy == 0) {
700: return null;
701: }
702:
703: return normalize(new double[] { dx, dy });
704: }
705:
706: /**
707: * Returns dx/dy for the out slope.
708: */
709: private double[] computeOutSlope(double[] curr, int currSegType,
710: double[] next, int nextSegType) {
711:
712: Point2D currEndPoint = getSegmentTerminatingPoint(curr,
713: currSegType);
714:
715: double dx = 0, dy = 0;
716:
717: switch (nextSegType) {
718: case PathIterator.SEG_CLOSE:
719: // Should not happen at this point, because all close
720: // segments have been replaced by lineTo segments.
721: break;
722: case PathIterator.SEG_CUBICTO:
723: case PathIterator.SEG_LINETO:
724: case PathIterator.SEG_QUADTO:
725: // If the next segment is a line, quad or cubic curve.
726: // the slope is about equal to that of the line from
727: // curEndPoint and the first control point
728: dx = next[0] - currEndPoint.getX();
729: dy = next[1] - currEndPoint.getY();
730: break;
731: case ExtendedPathIterator.SEG_ARCTO: {
732: // If the current segment is an ARCTO then we build the
733: // arc and ask for it's end angle and get the tangent there.
734: boolean large = (next[3] != 0.);
735: boolean goLeft = (next[4] != 0.);
736: Arc2D arc = ExtendedGeneralPath.computeArc(currEndPoint
737: .getX(), currEndPoint.getY(), next[0], next[1],
738: next[2], large, goLeft, next[5], next[6]);
739: double theta = arc.getAngleStart();
740: theta = Math.toRadians(theta);
741: dx = -arc.getWidth() / 2.0 * Math.sin(theta);
742: dy = arc.getHeight() / 2.0 * Math.cos(theta);
743: // System.out.println("Out Theta: " + Math.toDegrees(theta) +
744: // " Dx/Dy: " + dx + "/" + dy);
745: if (next[2] != 0) {
746: double ang = Math.toRadians(-next[2]);
747: double sinA = Math.sin(ang);
748: double cosA = Math.cos(ang);
749: double tdx = dx * cosA - dy * sinA;
750: double tdy = dx * sinA + dy * cosA;
751: dx = tdx;
752: dy = tdy;
753: }
754: // System.out.println(" Rotate: " + next[2] +
755: // " Dx/Dy: " + dx + "/" + dy);
756:
757: if (goLeft) {
758: dx = -dx;
759: } else {
760: dy = -dy;
761: }
762: // System.out.println(" GoLeft? " + goLeft +
763: // " Dx/Dy: " + dx + "/" + dy);
764: }
765: break;
766: case PathIterator.SEG_MOVETO:
767: // Cannot compute the out slope
768: default:
769: return null;
770: }
771:
772: if (dx == 0 && dy == 0) {
773: return null;
774: }
775:
776: return normalize(new double[] { dx, dy });
777: }
778:
779: /**
780: * Normalizes the input vector. This assumes an non-zero length
781: */
782: public double[] normalize(double[] v) {
783: double n = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
784: v[0] /= n;
785: v[1] /= n;
786: return v;
787: }
788:
789: /**
790: * Computes the transform for the input marker, so that it is positioned at
791: * the given position with the specified rotation
792: */
793: private AffineTransform computeMarkerTransform(Marker marker,
794: Point2D markerPosition, double rotation) {
795: Point2D ref = marker.getRef();
796: /*AffineTransform txf =
797: AffineTransform.getTranslateInstance(markerPosition.getX()
798: - ref.getX(),
799: markerPosition.getY()
800: - ref.getY());*/
801: AffineTransform txf = new AffineTransform();
802:
803: txf.translate(markerPosition.getX() - ref.getX(),
804: markerPosition.getY() - ref.getY());
805:
806: if (!Double.isNaN(rotation)) {
807: txf
808: .rotate(Math.toRadians(rotation), ref.getX(), ref
809: .getY());
810: }
811:
812: return txf;
813: }
814:
815: /**
816: * Extracts the terminating point, depending on the segment type.
817: */
818: protected Point2D getSegmentTerminatingPoint(double[] coords,
819: int segType) {
820: switch (segType) {
821: case PathIterator.SEG_CUBICTO:
822: return new Point2D.Double(coords[4], coords[5]);
823: case PathIterator.SEG_LINETO:
824: return new Point2D.Double(coords[0], coords[1]);
825: case PathIterator.SEG_MOVETO:
826: return new Point2D.Double(coords[0], coords[1]);
827: case PathIterator.SEG_QUADTO:
828: return new Point2D.Double(coords[2], coords[3]);
829: case ExtendedPathIterator.SEG_ARCTO:
830: return new Point2D.Double(coords[5], coords[6]);
831: case PathIterator.SEG_CLOSE:
832: default:
833: throw new Error("invalid segmentType:" + segType);
834: // Should never happen: close segments are replaced with lineTo
835: }
836: }
837: }
|