001: /*
002: * $Id: MorphShape.java,v 1.2 2002/03/14 20:27:38 awason Exp $
003: *
004: * ==========================================================================
005: *
006: * The JGenerator Software License, Version 1.0
007: *
008: * Copyright (c) 2000 Dmitry Skavish (skavish@usa.net). All rights reserved.
009: *
010: * Redistribution and use in source and binary forms, with or without
011: * modification, are permitted provided that the following conditions are met:
012: *
013: * 1. Redistributions of source code must retain the above copyright
014: * notice, this list of conditions and the following disclaimer.
015: *
016: * 2. Redistributions in binary form must reproduce the above copyright
017: * notice, this list of conditions and the following disclaimer in
018: * the documentation and/or other materials provided with the
019: * distribution.
020: *
021: * 3. The end-user documentation included with the redistribution, if
022: * any, must include the following acknowlegement:
023: * "This product includes software developed by Dmitry Skavish
024: * (skavish@usa.net, http://www.flashgap.com/)."
025: * Alternately, this acknowlegement may appear in the software itself,
026: * if and wherever such third-party acknowlegements normally appear.
027: *
028: * 4. The name "The JGenerator" must not be used to endorse or promote
029: * products derived from this software without prior written permission.
030: * For written permission, please contact skavish@usa.net.
031: *
032: * 5. Products derived from this software may not be called "The JGenerator"
033: * nor may "The JGenerator" appear in their names without prior written
034: * permission of Dmitry Skavish.
035: *
036: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
037: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
038: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
039: * DISCLAIMED. IN NO EVENT SHALL DMITRY SKAVISH OR THE OTHER
040: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
041: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
042: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
043: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
044: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
045: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
046: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
047: * SUCH DAMAGE.
048: *
049: */
050:
051: package org.openlaszlo.iv.flash.api.shape;
052:
053: import java.io.PrintStream;
054: import java.awt.geom.*;
055: import java.util.*;
056:
057: import org.openlaszlo.iv.flash.parser.*;
058: import org.openlaszlo.iv.flash.util.*;
059: import org.openlaszlo.iv.flash.api.*;
060:
061: /**
062: * MorphShape.
063: * <P>
064: * The DefineMorphShape tag defines the start and end states of a morph sequence.
065: * A morph object does not use previously defined shapes, it is considered a special
066: * type of shape with only one character ID. A morph object should be displayed with
067: * the PlaceObject2 tag, where the ratio field specifies how far the morph has progressed.
068: * <P>
069: * StartBounds defines the bounding-box of the shape at the start of the morph,
070: * and EndBounds defines the bounding-box at the end of the morph
071: * <P>
072: * MorphFillStyles contains an array interleaved fill styles for the start and end shapes.
073: * The fill style for the start shape is followed the corresponding fill style for the end shape.
074: * Similarly, MorphLineStyles contains an array of interleaved line styles.
075: * <P>
076: * The StartEdges array specifies the edges for the start shape, and the style change records for
077: * both shapes. Because the StyleChangeRecords must be the same for the start and end shapes,
078: * they are defined only in the StartEdges array. The EndEdges array specifies the edges for
079: * the end shape, and contains no style change records. The number of edges specified in StartEdges
080: * must equal the number of edges in EndEdges.
081: * <P>
082: * Note: Strictly speaking MoveTo records fall into the category of StyleChangeRecords,
083: * however they should be included in both the StartEdges and EndEdges arrays.
084: * <P>
085: * It is possible for an edge to change type over the course of a morph sequence.
086: * A straight edge can become a curved edge and vice versa. In this case, think of
087: * both edges as curved. A straight edge can be easily represented as a curve,
088: * by placing the off-curve (control) point at the mid-point of the straight edge,
089: * and the on-curve (anchor) point at the end of the straight edge.
090: * The calculation is as follows:
091: * <PRE><CODE>
092: * CurveControlDelta.x = StraightDelta.x / 2;
093: * CurveControlDelta.y = StraightDelta.y / 2;
094: * CurveAnchorDelta.x = StraightDelta.x / 2;
095: * CurveAnchorDelta.y = StraightDelta.y / 2;
096: * </CODE></PRE>
097: *
098: * @author Dmitry Skavish
099: * @see Shape
100: */
101: public final class MorphShape extends FlashDef {
102:
103: private MorphShapeStyles shape_styles; // morph shape styles
104: private ShapeRecords records_start; // start records
105: private ShapeRecords records_end; // end records
106: private Rectangle2D bounds_start; // bounding box of start shape
107: private Rectangle2D bounds_end; // bounding box of end shape
108:
109: /**
110: * Creates new empty morph shape
111: */
112: public MorphShape() {
113: }
114:
115: /**
116: * Returns shape styles
117: *
118: * @return object representing array of shape styles
119: */
120: public MorphShapeStyles getShapeStyles() {
121: if (shape_styles == null) {
122: shape_styles = new MorphShapeStyles();
123: }
124: return shape_styles;
125: }
126:
127: /**
128: * Returns start shape records
129: *
130: * @return object representing array of shape records
131: */
132: public ShapeRecords getShapeRecordsStart() {
133: if (records_start == null) {
134: records_start = new ShapeRecords();
135: }
136: return records_start;
137: }
138:
139: /**
140: * Returns end shape records
141: *
142: * @return object representing array of shape records
143: */
144: public ShapeRecords getShapeRecordsEnd() {
145: if (records_end == null) {
146: records_end = new ShapeRecords();
147: }
148: return records_end;
149: }
150:
151: /**
152: * Adds specified fill style to the array of shape styles.
153: *
154: * @param fillStyle specified fill style
155: * @return index of added fill style in the array
156: */
157: public int addFillStyle(MorphFillStyle fillStyle) {
158: return getShapeStyles().addFillStyle(fillStyle);
159: }
160:
161: /**
162: * Adds specified line style to the array of shape styles
163: *
164: * @param lineStyle specified line style
165: * @return index of added line style in the array
166: */
167: public int addLineStyle(MorphLineStyle lineStyle) {
168: return getShapeStyles().addLineStyle(lineStyle);
169: }
170:
171: /**
172: * Sets specified fillstyle as current fillstyle0.
173: * <P>
174: * If the specified fillstyle is not in the shapestyle array
175: * then it's added, if it's already there it's reused.
176: *
177: * @param fillStyle specified fillstyle
178: * @return index of specified fillstyle in the shapestyle array
179: */
180: public int setFillStyle0(MorphFillStyle fillStyle) {
181: int fs = getShapeStyles().getFillStyleIndex(fillStyle);
182: if (fs == -1)
183: fs = addFillStyle(fillStyle);
184: setFillStyle0(fs);
185: return fs;
186: }
187:
188: /**
189: * Sets specified fillstyle as current fillstyle1.
190: * <P>
191: * If the specified fillstyle is not in the shapestyle array
192: * then it's added, if it's already there it's reused.
193: *
194: * @param fillStyle specified fillstyle
195: * @return index of specified fillstyle in the shapestyle array
196: */
197: public int setFillStyle1(MorphFillStyle fillStyle) {
198: int fs = getShapeStyles().getFillStyleIndex(fillStyle);
199: if (fs == -1)
200: fs = addFillStyle(fillStyle);
201: setFillStyle1(fs);
202: return fs;
203: }
204:
205: /**
206: * Sets specified linestyle as current linestyle.
207: * <P>
208: * If the specified linestyle is not in the shapestyle array
209: * then it's added, if it's already there it's reused.
210: *
211: * @param lineStyle specified linestyle
212: * @return index of specified linestyle in the shapestyle array
213: */
214: public int setLineStyle(MorphLineStyle lineStyle) {
215: int ls = getShapeStyles().getLineStyleIndex(lineStyle);
216: if (ls == -1)
217: ls = addLineStyle(lineStyle);
218: setLineStyle(ls);
219: return ls;
220: }
221:
222: /**
223: * Sets current fillstyle0 by its index in shapestyle array.
224: *
225: * @param fillStyle index of fillstyle in shapestyle array to be set as current fillstyle0
226: */
227: public void setFillStyle0(int fillStyle) {
228: StyleChangeRecord sc = getStyleChange();
229: sc.addFlags(StyleChangeRecord.FILLSTYLE0);
230: sc.setFillStyle0(fillStyle);
231: }
232:
233: /**
234: * Sets current fillstyle1 by its index in shapestyle array.
235: *
236: * @param fillStyle index of fillstyle in shapestyle array to be set as current fillstyle1
237: */
238: public void setFillStyle1(int fillStyle) {
239: StyleChangeRecord sc = getStyleChange();
240: sc.addFlags(StyleChangeRecord.FILLSTYLE1);
241: sc.setFillStyle1(fillStyle);
242: }
243:
244: /**
245: * Sets current linestyle by its index in shapestyle array.
246: *
247: * @param lineStyle index of linestyle in shapestyle array to be set as current linestyle
248: */
249: public void setLineStyle(int lineStyle) {
250: StyleChangeRecord sc = getStyleChange();
251: sc.addFlags(StyleChangeRecord.LINESTYLE);
252: sc.setLineStyle(lineStyle);
253: }
254:
255: /**
256: * Returns index of current line style
257: *
258: * @return index of currently used line style
259: */
260: public int getLineStyleIndex() {
261: StyleChangeRecord sc = getStyleChange();
262: return sc.getLineStyle();
263: }
264:
265: /**
266: * Returns current line style
267: *
268: * @return currently used line style
269: */
270: public MorphLineStyle getLineStyle() {
271: int idx = getLineStyleIndex();
272: return getShapeStyles().getLineStyle(idx);
273: }
274:
275: /**
276: * Returns index of current fill style 0
277: *
278: * @return index of currently used fill style 0
279: */
280: public int getFillStyle0Index() {
281: StyleChangeRecord sc = getStyleChange();
282: return sc.getFillStyle0();
283: }
284:
285: /**
286: * Returns current fill style 0
287: *
288: * @return currently used fill style 0
289: */
290: public MorphFillStyle getFillStyle0() {
291: int idx = getFillStyle0Index();
292: return getShapeStyles().getFillStyle(idx);
293: }
294:
295: /**
296: * Returns index of current fill style 1
297: *
298: * @return index of currently used fill style 1
299: */
300: public int getFillStyle1Index() {
301: StyleChangeRecord sc = getStyleChange();
302: return sc.getFillStyle1();
303: }
304:
305: /**
306: * Returns current fill style 1
307: *
308: * @return currently used fill style 1
309: */
310: public MorphFillStyle getFillStyle1() {
311: int idx = getFillStyle1Index();
312: return getShapeStyles().getFillStyle(idx);
313: }
314:
315: public int getTag() {
316: return Tag.DEFINEMORPHSHAPE;
317: }
318:
319: public void collectDeps(DepsCollector dc) {
320: shape_styles.collectDeps(dc);
321: }
322:
323: protected StyleChangeRecord getStyleChange() {
324: return getShapeRecordsStart().getStyleChange();
325: }
326:
327: /**
328: * Parses Shape
329: *
330: * @param p Parser
331: * @return parsed shape
332: */
333: public static MorphShape parse(Parser p) {
334:
335: int tagCode = p.getTagCode();
336: MorphShape shape = new MorphShape();
337: shape.setID(p.getUWord());
338: shape.bounds_start = p.getRect();
339: shape.bounds_end = p.getRect();
340:
341: int offset = p.getUDWord();
342: int pos = p.getPos();
343:
344: // parse first styles
345: shape.shape_styles = MorphShapeStyles.parse(p);
346:
347: shape.records_start = ShapeRecords.parse(p);
348:
349: p.setPos(pos + offset);
350:
351: shape.records_end = ShapeRecords.parse(p);
352:
353: return shape;
354: }
355:
356: public void write(FlashOutput main) {
357: FlashOutput fob = new FlashOutput(main, 200);
358:
359: fob.write(bounds_start);
360: fob.write(bounds_end);
361:
362: fob.skip(4);
363: int pos = fob.getPos();
364:
365: shape_styles.write(fob);
366:
367: int nFillBits = shape_styles.calcNFillBits();
368: int nLineBits = shape_styles.calcNLineBits();
369: fob.writeByte((nFillBits << 4) | nLineBits);
370: records_start.write(fob, nFillBits, nLineBits);
371: fob.writeBits(0, 6);
372: fob.flushBits();
373:
374: int offset = fob.getPos() - pos;
375: fob.writeDWordAt(offset, pos - 4);
376:
377: fob.writeByte(0); // fill and line style bits
378: records_end.write(fob);
379:
380: main.writeTag(getTag(), 2 + fob.getSize());
381: main.writeDefID(this );
382: main.writeFOB(fob);
383: }
384:
385: public void printContent(PrintStream out, String indent) {
386: out.println(indent + "MorphShape(" + Tag.tagNames[getTag()]
387: + "): id=" + getID() + ", name='" + getName() + "'");
388: out.println(indent + " start bounds=" + bounds_start);
389: out.println(indent + " end bounds=" + bounds_end);
390: shape_styles.printContent(out, " ");
391: records_start.printContent(out, " ");
392: records_end.printContent(out, " ");
393: }
394:
395: public boolean isConstant() {
396: return true;
397: }
398:
399: /**
400: * Returns MorphShape bounds
401: *
402: * @return shape bounds
403: */
404: public Rectangle2D getBounds() {
405: Rectangle2D dst = GeomHelper.newRectangle();
406: Rectangle2D.union(bounds_start, bounds_end, dst);
407: return dst;
408: }
409:
410: /**
411: * Returns MorphShape start shape bounds
412: *
413: * @return shape bounds
414: */
415: public Rectangle2D getBoundsStart() {
416: return this .bounds_start;
417: }
418:
419: /**
420: * Returns MorphShape end shape bounds
421: *
422: * @return shape bounds
423: */
424: public Rectangle2D getBoundsEnd() {
425: return this .bounds_end;
426: }
427:
428: /**
429: * Sets bounding box for start shape.
430: *
431: * @param bounds new bounding box
432: */
433: public void setBoundsStart(Rectangle2D bounds) {
434: this .bounds_start = bounds;
435: }
436:
437: /**
438: * Sets bounding box for end shape.
439: *
440: * @param bounds new bounding box
441: */
442: public void setBoundsEnd(Rectangle2D bounds) {
443: this .bounds_end = bounds;
444: }
445:
446: /**
447: * Sets bounding box for start shape.
448: *
449: */
450: public void setBoundsStart(int x, int y, int width, int height) {
451: setBoundsStart(GeomHelper.newRectangle(x, y, width, height));
452: }
453:
454: /**
455: * Sets bounding box for end shape.
456: *
457: */
458: public void setBoundsEnd(int x, int y, int width, int height) {
459: setBoundsEnd(GeomHelper.newRectangle(x, y, width, height));
460: }
461:
462: protected FlashItem copyInto(FlashItem item, ScriptCopier copier) {
463: super .copyInto(item, copier);
464: ((MorphShape) item).bounds_start = (Rectangle2D) bounds_start
465: .clone();
466: ((MorphShape) item).bounds_end = (Rectangle2D) bounds_end
467: .clone();
468: ((MorphShape) item).records_start = (ShapeRecords) records_start
469: .getCopy(copier);
470: ((MorphShape) item).records_end = (ShapeRecords) records_end
471: .getCopy(copier);
472: ((MorphShape) item).shape_styles = (MorphShapeStyles) shape_styles
473: .getCopy(copier);
474: return item;
475: }
476:
477: public FlashItem getCopy(ScriptCopier copier) {
478: return copyInto(new MorphShape(), copier);
479: }
480:
481: /* ----------------------------------------------------------------------------------- */
482: /* D R A W I N G */
483: /* ----------------------------------------------------------------------------------- */
484:
485: /**
486: * Draws curve record.<p>
487: * All coordinates are in twixels.
488: *
489: * @param cx X control point
490: * @param cy Y control point
491: * @param ax X anchor point
492: * @param ay Y anchor point
493: */
494: public void drawCurveToStart(int cx, int cy, int ax, int ay) {
495: getShapeRecordsStart().drawCurveTo(cx, cy, ax, ay);
496: }
497:
498: public void drawCurveToEnd(int cx, int cy, int ax, int ay) {
499: getShapeRecordsEnd().drawCurveTo(cx, cy, ax, ay);
500: }
501:
502: /**
503: * Draws curve record.<p>
504: * All coordinates are in twixels.
505: *
506: * @param ax1 X anchor point 1
507: * @param ay1 Y anchor point 1
508: * @param cx X control point
509: * @param cy Y control point
510: * @param ax2 X anchor point 2
511: * @param ay2 Y anchor point 2
512: */
513: public void drawCurveStart(int ax1, int ay1, int cx, int cy,
514: int ax2, int ay2) {
515: getShapeRecordsStart().drawCurve(ax1, ay1, cx, cy, ax2, ay2);
516: }
517:
518: public void drawCurveEnd(int ax1, int ay1, int cx, int cy, int ax2,
519: int ay2) {
520: getShapeRecordsEnd().drawCurve(ax1, ay1, cx, cy, ax2, ay2);
521: }
522:
523: /**
524: * Draws curve record.<p>
525: * All coordinates are in twixels.
526: *
527: * @param anchor0 first anchor point
528: * @param control control point
529: * @param anchor1 second anchor point
530: */
531: public void drawCurveStart(Point2D anchor1, Point2D control,
532: Point2D anchor2) {
533: getShapeRecordsStart().drawCurve(anchor1, control, anchor2);
534: }
535:
536: public void drawCurveEnd(Point2D anchor1, Point2D control,
537: Point2D anchor2) {
538: getShapeRecordsEnd().drawCurve(anchor1, control, anchor2);
539: }
540:
541: /**
542: * Draws a straight line from current position to the specified one.<p>
543: * All coordinates are in twixels.
544: *
545: * @param x X of end of line
546: * @param y Y of end of line
547: */
548: public void drawLineToStart(int x, int y) {
549: getShapeRecordsStart().drawLineTo(x, y);
550: }
551:
552: public void drawLineToEnd(int x, int y) {
553: getShapeRecordsEnd().drawLineTo(x, y);
554: }
555:
556: /**
557: * Draws a straight line from current position to the specified one.<p>
558: * All coordinates are in twixels.
559: *
560: * @param p1 end of line
561: */
562: public void drawLineToStart(Point2D p1) {
563: getShapeRecordsStart().drawLineTo(p1);
564: }
565:
566: public void drawLineToEnd(Point2D p1) {
567: getShapeRecordsEnd().drawLineTo(p1);
568: }
569:
570: /**
571: * Draws a straight line specified by two points.
572: * <P>
573: * All coordinates are in twixels.
574: *
575: * @param x1 X of the beginning of the line
576: * @param y1 Y of the beginning of the line
577: * @param x2 X of the end of the line
578: * @param y2 Y of the end of the line
579: */
580: public void drawLineStart(int x1, int y1, int x2, int y2) {
581: getShapeRecordsStart().drawLine(x1, y1, x2, y2);
582: }
583:
584: public void drawLineEnd(int x1, int y1, int x2, int y2) {
585: getShapeRecordsEnd().drawLine(x1, y1, x2, y2);
586: }
587:
588: /**
589: * Draws a straight line specified by two points.
590: * <P>
591: * All coordinates are in twixels.
592: *
593: * @param p0 first point
594: * @param p1 second point
595: */
596: public void drawLineStart(Point2D p0, Point2D p1) {
597: getShapeRecordsStart().drawLine(p0, p1);
598: }
599:
600: public void drawLineEnd(Point2D p0, Point2D p1) {
601: getShapeRecordsEnd().drawLine(p0, p1);
602: }
603:
604: /**
605: * Draws a rectangle specified by its top-left corner and width and height
606: * <p>
607: * All coordinates are in twixels.
608: *
609: * @param x x coordinates of top-left corner of the rectangle
610: * @param y y coordinates of top-left corner of the rectangle
611: * @param width width of the rectangle
612: * @param height height of the rectangle
613: */
614: public void drawRectangleStart(int x, int y, int width, int height) {
615: getShapeRecordsStart().drawRectangle(x, y, width, height);
616: }
617:
618: public void drawRectangleEnd(int x, int y, int width, int height) {
619: getShapeRecordsEnd().drawRectangle(x, y, width, height);
620: }
621:
622: /**
623: * Draws a rectangle specified by {@link java.awt.geom.Rectangle2D}
624: * <p>
625: * All coordinates are in twixels.
626: *
627: * @param r specified rectangle
628: */
629: public void drawRectangleStart(Rectangle2D r) {
630: getShapeRecordsStart().drawRectangle(r);
631: }
632:
633: public void drawRectangleEnd(Rectangle2D r) {
634: getShapeRecordsEnd().drawRectangle(r);
635: }
636:
637: /**
638: * Moves pen to the specified position.<p>
639: * All coordinates are ABSOLUTE and are in twixels.
640: *
641: * @param x new current X
642: * @param y new current Y
643: */
644: public void movePenToStart(int x, int y) {
645: getShapeRecordsStart().movePenTo(x, y);
646: }
647:
648: public void movePenToEnd(int x, int y) {
649: getShapeRecordsEnd().movePenTo(x, y);
650: }
651:
652: /**
653: * Moves pen to the specified point.<p>
654: * All coordinates are ABSOLUTE and are in twixels!
655: *
656: * @param p new pen position
657: */
658: public void movePenToStart(Point2D p) {
659: getShapeRecordsStart().movePenTo(p);
660: }
661:
662: public void movePenToEnd(Point2D p) {
663: getShapeRecordsEnd().movePenTo(p);
664: }
665:
666: /**
667: * Draw AWT Shape
668: * <P>
669: * All shape coordinates are in twixels!
670: *
671: * @param shape AWT shape
672: */
673: public void drawAWTShapeStart(java.awt.Shape shape) {
674: getShapeRecordsStart().drawAWTShape(shape);
675: }
676:
677: public void drawAWTShapeEnd(java.awt.Shape shape) {
678: getShapeRecordsEnd().drawAWTShape(shape);
679: }
680:
681: /**
682: * Draw AWT Shape
683: * <P>
684: * All shape coordinates are in twixels!
685: *
686: * @param shape AWT shape
687: */
688: public void drawAWTShapeStart(java.awt.Shape shape,
689: AffineTransform matrix) {
690: getShapeRecordsStart().drawAWTShape(shape, matrix);
691: }
692:
693: public void drawAWTShapeEnd(java.awt.Shape shape,
694: AffineTransform matrix) {
695: getShapeRecordsEnd().drawAWTShape(shape, matrix);
696: }
697:
698: /**
699: * Draw AWT PathIterator
700: * <P>
701: * All coordinates are in twixels!
702: *
703: * @param pi AWT PathIterator
704: */
705: public void drawAWTPathIteratorStart(java.awt.geom.PathIterator pi) {
706: getShapeRecordsStart().drawAWTPathIterator(pi);
707: }
708:
709: public void drawAWTPathIteratorEnd(java.awt.geom.PathIterator pi) {
710: getShapeRecordsEnd().drawAWTPathIterator(pi);
711: }
712:
713: }
|