001: /*
002: * $RCSfile: TransformSegment.java,v $
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026: package com.sun.perseus.model;
027:
028: import com.sun.perseus.platform.MathSupport;
029:
030: /**
031: * Represents a segment in an animateTransform.
032: *
033: *
034: * @version $Id: TransformSegment.java,v 1.3 2006/06/29 10:47:36 ln156897 Exp $
035: */
036: public class TransformSegment implements Segment {
037: /**
038: * Holds the minimal information for the segment's start.
039: * The interpretation depends on the segment type.
040: * TYPE_TRANSLATE: start[0] = tx, start[1] = ty
041: * TYPE_SCALE: start[0] = sx, start[1] = sy
042: * TYPE_ROTATE: start[0] = rotate, start[1] = cx, start[2] = cy;
043: * TYPE_SKEW_X: start[0] = skewX
044: * TYPE_SKEW_Y: start[0] = skewY
045: */
046: float[] start;
047:
048: /**
049: * Holds the minimal information for the segment's end.
050: * @see #start
051: */
052: float[] end;
053:
054: /**
055: * The segment type.
056: */
057: int type;
058:
059: /**
060: * @return the start value.
061: */
062: public Object[] getStart() {
063: float[][] v = new float[6][1];
064: compute(0, v);
065: return v;
066: }
067:
068: /**
069: * @return set end value.
070: */
071: public Object[] getEnd() {
072: float[][] v = new float[6][1];
073: compute(1, v);
074: return v;
075: }
076:
077: /**
078: * Sets the start value to its notion of 'zero'.
079: * For a FloatSegment, a 'zero' start means zero all all
080: * dimensions for all components.
081: */
082: public void setZeroStart() {
083: switch (type) {
084: case AnimateTransform.TYPE_TRANSLATE:
085: start[0] = 0;
086: start[1] = 0;
087: break;
088: case AnimateTransform.TYPE_SCALE:
089: start[0] = 1;
090: start[1] = 1;
091: break;
092: case AnimateTransform.TYPE_ROTATE:
093: start[0] = 0;
094: break;
095: case AnimateTransform.TYPE_SKEW_X:
096: case AnimateTransform.TYPE_SKEW_Y:
097: default:
098: start[0] = 0;
099: break;
100: }
101: }
102:
103: /**
104: * Sets the start value.
105: *
106: * @param newStart the new segment start value.
107: */
108: public void setStart(Object[] newStart) {
109: float[][] ns = (float[][]) newStart;
110: switch (type) {
111: case AnimateTransform.TYPE_TRANSLATE:
112: start[0] = ns[4][0];
113: start[1] = ns[5][0];
114: break;
115: case AnimateTransform.TYPE_SCALE:
116: start[0] = ns[0][0];
117: start[1] = ns[3][0];
118: break;
119: case AnimateTransform.TYPE_ROTATE:
120: // This assumes that the input matrix is a simple rotate.
121: // We are not trying to extract the rotation from a complex
122: // matrix.
123: start[0] = MathSupport.atan2(ns[1][0], ns[0][0]);
124: break;
125: case AnimateTransform.TYPE_SKEW_X:
126: start[0] = MathSupport.atan(ns[2][0]);
127: break;
128: case AnimateTransform.TYPE_SKEW_Y:
129: default:
130: start[0] = MathSupport.atan(ns[1][0]);
131: break;
132: }
133: }
134:
135: /**
136: * Collapses this segment with the one passed as a parameter.
137: * Note that if the input segment is not of the same class
138: * as this one, an IllegalArgumentException is thrown. The
139: * method also throws an exception if the input segment's
140: * end does not have the same number of components as this
141: * segment's end.
142: *
143: * After this method is called, this segment's end value
144: * is the one of the input <code>seg</code> parameter.
145: *
146: * @param seg the Segment to collapse with this one.
147: * @param anim the Animation this segment is part of.
148: */
149: public void collapse(final Segment seg, final Animation anim) {
150: TransformSegment mseg = (TransformSegment) seg;
151: if (mseg.end.length != end.length) {
152: throw new IllegalArgumentException();
153: }
154:
155: end = mseg.end;
156: }
157:
158: /**
159: * Adds the input value to this Segment's end value.
160: *
161: * @param by the value to add. Throws IllegalArgumentException if this
162: * Segment type is not additive or if the input value is incompatible (e.g.,
163: * different number of components or different number of dimensions on a
164: * component).
165: */
166: public void addToEnd(Object[] by) {
167: float[][] ns = (float[][]) by;
168: switch (type) {
169: case AnimateTransform.TYPE_TRANSLATE:
170: end[0] += ns[4][0];
171: end[1] += ns[5][0];
172: break;
173: case AnimateTransform.TYPE_SCALE:
174: end[0] += ns[0][0];
175: end[1] += ns[3][0];
176: break;
177: case AnimateTransform.TYPE_ROTATE:
178: // Here, we need to add a value that has the form
179: // [1 0 x] [cos -sin 0] [1 0 -x] [cos -sin x-x.cos+y.sin]
180: // [0 1 y] [sin cos 0] [0 1 -y] = [sin cos y-x.sin-y.cos]
181: // [0 0 1] [0 0 1] [0 0 1] [0 0 1 ]
182: //
183: // We have:
184: // x.(1 - cos) + y.sin = a
185: // y.(1 - cos) - x.sin = b
186: //
187: // So:
188: // I : x.(1 - cos).sin + y.sin.sin = a.sin
189: // II : y.(1 - cos).(1 - cos) - x.sin.(1 - cos) = b.(1 - cos)
190: //
191: // Doing I + II yields:
192: //
193: // y.sin.sin + y.(1 - cos).(1 - cos) = a.sin + b.(1 - cos)
194: //
195: // y.(sin.sin + 1 - 2.cos + cos.cos) = a.sin + b.(1 - cos)
196: //
197: // y.2.(1 - cos) = a.sin + b.(1 - cos)
198: //
199:
200: // First, extract the rotation angle
201: float cos = ns[0][0];
202: float sin = ns[1][0];
203:
204: end[0] += MathSupport.atan2(ns[1][0], ns[0][0]);
205:
206: // Now extract the translation components.
207: float a = ns[4][0];
208: float b = ns[5][0];
209:
210: if (a != 0 && b != 0) {
211: // There is a translation component
212: float div = 2 * (1 - cos);
213: if (div != 0) {
214: float y = (a * sin + b * (1 - cos)) / div;
215: float x = (a - y * sin) / (1 - cos);
216: end[1] += x;
217: end[2] += y;
218: }
219: // else div = 0, there is no rotation, so we do not further
220: // process the translation component.
221: }
222: break;
223: case AnimateTransform.TYPE_SKEW_X:
224: end[0] += MathSupport.atan(ns[2][0]);
225: break;
226: case AnimateTransform.TYPE_SKEW_Y:
227: default:
228: end[0] += MathSupport.atan(ns[1][0]);
229: break;
230: }
231: }
232:
233: /**
234: * @return true if this segment type supports addition. false
235: * otherwise.
236: */
237: public boolean isAdditive() {
238: return true;
239: }
240:
241: /**
242: * @return the length of the segment
243: */
244: public float getLength() {
245: float length = 0;
246: switch (type) {
247: case AnimateTransform.TYPE_TRANSLATE:
248: if (end[0] > start[0]) {
249: length += end[0] - start[0];
250: } else {
251: length += start[0] - end[0];
252: }
253:
254: if (end[1] > start[1]) {
255: length += end[1] - start[1];
256: } else {
257: length += start[1] - end[1];
258: }
259: return length;
260:
261: case AnimateTransform.TYPE_SCALE:
262: if (end[0] > start[0]) {
263: length += end[0] - start[0];
264: } else {
265: length += start[0] - end[0];
266: }
267:
268: if (end[1] > start[1]) {
269: length += end[1] - start[1];
270: } else {
271: length += start[1] - end[1];
272: }
273:
274: return length;
275:
276: case AnimateTransform.TYPE_ROTATE:
277: if (end[0] > start[0]) {
278: length += end[0] - start[0];
279: } else {
280: length += start[0] - end[0];
281: }
282:
283: if (end[1] > start[1]) {
284: length += end[1] - start[1];
285: } else {
286: length += start[1] - end[1];
287: }
288:
289: if (end[2] > start[2]) {
290: length += end[2] - start[2];
291: } else {
292: length += start[2] - end[2];
293: }
294:
295: return length;
296: case AnimateTransform.TYPE_SKEW_X:
297: if (end[0] > start[0]) {
298: length += end[0] - start[0];
299: } else {
300: length += start[0] - end[0];
301: }
302:
303: return length;
304: case AnimateTransform.TYPE_SKEW_Y:
305: default:
306: if (end[0] > start[0]) {
307: length += end[0] - start[0];
308: } else {
309: length += start[0] - end[0];
310: }
311:
312: return length;
313: }
314: }
315:
316: /**
317: * Computes an interpolated value for the given penetration in the
318: * segment. Note that the start and end segment values must be set
319: * <em>before</em> calling this method. Otherwise, a NullPointerException
320: * is thrown.
321: *
322: * @param p the segment penetration. Should be in the [0, 1] range.
323: * @param w array where the computed value should be stored.
324: * @return the interpolated value.
325: */
326: public Object[] compute(final float p, final float[][] w) {
327: switch (type) {
328: case AnimateTransform.TYPE_TRANSLATE:
329: w[0][0] = 1;
330: w[1][0] = 0;
331: w[2][0] = 0;
332: w[3][0] = 1;
333: w[4][0] = (1 - p) * start[0] + p * end[0];
334: w[5][0] = (1 - p) * start[1] + p * end[1];
335: break;
336:
337: case AnimateTransform.TYPE_SCALE:
338: w[0][0] = (1 - p) * start[0] + p * end[0];
339: w[1][0] = 0;
340: w[2][0] = 0;
341: w[3][0] = (1 - p) * start[1] + p * end[1];
342: w[4][0] = 0;
343: w[5][0] = 0;
344: break;
345:
346: case AnimateTransform.TYPE_ROTATE: {
347: float x = (1 - p) * start[1] + p * end[1];
348: float y = (1 - p) * start[2] + p * end[2];
349: float theta = (1 - p) * start[0] + p * end[0];
350: float sin = MathSupport.sin(theta);
351: float cos = MathSupport.cos(theta);
352:
353: w[0][0] = cos;
354: w[1][0] = sin;
355: w[2][0] = -sin;
356: w[3][0] = cos;
357: w[4][0] = (x - x * cos + y * sin);
358: w[5][0] = (y - x * sin - y * cos);
359: }
360: break;
361: case AnimateTransform.TYPE_SKEW_X: {
362: float theta = (1 - p) * start[0] + p * end[0];
363: w[0][0] = 1;
364: w[1][0] = 0;
365: w[2][0] = MathSupport.tan(theta);
366: w[3][0] = 1;
367: w[4][0] = 0;
368: w[5][0] = 0;
369: }
370: break;
371:
372: case AnimateTransform.TYPE_SKEW_Y:
373: default: {
374: float theta = (1 - p) * start[0] + p * end[0];
375: w[0][0] = 1;
376: w[1][0] = MathSupport.tan(theta);
377: w[2][0] = 0;
378: w[3][0] = 1;
379: w[4][0] = 0;
380: w[5][0] = 0;
381: }
382: break;
383: }
384:
385: return w;
386: }
387:
388: /**
389: * Should be called after the segment's configuration is complete
390: * to give the segment's implementation a chance to initialize
391: * internal data and cache values.
392: */
393: public void initialize() {
394: }
395:
396: }
|