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/v8/ExceptionState.h"
26 #include "bindings/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"));
45 SVGMarkerOrientEnumeration::SVGMarkerOrientEnumeration(SVGAngle* angle)
46 : SVGEnumeration<SVGMarkerOrientType>(SVGMarkerOrientAngle)
51 SVGMarkerOrientEnumeration::~SVGMarkerOrientEnumeration()
55 void SVGMarkerOrientEnumeration::notifyChange()
58 m_angle->orientTypeChanged();
61 void SVGMarkerOrientEnumeration::add(PassRefPtr<SVGPropertyBase>, SVGElement*)
63 // SVGMarkerOrientEnumeration is only animated via SVGAngle
67 void SVGMarkerOrientEnumeration::calculateAnimatedValue(SVGAnimationElement*, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
69 // SVGMarkerOrientEnumeration is only animated via SVGAngle
73 float SVGMarkerOrientEnumeration::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement* contextElement)
75 // SVGMarkerOrientEnumeration is only animated via SVGAngle
81 : SVGPropertyBase(classType())
82 , m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
83 , m_valueInSpecifiedUnits(0)
84 , m_orientType(SVGMarkerOrientEnumeration::create(this))
88 SVGAngle::SVGAngle(SVGAngleType unitType, float valueInSpecifiedUnits, SVGMarkerOrientType orientType)
89 : SVGPropertyBase(classType())
90 , m_unitType(unitType)
91 , m_valueInSpecifiedUnits(valueInSpecifiedUnits)
92 , m_orientType(SVGMarkerOrientEnumeration::create(this))
94 m_orientType->setEnumValue(orientType);
101 PassRefPtr<SVGAngle> SVGAngle::clone() const
103 return adoptRef(new SVGAngle(m_unitType, m_valueInSpecifiedUnits, m_orientType->enumValue()));
106 PassRefPtr<SVGPropertyBase> SVGAngle::cloneForAnimation(const String& value) const
108 RefPtr<SVGAngle> point = create();
109 point->setValueAsString(value, IGNORE_EXCEPTION);
110 return point.release();
113 float SVGAngle::value() const
115 switch (m_unitType) {
116 case SVG_ANGLETYPE_GRAD:
117 return grad2deg(m_valueInSpecifiedUnits);
118 case SVG_ANGLETYPE_RAD:
119 return rad2deg(m_valueInSpecifiedUnits);
120 case SVG_ANGLETYPE_UNSPECIFIED:
121 case SVG_ANGLETYPE_UNKNOWN:
122 case SVG_ANGLETYPE_DEG:
123 return m_valueInSpecifiedUnits;
126 ASSERT_NOT_REACHED();
130 void SVGAngle::setValue(float value)
132 switch (m_unitType) {
133 case SVG_ANGLETYPE_GRAD:
134 m_valueInSpecifiedUnits = deg2grad(value);
136 case SVG_ANGLETYPE_RAD:
137 m_valueInSpecifiedUnits = deg2rad(value);
139 case SVG_ANGLETYPE_UNSPECIFIED:
140 case SVG_ANGLETYPE_UNKNOWN:
141 case SVG_ANGLETYPE_DEG:
142 m_valueInSpecifiedUnits = value;
145 m_orientType->setEnumValue(SVGMarkerOrientAngle);
148 template<typename CharType>
149 static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const CharType* end)
151 // If there's no unit given, the angle type is unspecified.
153 return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
155 const CharType firstChar = *ptr;
157 // If the unit contains only one character, the angle type is unknown.
160 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
162 const CharType secondChar = *ptr;
164 // If the unit contains only two characters, the angle type is unknown.
167 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
169 const CharType thirdChar = *ptr;
170 if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g')
171 return SVGAngle::SVG_ANGLETYPE_DEG;
172 if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd')
173 return SVGAngle::SVG_ANGLETYPE_RAD;
175 // If the unit contains three characters, but is not deg or rad, then it's unknown.
178 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
180 const CharType fourthChar = *ptr;
182 if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
183 return SVGAngle::SVG_ANGLETYPE_GRAD;
185 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
188 String SVGAngle::valueAsString() const
190 switch (m_unitType) {
191 case SVG_ANGLETYPE_DEG: {
192 DEFINE_STATIC_LOCAL(String, degString, ("deg"));
193 return String::number(m_valueInSpecifiedUnits) + degString;
195 case SVG_ANGLETYPE_RAD: {
196 DEFINE_STATIC_LOCAL(String, radString, ("rad"));
197 return String::number(m_valueInSpecifiedUnits) + radString;
199 case SVG_ANGLETYPE_GRAD: {
200 DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
201 return String::number(m_valueInSpecifiedUnits) + gradString;
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, false))
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);
241 float valueInSpecifiedUnits = 0;
242 SVGAngleType unitType = SVG_ANGLETYPE_UNKNOWN;
244 bool success = value.is8Bit() ? parseValue<LChar>(value, valueInSpecifiedUnits, unitType)
245 : parseValue<UChar>(value, valueInSpecifiedUnits, unitType);
247 exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid.");
251 m_orientType->setEnumValue(SVGMarkerOrientAngle);
252 m_unitType = unitType;
253 m_valueInSpecifiedUnits = valueInSpecifiedUnits;
256 void SVGAngle::newValueSpecifiedUnits(SVGAngleType unitType, float valueInSpecifiedUnits)
258 m_orientType->setEnumValue(SVGMarkerOrientAngle);
259 m_unitType = unitType;
260 m_valueInSpecifiedUnits = valueInSpecifiedUnits;
263 void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& exceptionState)
265 if (m_unitType == SVG_ANGLETYPE_UNKNOWN) {
266 exceptionState.throwDOMException(NotSupportedError, "Cannot convert from unknown or invalid units.");
270 if (unitType == m_unitType)
273 switch (m_unitType) {
274 case SVG_ANGLETYPE_RAD:
276 case SVG_ANGLETYPE_GRAD:
277 m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
279 case SVG_ANGLETYPE_UNSPECIFIED:
280 case SVG_ANGLETYPE_DEG:
281 m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
283 case SVG_ANGLETYPE_RAD:
284 case SVG_ANGLETYPE_UNKNOWN:
285 ASSERT_NOT_REACHED();
289 case SVG_ANGLETYPE_GRAD:
291 case SVG_ANGLETYPE_RAD:
292 m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
294 case SVG_ANGLETYPE_UNSPECIFIED:
295 case SVG_ANGLETYPE_DEG:
296 m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
298 case SVG_ANGLETYPE_GRAD:
299 case SVG_ANGLETYPE_UNKNOWN:
300 ASSERT_NOT_REACHED();
304 case SVG_ANGLETYPE_UNSPECIFIED:
305 // Spec: For angles, a unitless value is treated the same as if degrees were specified.
306 case SVG_ANGLETYPE_DEG:
308 case SVG_ANGLETYPE_RAD:
309 m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
311 case SVG_ANGLETYPE_GRAD:
312 m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
314 case SVG_ANGLETYPE_UNSPECIFIED:
316 case SVG_ANGLETYPE_DEG:
317 case SVG_ANGLETYPE_UNKNOWN:
318 ASSERT_NOT_REACHED();
322 case SVG_ANGLETYPE_UNKNOWN:
323 ASSERT_NOT_REACHED();
327 m_unitType = unitType;
328 m_orientType->setEnumValue(SVGMarkerOrientAngle);
331 void SVGAngle::add(PassRefPtr<SVGPropertyBase> other, SVGElement*)
333 RefPtr<SVGAngle> otherAngle = toSVGAngle(other);
335 // Only respect by animations, if from and by are both specified in angles (and not eg. 'auto').
336 if (orientType()->enumValue() != SVGMarkerOrientAngle || otherAngle->orientType()->enumValue() != SVGMarkerOrientAngle)
339 setValue(value() + otherAngle->value());
342 void SVGAngle::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDuration, SVGElement*)
344 ASSERT(animationElement);
345 bool isToAnimation = animationElement->animationMode() == ToAnimation;
347 RefPtr<SVGAngle> fromAngle = isToAnimation ? this : toSVGAngle(from);
348 RefPtr<SVGAngle> toAngle = toSVGAngle(to);
349 RefPtr<SVGAngle> toAtEndOfDurationAngle = toSVGAngle(toAtEndOfDuration);
351 SVGMarkerOrientType fromOrientType = fromAngle->orientType()->enumValue();
352 SVGMarkerOrientType toOrientType = toAngle->orientType()->enumValue();
354 if (fromOrientType != toOrientType) {
355 // Animating from eg. auto to 90deg, or auto to 90deg.
356 if (fromOrientType == SVGMarkerOrientAngle) {
357 // Animating from an angle value to eg. 'auto' - this disabled additive as 'auto' is a keyword..
358 if (toOrientType == SVGMarkerOrientAuto) {
359 if (percentage < 0.5f) {
360 newValueSpecifiedUnits(fromAngle->unitType(), fromAngle->valueInSpecifiedUnits());
363 orientType()->setEnumValue(SVGMarkerOrientAuto);
366 m_valueInSpecifiedUnits = 0;
367 orientType()->setEnumValue(SVGMarkerOrientUnknown);
372 // From 'auto' to 'auto'.
373 if (fromOrientType == SVGMarkerOrientAuto) {
374 m_valueInSpecifiedUnits = 0;
375 orientType()->setEnumValue(SVGMarkerOrientAuto);
379 // If the enumeration value is not angle or auto, its unknown.
380 if (fromOrientType != SVGMarkerOrientAngle) {
381 m_valueInSpecifiedUnits = 0;
382 orientType()->setEnumValue(SVGMarkerOrientUnknown);
386 // Regular from angle to angle animation, with all features like additive etc.
387 float animatedValue = value();
388 animationElement->animateAdditiveNumber(percentage, repeatCount, fromAngle->value(), toAngle->value(), toAtEndOfDurationAngle->value(), animatedValue);
389 orientType()->setEnumValue(SVGMarkerOrientAngle);
390 setValue(animatedValue);
393 float SVGAngle::calculateDistance(PassRefPtr<SVGPropertyBase> other, SVGElement*)
395 return fabsf(value() - toSVGAngle(other)->value());
398 void SVGAngle::orientTypeChanged()
400 if (orientType()->enumValue() == SVGMarkerOrientAuto) {
401 m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
402 m_valueInSpecifiedUnits = 0;