2 * Copyright (C) 2004, 2005, 2007, 2008 Nikolas Zimmermann <zimmermann@kde.org>
3 * Copyright (C) 2004, 2005, 2006 Rob Buis <buis@kde.org>
4 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
23 #include "core/svg/SVGAngle.h"
25 #include "bindings/core/v8/ExceptionState.h"
26 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
27 #include "core/dom/ExceptionCode.h"
28 #include "core/svg/SVGAnimationElement.h"
29 #include "core/svg/SVGParserUtilities.h"
30 #include "wtf/MathExtras.h"
31 #include "wtf/text/WTFString.h"
35 template<> const SVGEnumerationStringEntries& getStaticStringEntries<SVGMarkerOrientType>()
37 DEFINE_STATIC_LOCAL(SVGEnumerationStringEntries, entries, ());
38 if (entries.isEmpty()) {
39 entries.append(std::make_pair(SVGMarkerOrientAuto, "auto"));
40 entries.append(std::make_pair(SVGMarkerOrientAngle, "angle"));
41 entries.append(std::make_pair(SVGMarkerOrientAutoStartReverse, "auto-start-reverse"));
46 template<> unsigned short getMaxExposedEnumValue<SVGMarkerOrientType>()
48 return SVGMarkerOrientAngle;
51 SVGMarkerOrientEnumeration::SVGMarkerOrientEnumeration(SVGAngle* angle)
52 : SVGEnumeration<SVGMarkerOrientType>(SVGMarkerOrientAngle)
57 SVGMarkerOrientEnumeration::~SVGMarkerOrientEnumeration()
61 void SVGMarkerOrientEnumeration::notifyChange()
64 m_angle->orientTypeChanged();
67 void SVGMarkerOrientEnumeration::add(PassRefPtrWillBeRawPtr<SVGPropertyBase>, SVGElement*)
69 // SVGMarkerOrientEnumeration is only animated via SVGAngle
73 void SVGMarkerOrientEnumeration::calculateAnimatedValue(SVGAnimationElement*, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
75 // SVGMarkerOrientEnumeration is only animated via SVGAngle
79 float SVGMarkerOrientEnumeration::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement* contextElement)
81 // SVGMarkerOrientEnumeration is only animated via SVGAngle
87 : m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
88 , m_valueInSpecifiedUnits(0)
89 , m_orientType(SVGMarkerOrientEnumeration::create(this))
93 SVGAngle::SVGAngle(SVGAngleType unitType, float valueInSpecifiedUnits, SVGMarkerOrientType orientType)
94 : m_unitType(unitType)
95 , m_valueInSpecifiedUnits(valueInSpecifiedUnits)
96 , m_orientType(SVGMarkerOrientEnumeration::create(this))
98 m_orientType->setEnumValue(orientType);
101 SVGAngle::~SVGAngle()
105 PassRefPtr<SVGAngle> SVGAngle::clone() const
107 return adoptRef(new SVGAngle(m_unitType, m_valueInSpecifiedUnits, m_orientType->enumValue()));
110 float SVGAngle::value() const
112 switch (m_unitType) {
113 case SVG_ANGLETYPE_GRAD:
114 return grad2deg(m_valueInSpecifiedUnits);
115 case SVG_ANGLETYPE_RAD:
116 return rad2deg(m_valueInSpecifiedUnits);
117 case SVG_ANGLETYPE_TURN:
118 return turn2deg(m_valueInSpecifiedUnits);
119 case SVG_ANGLETYPE_UNSPECIFIED:
120 case SVG_ANGLETYPE_UNKNOWN:
121 case SVG_ANGLETYPE_DEG:
122 return m_valueInSpecifiedUnits;
125 ASSERT_NOT_REACHED();
129 void SVGAngle::setValue(float value)
131 switch (m_unitType) {
132 case SVG_ANGLETYPE_GRAD:
133 m_valueInSpecifiedUnits = deg2grad(value);
135 case SVG_ANGLETYPE_RAD:
136 m_valueInSpecifiedUnits = deg2rad(value);
138 case SVG_ANGLETYPE_TURN:
139 m_valueInSpecifiedUnits = deg2turn(value);
141 case SVG_ANGLETYPE_UNSPECIFIED:
142 case SVG_ANGLETYPE_UNKNOWN:
143 case SVG_ANGLETYPE_DEG:
144 m_valueInSpecifiedUnits = value;
147 m_orientType->setEnumValue(SVGMarkerOrientAngle);
150 template<typename CharType>
151 static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const CharType* end)
153 // If there's no unit given, the angle type is unspecified.
155 return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
157 SVGAngle::SVGAngleType type = SVGAngle::SVG_ANGLETYPE_UNKNOWN;
158 const CharType firstChar = *ptr++;
160 if (isHTMLSpace<CharType>(firstChar)) {
161 type = SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
162 } else if (end - ptr >= 2) {
163 const CharType secondChar = *ptr++;
164 const CharType thirdChar = *ptr++;
165 if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g') {
166 type = SVGAngle::SVG_ANGLETYPE_DEG;
167 } else if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd') {
168 type = SVGAngle::SVG_ANGLETYPE_RAD;
169 } else if (ptr != end) {
170 const CharType fourthChar = *ptr++;
171 if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
172 type = SVGAngle::SVG_ANGLETYPE_GRAD;
173 else if (firstChar == 't' && secondChar == 'u' && thirdChar == 'r' && fourthChar == 'n')
174 type = SVGAngle::SVG_ANGLETYPE_TURN;
178 if (!skipOptionalSVGSpaces(ptr, end))
181 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
184 String SVGAngle::valueAsString() const
186 switch (m_unitType) {
187 case SVG_ANGLETYPE_DEG: {
188 DEFINE_STATIC_LOCAL(String, degString, ("deg"));
189 return String::number(m_valueInSpecifiedUnits) + degString;
191 case SVG_ANGLETYPE_RAD: {
192 DEFINE_STATIC_LOCAL(String, radString, ("rad"));
193 return String::number(m_valueInSpecifiedUnits) + radString;
195 case SVG_ANGLETYPE_GRAD: {
196 DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
197 return String::number(m_valueInSpecifiedUnits) + gradString;
199 case SVG_ANGLETYPE_TURN: {
200 DEFINE_STATIC_LOCAL(String, turnString, ("turn"));
201 return String::number(m_valueInSpecifiedUnits) + turnString;
203 case SVG_ANGLETYPE_UNSPECIFIED:
204 case SVG_ANGLETYPE_UNKNOWN:
205 return String::number(m_valueInSpecifiedUnits);
208 ASSERT_NOT_REACHED();
212 template<typename CharType>
213 static bool parseValue(const String& value, float& valueInSpecifiedUnits, SVGAngle::SVGAngleType& unitType)
215 const CharType* ptr = value.getCharacters<CharType>();
216 const CharType* end = ptr + value.length();
218 if (!parseNumber(ptr, end, valueInSpecifiedUnits, AllowLeadingWhitespace))
221 unitType = stringToAngleType(ptr, end);
222 if (unitType == SVGAngle::SVG_ANGLETYPE_UNKNOWN)
228 void SVGAngle::setValueAsString(const String& value, ExceptionState& exceptionState)
230 if (value.isEmpty()) {
231 newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
235 if (value == "auto") {
236 newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
237 m_orientType->setEnumValue(SVGMarkerOrientAuto);
240 if (value == "auto-start-reverse") {
241 newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
242 m_orientType->setEnumValue(SVGMarkerOrientAutoStartReverse);
246 float valueInSpecifiedUnits = 0;
247 SVGAngleType unitType = SVG_ANGLETYPE_UNKNOWN;
249 bool success = value.is8Bit() ? parseValue<LChar>(value, valueInSpecifiedUnits, unitType)
250 : parseValue<UChar>(value, valueInSpecifiedUnits, unitType);
252 exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid.");
256 m_orientType->setEnumValue(SVGMarkerOrientAngle);
257 m_unitType = unitType;
258 m_valueInSpecifiedUnits = valueInSpecifiedUnits;
261 void SVGAngle::newValueSpecifiedUnits(SVGAngleType unitType, float valueInSpecifiedUnits)
263 m_orientType->setEnumValue(SVGMarkerOrientAngle);
264 m_unitType = unitType;
265 m_valueInSpecifiedUnits = valueInSpecifiedUnits;
268 void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& exceptionState)
270 if (m_unitType == SVG_ANGLETYPE_UNKNOWN) {
271 exceptionState.throwDOMException(NotSupportedError, "Cannot convert from unknown or invalid units.");
275 if (unitType == m_unitType)
278 switch (m_unitType) {
279 case SVG_ANGLETYPE_TURN:
281 case SVG_ANGLETYPE_GRAD:
282 m_valueInSpecifiedUnits = turn2grad(m_valueInSpecifiedUnits);
284 case SVG_ANGLETYPE_UNSPECIFIED:
285 case SVG_ANGLETYPE_DEG:
286 m_valueInSpecifiedUnits = turn2deg(m_valueInSpecifiedUnits);
288 case SVG_ANGLETYPE_RAD:
289 m_valueInSpecifiedUnits = deg2rad(turn2deg(m_valueInSpecifiedUnits));
291 case SVG_ANGLETYPE_TURN:
292 case SVG_ANGLETYPE_UNKNOWN:
293 ASSERT_NOT_REACHED();
297 case SVG_ANGLETYPE_RAD:
299 case SVG_ANGLETYPE_GRAD:
300 m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
302 case SVG_ANGLETYPE_UNSPECIFIED:
303 case SVG_ANGLETYPE_DEG:
304 m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
306 case SVG_ANGLETYPE_TURN:
307 m_valueInSpecifiedUnits = deg2turn(rad2deg(m_valueInSpecifiedUnits));
309 case SVG_ANGLETYPE_RAD:
310 case SVG_ANGLETYPE_UNKNOWN:
311 ASSERT_NOT_REACHED();
315 case SVG_ANGLETYPE_GRAD:
317 case SVG_ANGLETYPE_RAD:
318 m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
320 case SVG_ANGLETYPE_UNSPECIFIED:
321 case SVG_ANGLETYPE_DEG:
322 m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
324 case SVG_ANGLETYPE_TURN:
325 m_valueInSpecifiedUnits = grad2turn(m_valueInSpecifiedUnits);
327 case SVG_ANGLETYPE_GRAD:
328 case SVG_ANGLETYPE_UNKNOWN:
329 ASSERT_NOT_REACHED();
333 case SVG_ANGLETYPE_UNSPECIFIED:
334 // Spec: For angles, a unitless value is treated the same as if degrees were specified.
335 case SVG_ANGLETYPE_DEG:
337 case SVG_ANGLETYPE_RAD:
338 m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
340 case SVG_ANGLETYPE_GRAD:
341 m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
343 case SVG_ANGLETYPE_TURN:
344 m_valueInSpecifiedUnits = deg2turn(m_valueInSpecifiedUnits);
346 case SVG_ANGLETYPE_UNSPECIFIED:
347 case SVG_ANGLETYPE_DEG:
349 case SVG_ANGLETYPE_UNKNOWN:
350 ASSERT_NOT_REACHED();
354 case SVG_ANGLETYPE_UNKNOWN:
355 ASSERT_NOT_REACHED();
359 m_unitType = unitType;
360 m_orientType->setEnumValue(SVGMarkerOrientAngle);
363 void SVGAngle::add(PassRefPtrWillBeRawPtr<SVGPropertyBase> other, SVGElement*)
365 RefPtr<SVGAngle> otherAngle = toSVGAngle(other);
367 // Only respect by animations, if from and by are both specified in angles (and not eg. 'auto').
368 if (orientType()->enumValue() != SVGMarkerOrientAngle || otherAngle->orientType()->enumValue() != SVGMarkerOrientAngle)
371 setValue(value() + otherAngle->value());
374 void SVGAngle::assign(const SVGAngle& other)
376 SVGMarkerOrientType otherOrientType = other.orientType()->enumValue();
377 if (otherOrientType == SVGMarkerOrientAngle)
378 newValueSpecifiedUnits(other.unitType(), other.valueInSpecifiedUnits());
380 m_orientType->setEnumValue(otherOrientType);
383 void SVGAngle::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDuration, SVGElement*)
385 ASSERT(animationElement);
386 bool isToAnimation = animationElement->animationMode() == ToAnimation;
388 RefPtr<SVGAngle> fromAngle = isToAnimation ? this : toSVGAngle(from);
389 RefPtr<SVGAngle> toAngle = toSVGAngle(to);
390 SVGMarkerOrientType fromOrientType = fromAngle->orientType()->enumValue();
391 SVGMarkerOrientType toOrientType = toAngle->orientType()->enumValue();
393 if (fromOrientType != toOrientType) {
394 // Fall back to discrete animation.
395 assign(percentage < 0.5f ? *fromAngle : *toAngle);
399 switch (fromOrientType) {
400 // From 'auto' to 'auto', or 'auto-start-reverse' to 'auto-start-reverse'
401 case SVGMarkerOrientAuto:
402 case SVGMarkerOrientAutoStartReverse:
403 orientType()->setEnumValue(fromOrientType);
406 // Regular from angle to angle animation, with all features like additive etc.
407 case SVGMarkerOrientAngle:
409 float animatedValue = value();
410 RefPtr<SVGAngle> toAtEndOfDurationAngle = toSVGAngle(toAtEndOfDuration);
412 animationElement->animateAdditiveNumber(percentage, repeatCount, fromAngle->value(), toAngle->value(), toAtEndOfDurationAngle->value(), animatedValue);
413 orientType()->setEnumValue(SVGMarkerOrientAngle);
414 setValue(animatedValue);
418 // If the enumeration value is not angle or auto, its unknown.
420 m_valueInSpecifiedUnits = 0;
421 orientType()->setEnumValue(SVGMarkerOrientUnknown);
426 float SVGAngle::calculateDistance(PassRefPtr<SVGPropertyBase> other, SVGElement*)
428 return fabsf(value() - toSVGAngle(other)->value());
431 void SVGAngle::orientTypeChanged()
433 if (orientType()->enumValue() == SVGMarkerOrientAuto || orientType()->enumValue() == SVGMarkerOrientAutoStartReverse) {
434 m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
435 m_valueInSpecifiedUnits = 0;