AbstractAnimatepublic abstract class AbstractAnimate extends Animation AbstractAnimate is used as a base class for various
animation classes (see Animate and AnimateMotion ). |
Fields Summary |
---|
public static final int | CALC_MODE_LINEARcalcMode value for linear interpolation. | public static final int | CALC_MODE_DISCRETEcalcMode value for discrete interpolation | public static final int | CALC_MODE_PACEDcalcMode value for paced interpolation. | public static final int | CALC_MODE_SPLINEcalcMode for spline interpolation. | public static final float | MIN_FLATNESS_SQUAREMinimum required flatness for keySpline approximations by
polylines. | String | toEnd value specification | String | fromStarting value specification. | String | byIntermediate value specification. | String | valuesComplete values specification list. | int | calcModeThe interpolation mode, one of the CALC_MODE_XYZ values. | int | actualCalcModeThe actual calcMode. For types which do not support interpolation,
the calcMode is forced to be discrete. | float[] | keyTimesKey times, to control the animation pace. May be null or a list of
values between 0 and 1. | float[] | keySplinesKey splines, used to control the speed of animation on a particular
time interval (interval pacing).
May be null or an array of arrays of 4 floating point values. | float[] | refSplinesrefSplines is an array of points which define a linear approximation of
the keySplines. refSplines is computed in the validate() method and
used in the curve() method. There is one float array per keySpline. | boolean | additiveControls whether the animation is additive or not. True maps to the
SVG 'sum' value and false maps to the SVG 'replace' value. | boolean | accumulateControls whether the animation has a cumulative behavior or not. This
covers the behavior over multiple iterations of the simple duration. | RefValues | refValuesThe RefValues corresponding to this <set> element. A <set>
element has a single segment with the same begin and end value. | RefValues | toRefValuesUsed, temporarily, to hold the refValues for the to attribute. | RefValues | fromRefValuesUsed, temporarily, to hold the refValues for the from attribute. | RefValues | byRefValuesUsed, temporarily, to hold the refValues for the by attribute. | RefValues | valuesRefValuesUsed, temporarily, to hold the refValues for the values attribute. | float[] | refTimesrefTimes holds key times for the animation.
refTimes is computed in the validate method.
refTimes holds the key times for the begining of each segment.
Therefore, there is as many refTimes as there are segments, in all cases. | float[] | sispA working buffer for mapping segment index and progress | boolean | isToAnimationSimple flag to check if we are dealing with a to-animation or
not. This flag is needed to control the addition and cumulative
behavior on to-animations, which is different than that on other
animations (e.g., a values or a from-to animation). |
Constructors Summary |
---|
public AbstractAnimate(DocumentNode ownerDocument, String localName)Builds a new Animate element that belongs to the given
document. This Animate will belong
to the DocumentNode 's time container.
super(ownerDocument, localName);
|
Methods Summary |
---|
float | calcMode(float p, int si)Intepolates the input value depending on the calcMode attribute
and, in the case of spline interpolation, depending on the
keySplines value.
switch (actualCalcMode) {
case CALC_MODE_DISCRETE:
return 0;
case CALC_MODE_LINEAR:
// Linear does not modify the current penetration
// because we are on an x = y line.
case CALC_MODE_PACED:
// Paced does not modify the current penetration
// because we have computed the time intervals
// so as to have constant velocity. There is no
// further interpolation in this method.
return p;
case CALC_MODE_SPLINE:
default:
return curve(p, refSplines[si]);
}
| protected void | computeRefTimes()Computes refTimes from the calcMode and keyTimes attributes. Validates
that the keyTimes attribute is compatible with the animate set up. This
may be overridden by subclasses (e.g., animateMotion), when there are
special rules for checking keyTimes compatiblity.
// c.1 Validate that keyTimes is compatible with the animation set-up
//
// if (calcMode == paced)
// refTimes such that change velocity is constant
// refTimes = getPacedTiming(refValues) // see follow on slides
//
// else if (keyTimes defined)
// refTimes = keyTimes
// if (calcMode == discreet)
// refTimes[n] = 1 // Accounts for the added nth value
// // in refValues
//
// else refTimes = f(refValues)
// the simple duration is divided into n-1 intervals,
// where n = refValues.length
// refTimes = defaultTiming(refValues)
if (actualCalcMode == CALC_MODE_PACED) {
// keyTimes are ignored
// See: http://www.w3.org/TR/smil20/smil-timing.html#animation-adef-keyTimes
refTimes = getPacedTiming(refValues);
} else if (keyTimes != null) {
// Check keyTimes is compatible with the animation specification.
//
// a) In all cases, the first keyTime must be zero.
//
// b) For non-discrete animations,
// b.1) the last keyTime must be one.
// b.2) there should be one more keyTimes than there are
// segments.
//
// c) For discrete animations,
// c.1) there should be as many keyTimes as there are segments.
//
if (/* a) */ keyTimes.length < 1 || keyTimes[0] != 0
||
/* b) */ (actualCalcMode != CALC_MODE_DISCRETE
&&
(/* b.1) */ keyTimes[keyTimes.length - 1] != 1
||
/* b.2) */ keyTimes.length !=
refValues.getSegments() + 1))
||
/* c) */ (actualCalcMode == CALC_MODE_DISCRETE
&&
/* c.1) */ keyTimes.length !=
refValues.getSegments())) {
throw animationError(idRef,
traitNamespace,
traitName,
targetElement.getNamespaceURI(),
targetElement.getLocalName(),
getId(),
getNamespaceURI(),
getLocalName(),
Messages.formatMessage(
Messages.ERROR_INVALID_ANIMATION_KEY_TIMES,
new Object[] {
getTrait(SVGConstants.SVG_KEY_TIMES_ATTRIBUTE)
}));
}
// If the calcMode is _not_ discrete, we trim the last '1'
// value.
if (actualCalcMode != CALC_MODE_DISCRETE) {
refTimes = new float[keyTimes.length - 1];
System.arraycopy(keyTimes, 0, refTimes, 0, refTimes.length);
} else {
refTimes = keyTimes;
}
// Validate that there are as many refTimes as there are refValues.
// We do the check using the first component, as we know there is at
// least one component no matter what value type we are dealing
// with.
if (refTimes.length != refValues.getSegments()) {
throw animationError(idRef,
traitNamespace,
traitName,
targetElement.getNamespaceURI(),
targetElement.getLocalName(),
getId(),
getNamespaceURI(),
getLocalName(),
Messages.formatMessage(
Messages.ERROR_INVALID_ANIMATION_KEY_TIMES,
new Object[] {
getTrait(SVGConstants.SVG_KEY_TIMES_ATTRIBUTE)
}));
}
} else {
// Simple case, give the same time length to each segment.
refTimes = getDefaultTiming(refValues);
}
| static float | curve(float p, float[][] polyline)Computes the curve value for the requested value on the specified
curves, defined by a polyline approximation.
The method assumes that the input polyline points are between 0 and 1 and
in increasing order along the x axis. The method considers the p value to
be on the polyline's x-axis and finds the two points between which it
lies and returns a linear approximation for that segment.
For degenerate cases (e.g., if the polyline x-axis values are not in the
[0, 1] interval or if p is not in the [0, 1] interval either), the method
returns 0 if the input penetration p is less than the first polyline
point. The method returns 1 if the input penetration is more than the
last polyline x-axis coordinate.
int i = 0;
for (; i < polyline.length; i++) {
if (p < polyline[i][0]) {
break;
}
}
if (i == polyline.length || i == 0) {
// Degenerate cases.
//
// This should never happen:
//
// Should not be polyline.length because the last entry in polyline
// should be '1' and the maximum input value for p should be '1'.
//
// Should not get 0 because the first entry should be zero and the
// input parameter should be greater or equal to zero.
//
return i / (float) polyline.length; // returns 0 for 0 and 1 for 1
}
float[] from = polyline[i - 1];
float[] to = polyline[i];
// Compute the progress between from and to:
//
// p = (1 - t) * from[0] + t.to[0]
// p = t * (t[0] - from[0]) + from[0]
// t = (p - from[0]) / (to[0] - from[0])
//
// So, the interpolated progress is:
//
// ip = (1 - t) * from[1] + t.to[1]
// ip = (1 - (p - from[0]) / (to[0] - from[0])) * from[1]
// + ((p - from[0]) / (to[0] - from[0])) * to[1]
// ip = ((to[0] - from[0] - p + from[0]) / (to[0] - from[0])) * from[1]
// + (to[1] * p - to[1] * from[0]) / (to[0] - from[0])
// ip = ((to[0] - p) / (to[0] - from[0]) * from[1]
// + (to[1] * p - to[1] * from[0]) / (to[0] - from[0])
// ip = (to[0] * from[1] - p * from[1]) / (to[0] - from[0])
// + (to[1] * p - to[1] * from[0]) / (to[0] - from[0])
// ip = (to[0] * from[1] - p * from[1] + to[1] * p - to[1] * from[0])
// / (to[0] - from[0])
// ip = (to[0] * from[1] - to[1] * from[0] + p * (to[1] - from[1]))
// / (to[0] - from[0])
return (to[0] * from[1] - to[1] * from[0] + p * (to[1] - from[1]))
/ (to[0] - from[0]);
| java.lang.Object[] | f(long t)This is the Animate element's animation function.
f(t) {
a. Compute the 'simple duration penetration' p
p = t / dur
where dur is the simple duration.
b. Compute the 'current time segment' i
refTimes[i] <= p < refTimes[i+1]
c. Compute the 'segment penetration' sp
sp = (p - refTimes[i])
/
(refTimes[i+1] - refTimes[i])
Note: 0 <= sp <= 1
d. Compute the 'interpolated interval
penetration' isp
isp = calcMode(sp)
Note: 0 <= isp <= 1
e. Compute the animated value:
v = refValues.compute(isp)
v has the same number of components as refValues.
}
// a. Compute the simple duration penetration p
// See: http://www.w3.org/TR/smil20/smil-timing.html#animation-animationNS-InterpolationAndIndefSimpleDur
float p = 0;
if (timedElementSupport.simpleDur != null
&&
timedElementSupport.simpleDur.isResolved()) {
p = t / (float) timedElementSupport.simpleDur.value;
}
// Iterate for each component.
int nc = refValues.getComponents();
float sp = 0;
int si = 0;
float endTime = 1;
float beginTime = 0;
int i = 0;
// b. Compute the 'current time segment' index si[ci]
// We iterate from 1 because the first value in refTimes
// is always 0.
for (i = 1; i < refTimes.length; i++) {
if (p < refTimes[i]) {
endTime = refTimes[i];
break;
}
beginTime = refTimes[i];
}
si = i - 1;
// c. Compute the segment penetration
if (endTime == beginTime) {
sp = 1;
} else {
sp = (p - beginTime) / (endTime - beginTime);
}
// d. Compute the 'interpolated segment penetration'
sp = calcMode(sp, si);
// At this point, we have computed:
// a. the array of time segment indices corresponding to 't'
// b. the array of penetration into the time segments.
//
// The following call lets the animate implementation map
// the time segment indices and the time segment penetration
// into refValues indices and penetration, in case these are
// different. Typically, these are the same, but they may be
// different, for example in the case of animateMotion with
// keyPoints.
sisp[0] = si;
sisp[1] = sp;
mapToSegmentProgress(si, sp, sisp);
si = (int) sisp[0];
sp = sisp[1];
// If we are dealing with a to-animation, we need to get the
// value for the single segment's start value.
if (isToAnimation) {
Object[] baseValue = baseVal.getBaseValue();
refValues.getSegment(0).setStart(baseValue);
return refValues.compute(si, sp);
}
// The reminder of this method is for non-additive animations
// Compute the simple function value
Object[] v = refValues.compute(si, sp);
// Account for cumulative behavior.
if (!isToAnimation && accumulate && timedElementSupport.curIter > 0) {
// Last simple time value, lv
Object[] lv =
refValues.getSegment(refValues.getSegments() - 1).getEnd();
Object[] r = traitAnim.multiply(lv, timedElementSupport.curIter);
v = traitAnim.sum(r, v);
}
// Now, account for additive behavior.
if (!additive) {
return v;
}
return traitAnim.sum(baseVal.getBaseValue(), v);
| float[] | getDefaultTiming(RefValues refValues)Computes refTimes so that each animation segment lasts the same length
of time.
int ns = refValues.getSegments();
float[] refTimes = new float[ns];
float startTime = 0;
float segLength = 1 / (float) ns;
for (int i = 0; i < ns; i++) {
refTimes[i] = startTime;
startTime += segLength;
}
return refTimes;
| float[] | getPacedTiming(RefValues refValues)Computes refTimes so that there is a paced speed on each values segment.
// a) Compute the refValues length, D
// D = sum(from 0 to n; seg(i).length());
//
// b) Compute the overall paced velocity, V
// V = D / dur;
//
// c) For each segment i, compute its refTime:
// refTime[j][i] = refTime[j][i-1] + (seg(i).length(j) / V(j)) / dur;
float D = refValues.getLength();
int ns = refValues.getSegments();
refTimes = new float[ns];
float prevRefTime = 0;
if (D > 0) {
// For each segment index si
for (int si = 1; si < ns; si++) {
refTimes[si] = prevRefTime + refValues.getLength(si -1) / D;
prevRefTime = refTimes[si];
}
} else {
// Segments are all of zero length, they should be evenly spaced
// in time.
float sl = 1f / ns;
for (int si = 1; si < ns; si++) {
refTimes[si] = si * sl;
}
}
return refTimes;
| public java.lang.String | getTraitImpl(java.lang.String name)
if (SVGConstants.SVG_TO_ATTRIBUTE == name) {
return to;
} else if (SVGConstants.SVG_FROM_ATTRIBUTE == name) {
return from;
} else if (SVGConstants.SVG_BY_ATTRIBUTE == name) {
return by;
} else if (SVGConstants.SVG_VALUES_ATTRIBUTE == name) {
return values;
} else if (SVGConstants.SVG_CALC_MODE_ATTRIBUTE == name) {
switch (calcMode) {
case CALC_MODE_DISCRETE:
return SVGConstants.SVG_DISCRETE_VALUE;
case CALC_MODE_LINEAR:
return SVGConstants.SVG_LINEAR_VALUE;
case CALC_MODE_PACED:
return SVGConstants.SVG_PACED_VALUE;
case CALC_MODE_SPLINE:
default:
return SVGConstants.SVG_SPLINE_VALUE;
}
} else if (SVGConstants.SVG_KEY_TIMES_ATTRIBUTE == name) {
return toStringTrait(keyTimes);
} else if (SVGConstants.SVG_KEY_SPLINES_ATTRIBUTE == name) {
return toStringTrait(keySplines);
} else if (SVGConstants.SVG_ADDITIVE_ATTRIBUTE == name) {
if (additive) {
return SVGConstants.SVG_SUM_VALUE;
}
return SVGConstants.SVG_REPLACE_VALUE;
} else if (SVGConstants.SVG_ACCUMULATE_ATTRIBUTE == name) {
if (accumulate) {
return SVGConstants.SVG_SUM_VALUE;
}
return SVGConstants.SVG_NONE_VALUE;
} else {
return super.getTraitImpl(name);
}
| static boolean | isFlat(float[][] curve, float flatness)
// Compute the square distance of P2 and P3 to the (P1, P4) line.
float dx = curve[3][0] - curve[0][0];
float dy = curve[3][1] - curve[0][1];
float div = dx * dx + dy * dy;
if (div == 0) {
return true;
}
float den = (dx * (curve[1][1] - curve[0][1])
-
dy * (curve[1][0] - curve[0][0]));
float dP2Sq = (den * den) / div;
den = (dx * (curve[2][1] - curve[0][1])
-
dy * (curve[2][0] - curve[0][0]));
float dP3Sq = (den * den) / div;
return (dP2Sq <= flatness && dP3Sq <= flatness);
| protected void | mapToSegmentProgress(int si, float sp, float[] sisp)The following call lets the animate implementation map
the time segment indices and the time segment penetration
into refValues indices and penetration, in case these are
different. Typically, these are the same, but they may be
different, for example in the case of animateMotion with
keyPoints.
| final void | selectRefValues()Computes the 'right' source for reference values, depending on the
precedence rules for the different values sources.
refValues = null;
selectRefValuesExtra();
if (refValues != null) {
return;
}
// Pseudo-code for accounting for precedence rules.
//
// If (values defined)
// // values anim
// refValues = values;
// else if (from defined)
// if (to defined)
// // from-to anim
// refValues = (from; to)
// else if (by defined)
// // from-by anim
// refValues = (from; from+by)
// else
// // no effect, there is nothing like a from anim
// else if (by defined)
// // by anim
// refValues = (0; by) and force additive anim
// else if (to defined)
// // to anim
// refValues = (baseVal; to)
// else
// // no effect
//
isToAnimation = false;
if (values != null) {
refValues = valuesRefValues;
} else if (from != null) {
refValues = fromRefValues;
if (to != null) {
// from-to animatin
refValues.getSegment(0).collapse(toRefValues.getSegment(0),
this);
} else if (by != null) {
// from-by animation
if (refValues.getSegment(0).isAdditive()) {
try {
refValues.getSegment(0).addToEnd(
byRefValues.getSegment(0).getStart());
} catch (IllegalArgumentException iae) {
// Incompatible by value
throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
Messages.formatMessage(
Messages.ERROR_INCOMPATIBLE_FROM_BY,
new Object[] {
traitName,
traitNamespace,
getLocalName(),
getNamespaceURI(),
from,
to
}));
}
} else {
throw new DOMException(
DOMException.NOT_SUPPORTED_ERR,
Messages.formatMessage(
Messages.ERROR_ATTRIBUTE_NOT_ADDITIVE_FROM_BY,
new Object[] {
traitName,
traitNamespace,
getLocalName(),
getNamespaceURI()
}));
}
} else {
throw animationError(idRef,
traitNamespace,
traitName,
targetElement.getNamespaceURI(),
targetElement.getLocalName(),
getId(),
getNamespaceURI(),
getLocalName(),
Messages.formatMessage(
Messages.ERROR_INVALID_ANIMATION_FROM_ANIM,
null));
}
} else if (by != null) {
// by animation
if (!byRefValues.getSegment(0).isAdditive()) {
throw new DOMException(
DOMException.NOT_SUPPORTED_ERR,
Messages.formatMessage(
Messages.ERROR_ATTRIBUTE_NOT_ADDITIVE_BY,
new Object[] {
traitName,
traitNamespace,
getLocalName(),
getNamespaceURI()
}));
}
refValues = byRefValues;
refValues.getSegment(0).setZeroStart();
additive = true;
} else if (to != null) {
// to animation
// We cannot compute the base value yet so we can only set the
// refValues to the toRefValues and delay computing the segment's
// begin until we actually have a baseValue. This will have to be
// updated on each computing cycle because the base value may change
// over time.
isToAnimation = true;
refValues = toRefValues;
} else {
// SMIL Animation
// specification:
// http://www.w3.org/TR/2001/REC-smil-animation-20010904/#AnimFuncValues
// "similarly, if none of the from, to, by or values attributes are
// specified, the animation will have no effect."
hasNoEffect = true;
}
| void | selectRefValuesExtra()Allows extensions to select a different source for refValues, in case
the extension has addition values sources that have higher precedence
than the default. For example, animateMotion has the path attribute and
the mpath children which have higher precedence.
| public void | setTraitImpl(java.lang.String name, java.lang.String value)
if (SVGConstants.SVG_TO_ATTRIBUTE == name) {
checkWriteLoading(name);
to = value;
} else if (SVGConstants.SVG_FROM_ATTRIBUTE == name) {
checkWriteLoading(name);
from = value;
} else if (SVGConstants.SVG_BY_ATTRIBUTE == name) {
checkWriteLoading(name);
by = value;
} else if (SVGConstants.SVG_VALUES_ATTRIBUTE == name) {
checkWriteLoading(name);
values = value;
} else if (SVGConstants.SVG_KEY_TIMES_ATTRIBUTE == name) {
checkWriteLoading(name);
// Generic float array parsing
keyTimes = parseFloatArrayTrait(name, value.replace(';", ',"));
// Now, check that the keyTimes values are from 0 to 1 and
// in increasing order. Note that this only performs the
// validations which can be made prior to a call to validate.
if (keyTimes[0] < 0 || keyTimes[0] > 1) {
throw illegalTraitValue(name, value);
}
for (int i = 1; i < keyTimes.length; i++) {
if (keyTimes[i] < 0 || keyTimes[i] > 1) {
throw illegalTraitValue(name, value);
}
if (keyTimes[i] < keyTimes[i - 1]) {
throw illegalTraitValue(name, value);
}
}
} else if (SVGConstants.SVG_KEY_SPLINES_ATTRIBUTE == name) {
checkWriteLoading(name);
SimpleTokenizer st = new SimpleTokenizer(value, ";");
int n = st.countTokens();
keySplines = new float[n][];
for (int i = 0; i < n; i++) {
String splineDef = st.nextToken();
keySplines[i] = parseFloatArrayTrait(name, splineDef);
if (keySplines[i].length != 4 ||
keySplines[i][0] < 0 ||
keySplines[i][0] > 1 ||
keySplines[i][1] < 0 ||
keySplines[i][1] > 1 ||
keySplines[i][2] < 0 ||
keySplines[i][2] > 1 ||
keySplines[i][3] < 0 ||
keySplines[i][3] > 1) {
throw illegalTraitValue(name, value);
}
}
} else if (SVGConstants.SVG_ADDITIVE_ATTRIBUTE == name) {
checkWriteLoading(name);
if (SVGConstants.SVG_REPLACE_VALUE.equals(value)) {
additive = false;
} else if (SVGConstants.SVG_SUM_VALUE.equals(value)) {
additive = true;
} else {
throw illegalTraitValue(name, value);
}
} else if (SVGConstants.SVG_ACCUMULATE_ATTRIBUTE == name) {
checkWriteLoading(name);
if (SVGConstants.SVG_NONE_VALUE.equals(value)) {
accumulate = false;
} else if (SVGConstants.SVG_SUM_VALUE.equals(value)) {
accumulate = true;
} else {
throw illegalTraitValue(name, value);
}
} else if (SVGConstants.SVG_CALC_MODE_ATTRIBUTE == name) {
if (SVGConstants.SVG_DISCRETE_VALUE.equals(value)) {
calcMode = CALC_MODE_DISCRETE;
} else if (SVGConstants.SVG_LINEAR_VALUE.equals(value)) {
calcMode = CALC_MODE_LINEAR;
} else if (SVGConstants.SVG_PACED_VALUE.equals(value)) {
calcMode = CALC_MODE_PACED;
} else if (SVGConstants.SVG_SPLINE_VALUE.equals(value)) {
calcMode = CALC_MODE_SPLINE;
} else {
throw illegalTraitValue(name, value);
}
} else {
super.setTraitImpl(name, value);
}
| boolean | supportsTrait(java.lang.String traitName)Supported traits: to, attributeName
if (SVGConstants.SVG_TO_ATTRIBUTE == traitName
||
SVGConstants.SVG_FROM_ATTRIBUTE == traitName
||
SVGConstants.SVG_BY_ATTRIBUTE == traitName
||
SVGConstants.SVG_VALUES_ATTRIBUTE == traitName
||
SVGConstants.SVG_CALC_MODE_ATTRIBUTE == traitName
||
SVGConstants.SVG_KEY_TIMES_ATTRIBUTE == traitName
||
SVGConstants.SVG_KEY_SPLINES_ATTRIBUTE == traitName
||
SVGConstants.SVG_ADDITIVE_ATTRIBUTE == traitName
||
SVGConstants.SVG_ACCUMULATE_ATTRIBUTE == traitName) {
return true;
} else {
return super.supportsTrait(traitName);
}
| static void | toRefSpline(float[][] curve, java.util.Vector segments)Converts the input spline curve (defined by its four control points)
into a polyline approximation (i.e., an array of points). If the curve
is flat enough (see the isFlat method), then the curve is
approximated to a line between its two end control points and the
last control point is added to the input segment vector.
If the curve is not flat enough, then it is sub-divided and the
subdivided curves are, recursively, tested for flatness.
The flatness used for flatness test is controlled by the
MIN_FLATNESS_SQUARE constant.
if (isFlat(curve, MIN_FLATNESS_SQUARE)) {
segments.addElement(new float[] {curve[3][0], curve[3][1]});
return;
}
float[][] lcurve = new float[4][2];
float[][] rcurve = new float[4][2];
// L1 = P1
lcurve[0] = curve[0];
// L2 = (P1 + P2) / 2
lcurve[1][0] = (curve[0][0] + curve[1][0]) / 2;
lcurve[1][1] = (curve[0][1] + curve[1][1]) / 2;
// H = (P2 + P3) / 2
float[] h = new float[2];
h[0] = (curve[1][0] + curve[2][0]) / 2;
h[1] = (curve[1][1] + curve[2][1]) / 2;
// L3 = (L2 + H) / 2
lcurve[2][0] = (lcurve[1][0] + h[0]) / 2;
lcurve[2][1] = (lcurve[1][1] + h[1]) / 2;
// R3 = (P3 + P4) / 2
rcurve[2][0] = (curve[2][0] + curve[3][0]) / 2;
rcurve[2][1] = (curve[2][1] + curve[3][1]) / 2;
// R2 = (H + R3) / 2
rcurve[1][0] = (h[0] + rcurve[2][0]) / 2;
rcurve[1][1] = (h[1] + rcurve[2][1]) / 2;
// L4 = R1 = (L3 + R2) / 2
lcurve[3][0] = (lcurve[2][0] + rcurve[1][0]) / 2;
lcurve[3][1] = (lcurve[2][1] + rcurve[1][1]) / 2;
rcurve[0] = lcurve[3];
// R4 = P4
rcurve[3] = curve[3];
toRefSpline(lcurve, segments);
toRefSpline(rcurve, segments);
| static float[][][] | toRefSplines(float[][] splines)Computes an array of points which do a linear approximation of the
input splines.
float[][][] rSplines = new float[splines.length][][];
float[][] splineDef = new float[4][2];
for (int i = 0; i < splines.length; i++) {
Vector v = new Vector();
v.addElement(new float[] {0, 0});
splineDef[0][0] = 0;
splineDef[0][1] = 0;
splineDef[1][0] = splines[i][0];
splineDef[1][1] = splines[i][1];
splineDef[2][0] = splines[i][2];
splineDef[2][1] = splines[i][3];
splineDef[3][0] = 1;
splineDef[3][1] = 1;
toRefSpline(splineDef, v);
float[][] polyline = new float[v.size()][];
v.copyInto(polyline);
rSplines[i] = polyline;
}
return rSplines;
| void | validate()Validating an Animate consists in:
a) Setting its target element. If there was no idRef, then targetElement
is still null and will be positioned to the parent node.
b) Validating the from, to, by and values traits with the targetElement,
using the target trait name, namespace and value.
c) Validating the keyTimes and the keySplines trait values to check they
are compatible with the values specification.
// =====================================================================
// a) Set the target element.
if (targetElement == null) {
targetElement = (ElementNode) parent;
}
// =====================================================================
// Check that the traitName attribute was specified.
if (traitName == null) {
throw illegalTraitValue(SVGConstants.SVG_ATTRIBUTE_NAME_ATTRIBUTE,
traitName);
}
// =====================================================================
// b) Validate the to/from/by/values traits with the target element.
// Note that traitName should _never_ be null when validate() is
// invoked. It is either required (e.g., for Animate) or fixed (e.g.,
// for AnimateMotion. If, for example in some unit test configutations,
// this method is called without a specified traitName, the method
// generates a NullPointerException.
traitAnim = targetElement.getSafeTraitAnimNS(traitNamespace, traitName);
// If the traitAnim type does not support interpolation, force the
// calcMode to be discrete.
if (traitAnim.supportsInterpolation()) {
actualCalcMode = calcMode;
} else {
actualCalcMode = CALC_MODE_DISCRETE;
}
validateValues();
// Now, apply precedence rules to compute the actual RefValues.
selectRefValues();
// If the animation has no effect, stop here.
if (hasNoEffect) {
return;
}
// =====================================================================
// If we are dealing with discrete timing, we need to add a last time
// segment so that we implement the desired behavior for discrete
// timing. With a single interval with start/end values, only the start
// value will be shown. If we add a new interval with the end value,
// then the end value will be shown for the last time interval.
if (actualCalcMode == CALC_MODE_DISCRETE) {
refValues.makeDiscrete();
}
// Initialize the refValues
refValues.initialize();
// =====================================================================
// c. Validate that keyTimes and keySplines trait values are compatible
// with the values specification.
computeRefTimes();
// c.1 Validate that keySplines is compatible with the animation set-up
//
// See:
// http://www.w3.org/TR/2001/REC-smil20-20010807/smil20.html#animation-animationNS-InterpolationKeysplines
//
// The attribute is ignored unless the CALC_MODE is spline. For spline
// interpolation, there must be one fewer sets of control points in the
// keySplines attribute than there are keyTimes.
//
if (actualCalcMode == CALC_MODE_SPLINE) {
if (keySplines == null
||
refTimes.length != keySplines.length) {
throw animationError(idRef,
traitNamespace,
traitName,
targetElement.getNamespaceURI(),
targetElement.getLocalName(),
getId(),
getNamespaceURI(),
getLocalName(),
Messages.formatMessage(
Messages.ERROR_INVALID_ANIMATION_KEY_SPLINES,
new Object[] {
getTrait(
SVGConstants.SVG_KEY_SPLINES_ATTRIBUTE),
getTrait(SVGConstants.SVG_KEY_TIMES_ATTRIBUTE)
}));
}
// Turn the keySplines attribute into a set of (x, y) points
// which can be interpolated between.
refSplines = toRefSplines(keySplines);
}
| final void | validateValues()Validates the different so-called values attributes, such as the
to/from/by and values attributes. Derived classes may have more 'values'
attributes, and may override this method to validate these additional
attributes.
// b.1) Validate the to traits value
toRefValues = null;
if (to != null) {
toRefValues = traitAnim.toRefValues(this,
new String[] {to},
null,
SVGConstants.SVG_TO_ATTRIBUTE);
}
// b.2) Validate the by traits value
byRefValues = null;
if (by != null) {
byRefValues = traitAnim.toRefValues(this,
new String[] {by},
null,
SVGConstants.SVG_BY_ATTRIBUTE);
}
// b.3) Validate the from traits value
fromRefValues = null;
if (from != null) {
fromRefValues = traitAnim.toRefValues(this,
new String[] {from},
null,
SVGConstants.SVG_FROM_ATTRIBUTE);
}
// b.4) Validate the values trait value
valuesRefValues = null;
if (values != null) {
String[] v = parseStringArrayTrait(
SVGConstants.SVG_VALUES_ATTRIBUTE,
values,
";");
valuesRefValues = traitAnim.toRefValues(this,
v,
null,
SVGConstants.SVG_VALUES_ATTRIBUTE);
}
validateValuesExtra();
| void | validateValuesExtra()Allows extension classes to validate addition values sources.
|
|