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(SVGMarkerOrientUnknown, emptyString()));
40 entries.append(std::make_pair(SVGMarkerOrientAuto, "auto"));
41 entries.append(std::make_pair(SVGMarkerOrientAngle, "angle"));
46 SVGMarkerOrientEnumeration::SVGMarkerOrientEnumeration(SVGAngle* angle)
47 : SVGEnumeration<SVGMarkerOrientType>(SVGMarkerOrientAngle)
52 SVGMarkerOrientEnumeration::~SVGMarkerOrientEnumeration()
56 void SVGMarkerOrientEnumeration::notifyChange()
59 m_angle->orientTypeChanged();
62 void SVGMarkerOrientEnumeration::add(PassRefPtr<SVGPropertyBase>, SVGElement*)
64 // SVGMarkerOrientEnumeration is only animated via SVGAngle
68 void SVGMarkerOrientEnumeration::calculateAnimatedValue(SVGAnimationElement*, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDurationValue, SVGElement* contextElement)
70 // SVGMarkerOrientEnumeration is only animated via SVGAngle
74 float SVGMarkerOrientEnumeration::calculateDistance(PassRefPtr<SVGPropertyBase> to, SVGElement* contextElement)
76 // SVGMarkerOrientEnumeration is only animated via SVGAngle
82 : SVGPropertyBase(classType())
83 , m_unitType(SVG_ANGLETYPE_UNSPECIFIED)
84 , m_valueInSpecifiedUnits(0)
85 , m_orientType(SVGMarkerOrientEnumeration::create(this))
89 SVGAngle::SVGAngle(SVGAngleType unitType, float valueInSpecifiedUnits, SVGMarkerOrientType orientType)
90 : SVGPropertyBase(classType())
91 , m_unitType(unitType)
92 , m_valueInSpecifiedUnits(valueInSpecifiedUnits)
93 , m_orientType(SVGMarkerOrientEnumeration::create(this))
95 m_orientType->setEnumValue(orientType);
102 PassRefPtr<SVGAngle> SVGAngle::clone() const
104 return adoptRef(new SVGAngle(m_unitType, m_valueInSpecifiedUnits, m_orientType->enumValue()));
107 PassRefPtr<SVGPropertyBase> SVGAngle::cloneForAnimation(const String& value) const
109 RefPtr<SVGAngle> point = create();
110 point->setValueAsString(value, IGNORE_EXCEPTION);
111 return point.release();
114 float SVGAngle::value() const
116 switch (m_unitType) {
117 case SVG_ANGLETYPE_GRAD:
118 return grad2deg(m_valueInSpecifiedUnits);
119 case SVG_ANGLETYPE_RAD:
120 return rad2deg(m_valueInSpecifiedUnits);
121 case SVG_ANGLETYPE_UNSPECIFIED:
122 case SVG_ANGLETYPE_UNKNOWN:
123 case SVG_ANGLETYPE_DEG:
124 return m_valueInSpecifiedUnits;
127 ASSERT_NOT_REACHED();
131 void SVGAngle::setValue(float value)
133 switch (m_unitType) {
134 case SVG_ANGLETYPE_GRAD:
135 m_valueInSpecifiedUnits = deg2grad(value);
137 case SVG_ANGLETYPE_RAD:
138 m_valueInSpecifiedUnits = deg2rad(value);
140 case SVG_ANGLETYPE_UNSPECIFIED:
141 case SVG_ANGLETYPE_UNKNOWN:
142 case SVG_ANGLETYPE_DEG:
143 m_valueInSpecifiedUnits = value;
146 m_orientType->setEnumValue(SVGMarkerOrientAngle);
149 template<typename CharType>
150 static SVGAngle::SVGAngleType stringToAngleType(const CharType*& ptr, const CharType* end)
152 // If there's no unit given, the angle type is unspecified.
154 return SVGAngle::SVG_ANGLETYPE_UNSPECIFIED;
156 const CharType firstChar = *ptr;
158 // If the unit contains only one character, the angle type is unknown.
161 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
163 const CharType secondChar = *ptr;
165 // If the unit contains only two characters, the angle type is unknown.
168 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
170 const CharType thirdChar = *ptr;
171 if (firstChar == 'd' && secondChar == 'e' && thirdChar == 'g')
172 return SVGAngle::SVG_ANGLETYPE_DEG;
173 if (firstChar == 'r' && secondChar == 'a' && thirdChar == 'd')
174 return SVGAngle::SVG_ANGLETYPE_RAD;
176 // If the unit contains three characters, but is not deg or rad, then it's unknown.
179 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
181 const CharType fourthChar = *ptr;
183 if (firstChar == 'g' && secondChar == 'r' && thirdChar == 'a' && fourthChar == 'd')
184 return SVGAngle::SVG_ANGLETYPE_GRAD;
186 return SVGAngle::SVG_ANGLETYPE_UNKNOWN;
189 String SVGAngle::valueAsString() const
191 switch (m_unitType) {
192 case SVG_ANGLETYPE_DEG: {
193 DEFINE_STATIC_LOCAL(String, degString, ("deg"));
194 return String::number(m_valueInSpecifiedUnits) + degString;
196 case SVG_ANGLETYPE_RAD: {
197 DEFINE_STATIC_LOCAL(String, radString, ("rad"));
198 return String::number(m_valueInSpecifiedUnits) + radString;
200 case SVG_ANGLETYPE_GRAD: {
201 DEFINE_STATIC_LOCAL(String, gradString, ("grad"));
202 return String::number(m_valueInSpecifiedUnits) + gradString;
204 case SVG_ANGLETYPE_UNSPECIFIED:
205 case SVG_ANGLETYPE_UNKNOWN:
206 return String::number(m_valueInSpecifiedUnits);
209 ASSERT_NOT_REACHED();
213 template<typename CharType>
214 static bool parseValue(const String& value, float& valueInSpecifiedUnits, SVGAngle::SVGAngleType& unitType)
216 const CharType* ptr = value.getCharacters<CharType>();
217 const CharType* end = ptr + value.length();
219 if (!parseNumber(ptr, end, valueInSpecifiedUnits, false))
222 unitType = stringToAngleType(ptr, end);
223 if (unitType == SVGAngle::SVG_ANGLETYPE_UNKNOWN)
229 void SVGAngle::setValueAsString(const String& value, ExceptionState& exceptionState)
231 if (value.isEmpty()) {
232 newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
236 if (value == "auto") {
237 newValueSpecifiedUnits(SVG_ANGLETYPE_UNSPECIFIED, 0);
238 m_orientType->setEnumValue(SVGMarkerOrientAuto);
242 float valueInSpecifiedUnits = 0;
243 SVGAngleType unitType = SVG_ANGLETYPE_UNKNOWN;
245 bool success = value.is8Bit() ? parseValue<LChar>(value, valueInSpecifiedUnits, unitType)
246 : parseValue<UChar>(value, valueInSpecifiedUnits, unitType);
248 exceptionState.throwDOMException(SyntaxError, "The value provided ('" + value + "') is invalid.");
252 m_orientType->setEnumValue(SVGMarkerOrientAngle);
253 m_unitType = unitType;
254 m_valueInSpecifiedUnits = valueInSpecifiedUnits;
257 void SVGAngle::newValueSpecifiedUnits(SVGAngleType unitType, float valueInSpecifiedUnits)
259 m_orientType->setEnumValue(SVGMarkerOrientAngle);
260 m_unitType = unitType;
261 m_valueInSpecifiedUnits = valueInSpecifiedUnits;
264 void SVGAngle::convertToSpecifiedUnits(SVGAngleType unitType, ExceptionState& exceptionState)
266 if (m_unitType == SVG_ANGLETYPE_UNKNOWN) {
267 exceptionState.throwDOMException(NotSupportedError, "Cannot convert from unknown or invalid units.");
271 if (unitType == m_unitType)
274 switch (m_unitType) {
275 case SVG_ANGLETYPE_RAD:
277 case SVG_ANGLETYPE_GRAD:
278 m_valueInSpecifiedUnits = rad2grad(m_valueInSpecifiedUnits);
280 case SVG_ANGLETYPE_UNSPECIFIED:
281 case SVG_ANGLETYPE_DEG:
282 m_valueInSpecifiedUnits = rad2deg(m_valueInSpecifiedUnits);
284 case SVG_ANGLETYPE_RAD:
285 case SVG_ANGLETYPE_UNKNOWN:
286 ASSERT_NOT_REACHED();
290 case SVG_ANGLETYPE_GRAD:
292 case SVG_ANGLETYPE_RAD:
293 m_valueInSpecifiedUnits = grad2rad(m_valueInSpecifiedUnits);
295 case SVG_ANGLETYPE_UNSPECIFIED:
296 case SVG_ANGLETYPE_DEG:
297 m_valueInSpecifiedUnits = grad2deg(m_valueInSpecifiedUnits);
299 case SVG_ANGLETYPE_GRAD:
300 case SVG_ANGLETYPE_UNKNOWN:
301 ASSERT_NOT_REACHED();
305 case SVG_ANGLETYPE_UNSPECIFIED:
306 // Spec: For angles, a unitless value is treated the same as if degrees were specified.
307 case SVG_ANGLETYPE_DEG:
309 case SVG_ANGLETYPE_RAD:
310 m_valueInSpecifiedUnits = deg2rad(m_valueInSpecifiedUnits);
312 case SVG_ANGLETYPE_GRAD:
313 m_valueInSpecifiedUnits = deg2grad(m_valueInSpecifiedUnits);
315 case SVG_ANGLETYPE_UNSPECIFIED:
317 case SVG_ANGLETYPE_DEG:
318 case SVG_ANGLETYPE_UNKNOWN:
319 ASSERT_NOT_REACHED();
323 case SVG_ANGLETYPE_UNKNOWN:
324 ASSERT_NOT_REACHED();
328 m_unitType = unitType;
329 m_orientType->setEnumValue(SVGMarkerOrientAngle);
332 void SVGAngle::add(PassRefPtr<SVGPropertyBase> other, SVGElement*)
334 RefPtr<SVGAngle> otherAngle = toSVGAngle(other);
336 // Only respect by animations, if from and by are both specified in angles (and not eg. 'auto').
337 if (orientType()->enumValue() != SVGMarkerOrientAngle || otherAngle->orientType()->enumValue() != SVGMarkerOrientAngle)
340 setValue(value() + otherAngle->value());
343 void SVGAngle::calculateAnimatedValue(SVGAnimationElement* animationElement, float percentage, unsigned repeatCount, PassRefPtr<SVGPropertyBase> from, PassRefPtr<SVGPropertyBase> to, PassRefPtr<SVGPropertyBase> toAtEndOfDuration, SVGElement*)
345 ASSERT(animationElement);
346 bool isToAnimation = animationElement->animationMode() == ToAnimation;
348 RefPtr<SVGAngle> fromAngle = isToAnimation ? this : toSVGAngle(from);
349 RefPtr<SVGAngle> toAngle = toSVGAngle(to);
350 RefPtr<SVGAngle> toAtEndOfDurationAngle = toSVGAngle(toAtEndOfDuration);
352 SVGMarkerOrientType fromOrientType = fromAngle->orientType()->enumValue();
353 SVGMarkerOrientType toOrientType = toAngle->orientType()->enumValue();
355 if (fromOrientType != toOrientType) {
356 // Animating from eg. auto to 90deg, or auto to 90deg.
357 if (fromOrientType == SVGMarkerOrientAngle) {
358 // Animating from an angle value to eg. 'auto' - this disabled additive as 'auto' is a keyword..
359 if (toOrientType == SVGMarkerOrientAuto) {
360 if (percentage < 0.5f) {
361 newValueSpecifiedUnits(fromAngle->unitType(), fromAngle->valueInSpecifiedUnits());
364 orientType()->setEnumValue(SVGMarkerOrientAuto);
367 m_valueInSpecifiedUnits = 0;
368 orientType()->setEnumValue(SVGMarkerOrientUnknown);
373 // From 'auto' to 'auto'.
374 if (fromOrientType == SVGMarkerOrientAuto) {
375 m_valueInSpecifiedUnits = 0;
376 orientType()->setEnumValue(SVGMarkerOrientAuto);
380 // If the enumeration value is not angle or auto, its unknown.
381 if (fromOrientType != SVGMarkerOrientAngle) {
382 m_valueInSpecifiedUnits = 0;
383 orientType()->setEnumValue(SVGMarkerOrientUnknown);
387 // Regular from angle to angle animation, with all features like additive etc.
388 float animatedValue = value();
389 animationElement->animateAdditiveNumber(percentage, repeatCount, fromAngle->value(), toAngle->value(), toAtEndOfDurationAngle->value(), animatedValue);
390 orientType()->setEnumValue(SVGMarkerOrientAngle);
391 setValue(animatedValue);
394 float SVGAngle::calculateDistance(PassRefPtr<SVGPropertyBase> other, SVGElement*)
396 return fabsf(value() - toSVGAngle(other)->value());
399 void SVGAngle::orientTypeChanged()
401 if (orientType()->enumValue() == SVGMarkerOrientAuto) {
402 m_unitType = SVG_ANGLETYPE_UNSPECIFIED;
403 m_valueInSpecifiedUnits = 0;