2007-01-02 Eric Seidel <eric@webkit.org>
authoreseidel <eseidel@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Jan 2007 09:03:08 +0000 (09:03 +0000)
committereseidel <eseidel@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 2 Jan 2007 09:03:08 +0000 (09:03 +0000)
        Reviewed by rwlbuis.

        Refactor more animation code for better sharing between SVGAnimate*Element classes.
        - replace handleEndCondition with resetValues() and updateLastValueWithCurrent()
        - move initialTransform() function down into SVGTimer (eventually to AnimationCompositor)
        Add non-functional (only parses) SVGAnimateMotionElement
        Improve precision of getPointAtLength calculations and add support for "getNormalAtLength" functionality (needed for rotate='auto' support in animateMotion)
        Move add getSegmentAtLength functionality to SVGPathSegList, move toPathData into SVGPathSegList as well.
        Add SVGAnimationElement::hasValidTarget() for future better/simpler ASSERT checking
        change transformMatrix() to currentTransform() to match AffineTransform class name

        * WebCore.xcodeproj/project.pbxproj:
        * ksvg2/misc/SVGTimer.cpp:
        (WebCore::SVGTimer::animationsByElement):
        (WebCore::baseValueTransformList):
        (WebCore::SVGTimer::applyAnimations):
        * ksvg2/svg/SVGAnimateColorElement.cpp:
        (WebCore::SVGAnimateColorElement::updateLastValueWithCurrent):
        * ksvg2/svg/SVGAnimateColorElement.h:
        * ksvg2/svg/SVGAnimateMotionElement.cpp: Added.
        (WebCore::SVGAnimateMotionElement::SVGAnimateMotionElement):
        (WebCore::SVGAnimateMotionElement::~SVGAnimateMotionElement):
        (WebCore::SVGAnimateMotionElement::hasValidTarget): added.
        (WebCore::SVGAnimateMotionElement::parseMappedAttribute):
        (WebCore::SVGAnimateMotionElement::animationPath): fetch path from <mpath> or d attribute
        (WebCore::SVGAnimateMotionElement::updateCurrentValue):
        (WebCore::SVGAnimateMotionElement::handleStartCondition):
        (WebCore::SVGAnimateMotionElement::applyAnimationToValue):
        * ksvg2/svg/SVGAnimateMotionElement.h: Added.
        (WebCore::SVGAnimateMotionElement::contextElement):
        (WebCore::SVGAnimateMotionElement::):
        * ksvg2/svg/SVGAnimateTransformElement.cpp:
        (WebCore::SVGAnimateTransformElement::hasValidTarget): added.
        (WebCore::SVGAnimateTransformElement::storeInitialValue):
        (WebCore::SVGAnimateTransformElement::updateCurrentValue):
        (WebCore::SVGAnimateTransformElement::updateLastValueWithCurrent):
        (WebCore::SVGAnimateTransformElement::applyAnimationToValue):
        (WebCore::SVGAnimateTransformElement::currentTransform):
        * ksvg2/svg/SVGAnimateTransformElement.h:
        * ksvg2/svg/SVGAnimationElement.cpp:
        (WebCore::SVGAnimationElement::hasValidTarget): added.
        (WebCore::SVGAnimationElement::parseMappedAttribute):
        (WebCore::SVGAnimationElement::isAdditive):
        (WebCore::SVGAnimationElement::isAccumulated):
        (WebCore::SVGAnimationElement::handleTimerEvent):
        * ksvg2/svg/SVGAnimationElement.h:
        (WebCore::SVGAnimationElement::updateLastValueWithCurrent):
        (WebCore::SVGAnimationElement::resetValues):
        * ksvg2/svg/SVGPathElement.cpp:
        (WebCore::SVGPathElement::getPointAtLength):
        (WebCore::SVGPathElement::getPathSegAtLength):
        (WebCore::SVGPathElement::parseMappedAttribute):
        (WebCore::SVGPathElement::toPathData):
        * ksvg2/svg/SVGPathSegList.cpp:
        (WebCore::SVGPathSegList::getPathSegAtLength): added.
        (WebCore::SVGPathSegList::toPathData): added.
        * ksvg2/svg/SVGPathSegList.h:
        * ksvg2/svg/svgtags.in: add animateMotion
        * platform/graphics/Path.cpp:
        (WebCore::pathLengthApplierFunction): add support for TraversalNormalAngleAtLength
        * platform/graphics/PathTraversalState.cpp:
        (WebCore::curveLength): support higher precision pointAtLength calculations, and add normalAtLength support
        (WebCore::PathTraversalState::quadraticBezierTo):
        (WebCore::PathTraversalState::cubicBezierTo):
        * platform/graphics/PathTraversalState.h:
        (WebCore::PathTraversalState::): add TraversalNormalAngleAtLength mode

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@18518 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
WebCore/CMakeLists.txt
WebCore/ChangeLog
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/ksvg2/misc/SVGTimer.cpp
WebCore/ksvg2/svg/SVGAnimateColorElement.cpp
WebCore/ksvg2/svg/SVGAnimateColorElement.h
WebCore/ksvg2/svg/SVGAnimateMotionElement.cpp [new file with mode: 0644]
WebCore/ksvg2/svg/SVGAnimateMotionElement.h [new file with mode: 0644]
WebCore/ksvg2/svg/SVGAnimateTransformElement.cpp
WebCore/ksvg2/svg/SVGAnimateTransformElement.h
WebCore/ksvg2/svg/SVGAnimationElement.cpp
WebCore/ksvg2/svg/SVGAnimationElement.h
WebCore/ksvg2/svg/SVGPathElement.cpp
WebCore/ksvg2/svg/SVGPathSegList.cpp
WebCore/ksvg2/svg/SVGPathSegList.h
WebCore/ksvg2/svg/svgtags.in
WebCore/platform/graphics/Path.cpp
WebCore/platform/graphics/Path.h
WebCore/platform/graphics/PathTraversalState.cpp
WebCore/platform/graphics/PathTraversalState.h

index d3d8644..5ea89d0 100644 (file)
@@ -552,6 +552,7 @@ set (SVG_SOURCES
     ksvg2/svg/SVGAnimatedPathData.cpp
     ksvg2/svg/SVGAnimatedPoints.cpp
     ksvg2/svg/SVGAnimateElement.cpp
+    ksvg2/svg/SVGAnimateMotionElement.cpp
     ksvg2/svg/SVGAnimateTransformElement.cpp
     ksvg2/svg/SVGAnimationElement.cpp
     ksvg2/svg/SVGCircleElement.cpp
index 6a6bdd9..d8ff734 100644 (file)
@@ -1,3 +1,72 @@
+2007-01-02  Eric Seidel  <eric@webkit.org>
+
+        Reviewed by rwlbuis.
+
+        Refactor more animation code for better sharing between SVGAnimate*Element classes.
+        - replace handleEndCondition with resetValues() and updateLastValueWithCurrent()
+        - move initialTransform() function down into SVGTimer (eventually to AnimationCompositor)
+        Add non-functional (only parses) SVGAnimateMotionElement
+        Improve precision of getPointAtLength calculations and add support for "getNormalAtLength" functionality (needed for rotate='auto' support in animateMotion)
+        Move add getSegmentAtLength functionality to SVGPathSegList, move toPathData into SVGPathSegList as well.
+        Add SVGAnimationElement::hasValidTarget() for future better/simpler ASSERT checking
+        change transformMatrix() to currentTransform() to match AffineTransform class name
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * ksvg2/misc/SVGTimer.cpp:
+        (WebCore::SVGTimer::animationsByElement):
+        (WebCore::baseValueTransformList):
+        (WebCore::SVGTimer::applyAnimations):
+        * ksvg2/svg/SVGAnimateColorElement.cpp:
+        (WebCore::SVGAnimateColorElement::updateLastValueWithCurrent):
+        * ksvg2/svg/SVGAnimateColorElement.h:
+        * ksvg2/svg/SVGAnimateMotionElement.cpp: Added.
+        (WebCore::SVGAnimateMotionElement::SVGAnimateMotionElement):
+        (WebCore::SVGAnimateMotionElement::~SVGAnimateMotionElement):
+        (WebCore::SVGAnimateMotionElement::hasValidTarget): added.
+        (WebCore::SVGAnimateMotionElement::parseMappedAttribute):
+        (WebCore::SVGAnimateMotionElement::animationPath): fetch path from <mpath> or d attribute
+        (WebCore::SVGAnimateMotionElement::updateCurrentValue):
+        (WebCore::SVGAnimateMotionElement::handleStartCondition):
+        (WebCore::SVGAnimateMotionElement::applyAnimationToValue):
+        * ksvg2/svg/SVGAnimateMotionElement.h: Added.
+        (WebCore::SVGAnimateMotionElement::contextElement):
+        (WebCore::SVGAnimateMotionElement::):
+        * ksvg2/svg/SVGAnimateTransformElement.cpp:
+        (WebCore::SVGAnimateTransformElement::hasValidTarget): added.
+        (WebCore::SVGAnimateTransformElement::storeInitialValue):
+        (WebCore::SVGAnimateTransformElement::updateCurrentValue):
+        (WebCore::SVGAnimateTransformElement::updateLastValueWithCurrent):
+        (WebCore::SVGAnimateTransformElement::applyAnimationToValue):
+        (WebCore::SVGAnimateTransformElement::currentTransform):
+        * ksvg2/svg/SVGAnimateTransformElement.h:
+        * ksvg2/svg/SVGAnimationElement.cpp:
+        (WebCore::SVGAnimationElement::hasValidTarget): added.
+        (WebCore::SVGAnimationElement::parseMappedAttribute):
+        (WebCore::SVGAnimationElement::isAdditive):
+        (WebCore::SVGAnimationElement::isAccumulated):
+        (WebCore::SVGAnimationElement::handleTimerEvent):
+        * ksvg2/svg/SVGAnimationElement.h:
+        (WebCore::SVGAnimationElement::updateLastValueWithCurrent):
+        (WebCore::SVGAnimationElement::resetValues):
+        * ksvg2/svg/SVGPathElement.cpp:
+        (WebCore::SVGPathElement::getPointAtLength):
+        (WebCore::SVGPathElement::getPathSegAtLength):
+        (WebCore::SVGPathElement::parseMappedAttribute):
+        (WebCore::SVGPathElement::toPathData):
+        * ksvg2/svg/SVGPathSegList.cpp:
+        (WebCore::SVGPathSegList::getPathSegAtLength): added.
+        (WebCore::SVGPathSegList::toPathData): added.
+        * ksvg2/svg/SVGPathSegList.h:
+        * ksvg2/svg/svgtags.in: add animateMotion
+        * platform/graphics/Path.cpp:
+        (WebCore::pathLengthApplierFunction): add support for TraversalNormalAngleAtLength
+        * platform/graphics/PathTraversalState.cpp:
+        (WebCore::curveLength): support higher precision pointAtLength calculations, and add normalAtLength support
+        (WebCore::PathTraversalState::quadraticBezierTo):
+        (WebCore::PathTraversalState::cubicBezierTo):
+        * platform/graphics/PathTraversalState.h:
+        (WebCore::PathTraversalState::): add TraversalNormalAngleAtLength mode
+
 2007-01-01  Mitz Pettel  <mitz@webkit.org>
 
         Reviewed by Darin.
index 4cab89d..eef717d 100644 (file)
                A8239E0109B3CF8A00B60641 /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = A8239DFF09B3CF8A00B60641 /* Logging.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A8241D8B0B48D31E0084722B /* SVGTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = A8241D890B48D31E0084722B /* SVGTimer.h */; };
                A8241D8C0B48D31E0084722B /* SVGTimer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8241D8A0B48D31E0084722B /* SVGTimer.cpp */; };
+               A82422700B4A16C50084722B /* SVGAnimateMotionElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A824226E0B4A16C50084722B /* SVGAnimateMotionElement.h */; };
+               A82422710B4A16C50084722B /* SVGAnimateMotionElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A824226F0B4A16C50084722B /* SVGAnimateMotionElement.cpp */; };
                A826E8AE0A1A8F2300CD1BB6 /* JSHTMLOptionElementConstructor.h in Headers */ = {isa = PBXBuildFile; fileRef = A826E8AC0A1A8F2300CD1BB6 /* JSHTMLOptionElementConstructor.h */; };
                A826EC480A1B0CBE00CD1BB6 /* JSHTMLOptionElementConstructor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A826E8AD0A1A8F2300CD1BB6 /* JSHTMLOptionElementConstructor.cpp */; };
                A833C7CA0A2CF06B00D57664 /* SVGNames.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 656581E809D1508D000E61D7 /* SVGNames.cpp */; };
                A8239DFF09B3CF8A00B60641 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
                A8241D890B48D31E0084722B /* SVGTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGTimer.h; sourceTree = "<group>"; };
                A8241D8A0B48D31E0084722B /* SVGTimer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SVGTimer.cpp; sourceTree = "<group>"; };
+               A824226E0B4A16C50084722B /* SVGAnimateMotionElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVGAnimateMotionElement.h; sourceTree = "<group>"; };
+               A824226F0B4A16C50084722B /* SVGAnimateMotionElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SVGAnimateMotionElement.cpp; sourceTree = "<group>"; };
                A826E8AC0A1A8F2300CD1BB6 /* JSHTMLOptionElementConstructor.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = JSHTMLOptionElementConstructor.h; sourceTree = "<group>"; };
                A826E8AD0A1A8F2300CD1BB6 /* JSHTMLOptionElementConstructor.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = JSHTMLOptionElementConstructor.cpp; sourceTree = "<group>"; };
                A82FC33B08CBB07C00EFEE23 /* SVGCursorElement.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.c.h; path = SVGCursorElement.h; sourceTree = "<group>"; tabWidth = 8; usesTabs = 0; };
                                A8C0F822089701F300BA5114 /* SVGAnimateElement.cpp */,
                                A8C0F823089701F300BA5114 /* SVGAnimateElement.h */,
                                B2CB41060AB758E6004D9C45 /* SVGAnimateElement.idl */,
+                               A824226F0B4A16C50084722B /* SVGAnimateMotionElement.cpp */,
+                               A824226E0B4A16C50084722B /* SVGAnimateMotionElement.h */,
                                A8C0F824089701F300BA5114 /* SVGAnimateTransformElement.cpp */,
                                A8C0F825089701F300BA5114 /* SVGAnimateTransformElement.h */,
                                B2CB41070AB758E6004D9C45 /* SVGAnimateTransformElement.idl */,
                                A8C9E0950B43A5250097883A /* SVGImageEmptyClients.h in Headers */,
                                A88DD4870B4629A300C02990 /* PathTraversalState.h in Headers */,
                                A8241D8B0B48D31E0084722B /* SVGTimer.h in Headers */,
+                               A82422700B4A16C50084722B /* SVGAnimateMotionElement.h in Headers */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
                                A8C9DD270B4374260097883A /* SVGImage.cpp in Sources */,
                                A88DD4890B4629B000C02990 /* PathTraversalState.cpp in Sources */,
                                A8241D8C0B48D31E0084722B /* SVGTimer.cpp in Sources */,
+                               A82422710B4A16C50084722B /* SVGAnimateMotionElement.cpp in Sources */,
                        );
                        runOnlyForDeploymentPostprocessing = 0;
                };
index a38a9d4..e45a80e 100644 (file)
@@ -2,6 +2,7 @@
     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                   2004, 2005 Rob Buis <buis@kde.org>
     Copyright (C) 2006 Apple Computer, Inc.
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
     This file is part of the KDE project
 
@@ -27,6 +28,7 @@
 
 #include <wtf/HashMap.h>
 #include "SVGAnimateTransformElement.h"
+#include "SVGAnimateMotionElement.h"
 #include "SVGTransformList.h"
 #include "SVGAnimateColorElement.h"
 #include "SVGStyledTransformableElement.h"
@@ -68,7 +70,7 @@ SVGTimer::TargetAnimationMap SVGTimer::animationsByElement(double elapsedSeconds
                 continue;
         }
         
-        SVGElement* target = const_cast<SVGElement *>(animation->targetElement());
+        SVGElement* target = const_cast<SVGElement*>(animation->targetElement());
         TargetAnimationMap::iterator i = targetMap.find(target);
         if (i != targetMap.end())
             i->second.append(animation);
@@ -81,11 +83,28 @@ SVGTimer::TargetAnimationMap SVGTimer::animationsByElement(double elapsedSeconds
     return targetMap;
 }
 
+// FIXME: Eventually this logic will move into an AnimationCompositor class
+// FIXME: I also think this also violates the spec.  We should probably just copy the baseValue and not consolidate
+static PassRefPtr<SVGTransformList> baseValueTransformList(SVGElement* targetElement)
+{
+    ASSERT(targetElement->isStyledTransformable());
+    SVGStyledTransformableElement* transform = static_cast<SVGStyledTransformableElement*>(targetElement);
+    RefPtr<SVGTransformList> targetTransforms = new SVGTransformList();
+    SVGTransformList* transformList = transform->transformBaseValue();
+    if (transformList) {
+        RefPtr<SVGTransform> initialTransform = transformList->concatenate();
+        if (initialTransform) {
+            ExceptionCode ec;
+            targetTransforms->appendItem(initialTransform.get(), ec);
+            return targetTransforms.release();
+        }
+    }
+    return targetTransforms;
+}
+
 // FIXME: This funtion will eventually become part of the AnimationCompositor
 void SVGTimer::applyAnimations(double elapsedSeconds, const SVGTimer::TargetAnimationMap& targetMap)
-{
-    ExceptionCode ec = 0;
-    
+{    
     TargetAnimationMap::const_iterator targetIterator = targetMap.begin();
     TargetAnimationMap::const_iterator tend = targetMap.end();
     for (; targetIterator != tend; ++targetIterator) {
@@ -102,21 +121,20 @@ void SVGTimer::applyAnimations(double elapsedSeconds, const SVGTimer::TargetAnim
         for (unsigned i = 0; i < count; ++i) {
             SVGAnimationElement* animation = targetIterator->second[i];
             
+            if (!animation->hasValidTarget())
+                continue;
+            
             if (!animation->updateForElapsedSeconds(elapsedSeconds))
                 continue;
             
-            if (animation->hasTagName(SVGNames::animateTransformTag)) {
-                SVGAnimateTransformElement* animTransform = static_cast<SVGAnimateTransformElement*>(animation);
-                if (!targetTransforms) {
-                    targetTransforms = new SVGTransformList();
-                    if (animation->isAdditive()) { // Avoid mallocing a transform which is about to be replaced
-                        RefPtr<SVGTransform> initialTransform = new SVGTransform();
-                        initialTransform->setMatrix(animTransform->initialMatrix());
-                        targetTransforms->appendItem(initialTransform.get(), ec);
-                    }
-                }
-                animTransform->applyAnimationToValue(targetTransforms.get());
-            } else if (animation->hasTagName(SVGNames::animateColorTag)) {
+            if (!targetTransforms && (animation->hasTagName(SVGNames::animateTransformTag) || animation->hasTagName(SVGNames::animateMotionTag)))
+                targetTransforms = baseValueTransformList(animation->targetElement());
+            
+            if (animation->hasTagName(SVGNames::animateTransformTag))
+                static_cast<SVGAnimateTransformElement*>(animation)->applyAnimationToValue(targetTransforms.get());
+            else if (animation->hasTagName(SVGNames::animateMotionTag))
+                static_cast<SVGAnimateMotionElement*>(animation)->applyAnimationToValue(targetTransforms.get());
+            else if (animation->hasTagName(SVGNames::animateColorTag)) {
                 SVGAnimateColorElement* animColor = static_cast<SVGAnimateColorElement*>(animation);
                 String name = animColor->attributeName();
                 Color currentColor = attributeToColorMap.contains(name) ? attributeToColorMap.get(name) : animColor->initialColor();
@@ -125,7 +143,7 @@ void SVGTimer::applyAnimations(double elapsedSeconds, const SVGTimer::TargetAnim
             }
         }
         
-        // Apply any transform changes (animateTransform)
+        // Apply any transform changes (animateTransform & animateMotion)
         if (targetTransforms) {
             SVGElement* key = targetIterator->first;
             if (key && key->isStyledTransformable()) {
index dab6209..2821f25 100644 (file)
@@ -1,6 +1,7 @@
 /*
     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                   2004, 2005, 2006 Rob Buis <buis@kde.org>
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
     This file is part of the KDE project
 
@@ -171,16 +172,9 @@ bool SVGAnimateColorElement::handleStartCondition()
     return true;
 }
 
-void SVGAnimateColorElement::handleEndCondition()
+void SVGAnimateColorElement::updateLastValueWithCurrent()
 {
-    if ((m_repeatCount > 0 && m_repeations < m_repeatCount - 1) || isIndefinite(m_repeatCount)) {
-        m_lastColor = m_currentColor;
-        m_repeations++;
-        return;
-    }
-    
-    disconnectTimer();
-    resetValues();
+    m_lastColor = m_currentColor;
 }
 
 void SVGAnimateColorElement::applyAnimationToValue(Color& currentColor)
index f8ec61b..10ba98f 100644 (file)
@@ -1,6 +1,7 @@
 /*
     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                   2004, 2005, 2006 Rob Buis <buis@kde.org>
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
     This file is part of the KDE project
 
@@ -20,8 +21,8 @@
     Boston, MA 02111-1307, USA.
 */
 
-#ifndef KSVG_SVGAnimateColorElementImpl_H
-#define KSVG_SVGAnimateColorElementImpl_H
+#ifndef SVGAnimateColorElement_H
+#define SVGAnimateColorElement_H
 #ifdef SVG_SUPPORT
 
 #include "SVGAnimationElement.h"
@@ -49,11 +50,11 @@ namespace WebCore {
     protected:
         virtual const SVGElement* contextElement() const { return this; }
         void storeInitialValue();
-        void resetValues();
+        virtual void resetValues();
         
         virtual bool updateCurrentValue(double timePercentage);
         virtual bool handleStartCondition();
-        virtual void handleEndCondition();
+        virtual void updateLastValueWithCurrent();
 
     private:
         Color m_lastColor;
diff --git a/WebCore/ksvg2/svg/SVGAnimateMotionElement.cpp b/WebCore/ksvg2/svg/SVGAnimateMotionElement.cpp
new file mode 100644 (file)
index 0000000..fee65d3
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+
+    This file is part of the WebKit project
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public License
+    along with this library; see the file COPYING.LIB.  If not, write to
+    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+    Boston, MA 02111-1307, USA.
+*/
+
+#include "config.h"
+#ifdef SVG_SUPPORT
+#include "SVGAnimateMotionElement.h"
+
+#include "SVGPathElement.h"
+#include "SVGTransformList.h"
+
+namespace WebCore {
+
+SVGAnimateMotionElement::SVGAnimateMotionElement(const QualifiedName& tagName, Document* doc)
+    : SVGAnimationElement(tagName, doc)
+    , m_rotateMode(AngleMode)
+    , m_angle(0)
+{
+}
+
+SVGAnimateMotionElement::~SVGAnimateMotionElement()
+{
+}
+
+bool SVGAnimateMotionElement::hasValidTarget() const
+{
+    return (SVGAnimationElement::hasValidTarget() && targetElement()->isStyledTransformable());
+}
+
+void SVGAnimateMotionElement::parseMappedAttribute(MappedAttribute* attr)
+{
+    const QualifiedName& name = attr->name();
+    const String& value = attr->value();
+    if (name == SVGNames::rotateAttr) {
+        if (value == "auto")
+            m_rotateMode = AutoMode;
+        else if (value == "auto-reverse")
+            m_rotateMode = AutoReverseMode;
+        else {
+            m_rotateMode = AngleMode;
+            m_angle = value.toDouble();
+        }
+    } else if (name == SVGNames::keyPointsAttr) {
+        // FIXME: not implemented
+    } else if (name == SVGNames::dAttr) {
+        // FIXME: This dummy object is neccessary until path parsing is untangled from SVGPathElement
+        RefPtr<SVGPathElement> dummyPath = new SVGPathElement(SVGNames::pathTag, document());
+        if (dummyPath->parseSVG(attr->value(), true))
+            m_path = dummyPath->toPathData();
+        else
+            m_path = Path();
+    } else
+        SVGAnimationElement::parseMappedAttribute(attr);
+}
+
+Path SVGAnimateMotionElement::animationPath()
+{
+#if 0 // disabled until SVGMPathElement is added
+    for (Node* child = firstChild(); child; child->nextSibling()) {
+        if (child->hasName(SVGNames::mPathTag))
+            return static_cast<SVGMPathElement*>(child)->toPathData();
+    }
+#endif
+    if (hasAttribute(SVGNames::dAttr))
+        return m_path;
+    return Path();
+}
+
+bool SVGAnimateMotionElement::updateCurrentValue(double timePercentage)
+{
+    return true;
+}
+
+bool SVGAnimateMotionElement::handleStartCondition()
+{
+    return true;
+}
+
+void SVGAnimateMotionElement::applyAnimationToValue(SVGTransformList* targetTransforms)
+{
+    ExceptionCode ec;
+    if (!isAdditive())
+        targetTransforms->clear(ec);
+    
+    RefPtr<SVGTransform> targetTransform = new SVGTransform();
+    targetTransform->setMatrix(m_currentTransform);
+    targetTransforms->appendItem(targetTransform.get(), ec);
+}
+
+}
+
+#endif // SVG_SUPPORT
+
+// vim:ts=4:noet
diff --git a/WebCore/ksvg2/svg/SVGAnimateMotionElement.h b/WebCore/ksvg2/svg/SVGAnimateMotionElement.h
new file mode 100644 (file)
index 0000000..262f983
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+ This file is part of the WebKit project
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Library General Public License for more details.
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB.  If not, write to
+ the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SVGAnimateMotionElement_H
+#define SVGAnimateMotionElement_H
+#ifdef SVG_SUPPORT
+
+#include "SVGAnimationElement.h"
+#include "AffineTransform.h"
+#include "Path.h"
+
+namespace WebCore {
+            
+    class SVGAnimateMotionElement : public SVGAnimationElement {
+    public:
+        SVGAnimateMotionElement(const QualifiedName&, Document*);
+        virtual ~SVGAnimateMotionElement();
+        
+        virtual bool hasValidTarget() const;
+        
+        virtual void parseMappedAttribute(MappedAttribute*);
+        
+        void applyAnimationToValue(SVGTransformList*);
+        
+        Path animationPath();
+        
+    protected:
+        virtual const SVGElement* contextElement() const { return this; }
+        
+        virtual bool updateCurrentValue(double timePercentage);
+        virtual bool handleStartCondition();
+        
+    private:
+        Path m_path;
+        Vector<float> m_keyPoints;
+        enum RotateMode {
+            AngleMode,
+            AutoMode,
+            AutoReverseMode
+        };
+        RotateMode m_rotateMode;
+        float m_angle;
+        AffineTransform m_currentTransform;
+    };
+    
+} // namespace WebCore
+
+#endif // SVG_SUPPORT
+#endif // SVGAnimateMotionElement_H
+
+// vim:ts=4:noet
index a843449..46ea8f7 100644 (file)
@@ -1,6 +1,7 @@
 /*
     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                   2004, 2005, 2006 Rob Buis <buis@kde.org>
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
     This file is part of the KDE project
 
@@ -52,6 +53,11 @@ SVGAnimateTransformElement::~SVGAnimateTransformElement()
 {
 }
 
+bool SVGAnimateTransformElement::hasValidTarget() const
+{
+    return (SVGAnimationElement::hasValidTarget() && targetElement()->isStyledTransformable());
+}
+
 void SVGAnimateTransformElement::parseMappedAttribute(MappedAttribute* attr)
 {
     if (attr->name() == SVGNames::typeAttr) {
@@ -85,6 +91,7 @@ void SVGAnimateTransformElement::storeInitialValue()
                 if (!value)
                     continue;
                 
+                // FIXME: This is wrong if the initial transform list has not been normalized
                 if (value->type() == m_type) {
                     m_initialTransform = value;
                     break;
@@ -153,16 +160,10 @@ bool SVGAnimateTransformElement::updateCurrentValue(double timePercentage)
     if (m_fromTransform && qFromMatrix.isIdentity())
         qFromMatrix = m_fromTransform->matrix();
     
-    /* FIXME: This logic needs fixing, was broken before, is still broken.
-        if (m_transformMatrix)
-        m_transformMatrix = new SVGMatrix();
-    else {
-        m_transformMatrix.reset();
-        
-        if (isAccumulated() && repeations() != 0.0 && m_lastMatrix)
-            m_transformMatrix->multiply(m_lastMatrix.get());
-    }
-    */   
+    m_currentTransform.reset();
+    
+    if (isAccumulated() && repeations() != 0.0)
+        m_currentTransform.multiply(m_lastTransform);
     
     switch (m_type) {
         case SVGTransform::SVG_TRANSFORM_TRANSLATE:
@@ -170,7 +171,7 @@ bool SVGAnimateTransformElement::updateCurrentValue(double timePercentage)
             double dx = ((qToMatrix.e() - qFromMatrix.e()) * useTimePercentage) + qFromMatrix.e();
             double dy = ((qToMatrix.f() - qFromMatrix.f()) * useTimePercentage) + qFromMatrix.f();
             
-            m_transformMatrix.translate(dx, dy);
+            m_currentTransform.translate(dx, dy);
             break;
         }
         case SVGTransform::SVG_TRANSFORM_SCALE:
@@ -178,7 +179,7 @@ bool SVGAnimateTransformElement::updateCurrentValue(double timePercentage)
             double sx = ((qToMatrix.a() - qFromMatrix.a()) * useTimePercentage) + qFromMatrix.a();
             double sy = ((qToMatrix.d() - qFromMatrix.d()) * useTimePercentage) + qFromMatrix.d();
             
-            m_transformMatrix.scale(sx, sy);
+            m_currentTransform.scale(sx, sy);
             break;
         }
         case SVGTransform::SVG_TRANSFORM_ROTATE:
@@ -197,9 +198,9 @@ bool SVGAnimateTransformElement::updateCurrentValue(double timePercentage)
             double cx = (toCx - fromCx) * useTimePercentage + fromCx;
             double cy = (toCy - fromCy) * useTimePercentage + fromCy;
             
-            m_transformMatrix.translate(cx, cy);
-            m_transformMatrix.rotate(angle);
-            m_transformMatrix.translate(-cx, -cy);
+            m_currentTransform.translate(cx, cy);
+            m_currentTransform.rotate(angle);
+            m_currentTransform.translate(-cx, -cy);
             break;
         }
         case SVGTransform::SVG_TRANSFORM_SKEWX:
@@ -207,7 +208,7 @@ bool SVGAnimateTransformElement::updateCurrentValue(double timePercentage)
             double sx = (SVGAngle::todeg(atan(qToMatrix.c()) - atan(qFromMatrix.c())) *
                          useTimePercentage) + SVGAngle::todeg(atan(qFromMatrix.c()));
             
-            m_transformMatrix.shear(sx, 1.0);
+            m_currentTransform.shear(sx, 1.0);
             break;
         }
         case SVGTransform::SVG_TRANSFORM_SKEWY:
@@ -215,7 +216,7 @@ bool SVGAnimateTransformElement::updateCurrentValue(double timePercentage)
             double sy = (SVGAngle::todeg(atan(qToMatrix.b()) - atan(qFromMatrix.b())) *
                          useTimePercentage) + SVGAngle::todeg(atan(qFromMatrix.b()));
             
-            m_transformMatrix.shear(1.0, sy);
+            m_currentTransform.shear(1.0, sy);
             break;
         }
         default:
@@ -283,28 +284,9 @@ bool SVGAnimateTransformElement::handleStartCondition()
     return true;
 }
 
-void SVGAnimateTransformElement::handleEndCondition()
+void SVGAnimateTransformElement::updateLastValueWithCurrent()
 {
-    if ((m_repeatCount > 0 && m_repeations < m_repeatCount - 1) || isIndefinite(m_repeatCount)) {
-        m_lastMatrix.reset();
-        
-        if (!m_transformMatrix.isIdentity())
-            m_lastMatrix = m_transformMatrix;
-        
-        m_repeations++;
-        return;
-    }
-    
-    disconnectTimer();
-    resetValues();
-    
-    if (!isFrozen()) {
-        AffineTransform initial = initialMatrix();
-        if (!initial.isIdentity())
-            m_transformMatrix = initial;
-        else
-            m_transformMatrix.reset();
-    }
+    m_lastTransform = m_currentTransform;
 }
 
 void SVGAnimateTransformElement::applyAnimationToValue(SVGTransformList* targetTransforms)
@@ -314,7 +296,7 @@ void SVGAnimateTransformElement::applyAnimationToValue(SVGTransformList* targetT
         targetTransforms->clear(ec);
     
     RefPtr<SVGTransform> targetTransform = new SVGTransform();
-    targetTransform->setMatrix(m_transformMatrix);
+    targetTransform->setMatrix(m_currentTransform);
     targetTransforms->appendItem(targetTransform.get(), ec);
 }
 
@@ -432,25 +414,9 @@ void SVGAnimateTransformElement::calculateRotationFromMatrix(const AffineTransfo
     angle = 0.0;
 }
 
-AffineTransform SVGAnimateTransformElement::initialMatrix() const
-{
-    if (!targetElement()->isStyledTransformable())
-        return AffineTransform();
-    SVGStyledTransformableElement* transform = static_cast<SVGStyledTransformableElement*>(targetElement());
-    SVGTransformList* transformList = (transform ? transform->transformBaseValue() : 0);
-    if (!transformList)
-        return AffineTransform();
-
-    RefPtr<SVGTransform> result = transformList->concatenate();
-    if (!result)
-        return AffineTransform();
-
-    return result->matrix();
-}
-
-AffineTransform SVGAnimateTransformElement::transformMatrix() const
+AffineTransform SVGAnimateTransformElement::currentTransform() const
 {
-    return m_transformMatrix;
+    return m_currentTransform;
 }
 
 }
index c8ecc68..09df896 100644 (file)
@@ -1,7 +1,8 @@
 /*
     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                   2004, 2005, 2006 Rob Buis <buis@kde.org>
-
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+    
     This file is part of the KDE project
 
     This library is free software; you can redistribute it and/or
@@ -35,6 +36,8 @@ namespace WebCore {
     public:
         SVGAnimateTransformElement(const QualifiedName&, Document*);
         virtual ~SVGAnimateTransformElement();
+        
+        virtual bool hasValidTarget() const;
 
         virtual void parseMappedAttribute(MappedAttribute*);
 
@@ -44,17 +47,16 @@ namespace WebCore {
         RefPtr<SVGTransform> parseTransformValue(const String&) const;
         void calculateRotationFromMatrix(const AffineTransform&, double& angle, double& cx, double& cy) const;
 
-        AffineTransform initialMatrix() const;
-        AffineTransform transformMatrix() const;
+        AffineTransform currentTransform() const;
 
     protected:
         virtual const SVGElement* contextElement() const { return this; }
         void storeInitialValue();
-        void resetValues();
+        virtual void resetValues();
         
         virtual bool updateCurrentValue(double timePercentage);
         virtual bool handleStartCondition();
-        virtual void handleEndCondition();
+        virtual void updateLastValueWithCurrent();
 
     private:
         int m_currentItem;
@@ -64,8 +66,8 @@ namespace WebCore {
         RefPtr<SVGTransform> m_fromTransform;
         RefPtr<SVGTransform> m_initialTransform;
 
-        AffineTransform m_lastMatrix;
-        AffineTransform m_transformMatrix;
+        AffineTransform m_lastTransform;
+        AffineTransform m_currentTransform;
 
         mutable bool m_rotateSpecialCase : 1;
         bool m_toRotateSpecialCase : 1;
index f5b2a36..f2c0523 100644 (file)
@@ -1,6 +1,7 @@
 /*
     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                   2004, 2005, 2006 Rob Buis <buis@kde.org>
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
     This file is part of the KDE project
 
@@ -58,7 +59,7 @@ SVGAnimationElement::SVGAnimationElement(const QualifiedName& tagName, Document*
     , m_min(0.0)
     , m_end(0.0)
     , m_begin(0.0)
-    , m_repeations(0)
+    , m_repetitions(0)
     , m_repeatCount(0)
 {
 
@@ -68,6 +69,11 @@ SVGAnimationElement::~SVGAnimationElement()
 {
 }
 
+bool SVGAnimationElement::hasValidTarget() const
+{
+    return targetElement();
+}
+
 SVGElement* SVGAnimationElement::targetElement() const
 {
     if (!m_targetElement) {
@@ -124,17 +130,12 @@ void SVGAnimationElement::parseMappedAttribute(MappedAttribute* attr)
         else if (value == "auto")
             m_attributeType = ATTRIBUTETYPE_AUTO;
     } else if (attr->name() == SVGNames::beginAttr || attr->name() == SVGNames::endAttr) {
-        // Create list
-        RefPtr<SVGStringList> temp = new SVGStringList();
-
-        // Feed data into list
-        temp->parse(value, ';');
+        RefPtr<SVGStringList> valueList = new SVGStringList();
+        valueList->parse(value, ';');
 
         ExceptionCode ec = 0;
-
-        // Parse data
-        for (unsigned int i = 0; i < temp->numberOfItems(); i++) {
-            String current = temp->getItem(i, ec);
+        for (unsigned int i = 0; i < valueList->numberOfItems(); i++) {
+            String current = valueList->getItem(i, ec);
 
             if (current.startsWith("accessKey")) {
                 // Register keyDownEventListener for the character
@@ -249,9 +250,10 @@ void SVGAnimationElement::parseMappedAttribute(MappedAttribute* attr)
         else if (value == "none")
             m_accumulate = ACCUMULATE_NONE;
     } else {
-        if (SVGTests::parseMappedAttribute(attr)) return;
-        if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) return;
-        
+        if (SVGTests::parseMappedAttribute(attr))
+            return;
+        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
+            return;
         SVGElement::parseMappedAttribute(attr);
     }
 }
@@ -425,14 +427,12 @@ bool SVGAnimationElement::isFrozen() const
 
 bool SVGAnimationElement::isAdditive() const
 {
-    return (m_additive == ADDITIVE_SUM) ||
-           (detectAnimationMode() == BY_ANIMATION);
+    return (m_additive == ADDITIVE_SUM) || (detectAnimationMode() == BY_ANIMATION);
 }
 
 bool SVGAnimationElement::isAccumulated() const
 {
-    return (m_accumulate == ACCUMULATE_SUM) &&
-           (detectAnimationMode() != TO_ANIMATION);
+    return (m_accumulate == ACCUMULATE_SUM) && (detectAnimationMode() != TO_ANIMATION);
 }
 
 EAnimationMode SVGAnimationElement::detectAnimationMode() const
@@ -504,7 +504,7 @@ double SVGAnimationElement::calculateRelativeTimePercentage(double timePercentag
 
 double SVGAnimationElement::repeations() const
 {
-    return m_repeations;
+    return m_repetitions;
 }
 
 bool SVGAnimationElement::isIndefinite(double value) const
@@ -548,15 +548,23 @@ void SVGAnimationElement::handleTimerEvent(double timePercentage)
 {
     if (!connectedToTimer()) {
         connectTimer();
-        handleStartCondition(); // Check bool if adding anything after this call
+        handleStartCondition(); // Need to check bool return if adding anything after this call
         return;
     }
     
     if (!updateCurrentValue(timePercentage))
         return;
     
-    if (timePercentage == 1.0)
-        handleEndCondition();
+    if (timePercentage == 1.0) {
+        if ((m_repeatCount > 0 && m_repetitions < m_repeatCount - 1) || isIndefinite(m_repeatCount)) {
+            updateLastValueWithCurrent();
+            m_repetitions++;
+            return;
+        }
+        
+        disconnectTimer();
+        resetValues();   
+    }
 }
 
 bool SVGAnimationElement::updateForElapsedSeconds(double elapsedSeconds)
@@ -568,7 +576,7 @@ bool SVGAnimationElement::updateForElapsedSeconds(double elapsedSeconds)
     if ((m_simpleDuration <= 0.0 && m_end <= 0.0) || (isIndefinite(m_simpleDuration) && m_end <= 0.0))
         return false; // Ignore dur="0" or dur="-neg"
     
-    float percentage = calculateTimePercentage(elapsedSeconds, m_begin, m_end, m_simpleDuration, m_repeations);
+    float percentage = calculateTimePercentage(elapsedSeconds, m_begin, m_end, m_simpleDuration, m_repetitions);
     
     if (percentage <= 1.0 || connectedToTimer())
         handleTimerEvent(percentage);
index 8d3d6c3..3fbdbba 100644 (file)
@@ -1,6 +1,7 @@
 /*
     Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org>
                   2004, 2005, 2006 Rob Buis <buis@kde.org>
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
     This file is part of the KDE project
 
@@ -85,6 +86,8 @@ namespace WebCore {
         // 'SVGAnimationElement' functions
         SVGElement* targetElement() const;
         
+        virtual bool hasValidTarget() const;
+        
         virtual bool isValid() const { return SVGTests::isValid(); }
 
         double getEndTime() const;
@@ -132,7 +135,8 @@ namespace WebCore {
         
         virtual bool updateCurrentValue(double timePercentage) = 0;
         virtual bool handleStartCondition() = 0;
-        virtual void handleEndCondition() = 0;
+        virtual void updateLastValueWithCurrent() { };
+        virtual void resetValues() { }
 
         ANIMATED_PROPERTY_FORWARD_DECLARATIONS(SVGExternalResourcesRequired, bool, ExternalResourcesRequired, externalResourcesRequired)
 
@@ -161,7 +165,7 @@ namespace WebCore {
         double m_end;
         double m_begin;
 
-        double m_repeations;
+        double m_repetitions;
         double m_repeatCount;
 
         RefPtr<SVGStringList> m_values;
index 7bba2ef..d281de7 100644 (file)
@@ -63,16 +63,16 @@ double SVGPathElement::getTotalLength()
     return toPathData().length();
 }
 
-FloatPoint SVGPathElement::getPointAtLength(double distance)
+FloatPoint SVGPathElement::getPointAtLength(double length)
 {
     // FIXME: this may wish to use the pathSegList instead of the pathdata if that's cheaper to build (or cached)
     bool ok = false;
-    return toPathData().pointAtLength(distance, ok);
+    return toPathData().pointAtLength(length, ok);
 }
 
-unsigned long SVGPathElement::getPathSegAtLength(double)
+unsigned long SVGPathElement::getPathSegAtLength(double length)
 {
-    return 0;
+    return pathSegList()->getPathSegAtLength(length);
 }
 
 SVGPathSegClosePath* SVGPathElement::createSVGPathSegClosePath()
@@ -274,9 +274,12 @@ void SVGPathElement::parseMappedAttribute(MappedAttribute* attr)
         if (!parseSVG(attr->value(), true))
             pathSegList()->clear(ec);
     } else {
-        if (SVGTests::parseMappedAttribute(attr)) return;
-        if (SVGLangSpace::parseMappedAttribute(attr)) return;
-        if (SVGExternalResourcesRequired::parseMappedAttribute(attr)) return;
+        if (SVGTests::parseMappedAttribute(attr))
+            return;
+        if (SVGLangSpace::parseMappedAttribute(attr))
+            return;
+        if (SVGExternalResourcesRequired::parseMappedAttribute(attr))
+            return;
         SVGStyledTransformableElement::parseMappedAttribute(attr);
     }
 }
@@ -307,48 +310,9 @@ SVGPathSegList* SVGPathElement::animatedNormalizedPathSegList() const
     return 0;
 }
 
-// FIXME: This probably belongs on SVGPathSegList instead. -- ecs 12/5/05
 Path SVGPathElement::toPathData() const
 {
-    Path pathData;
-    // TODO : also support non-normalized mode, at least in dom structure
-    int len = pathSegList()->numberOfItems();
-    if (len < 1)
-        return pathData;
-
-    ExceptionCode ec = 0;
-    for (int i = 0; i < len; ++i) {
-        SVGPathSeg* p = pathSegList()->getItem(i, ec).get();;
-        switch (p->pathSegType())
-        {
-            case SVGPathSeg::PATHSEG_MOVETO_ABS:
-            {
-                SVGPathSegMovetoAbs* moveTo = static_cast<SVGPathSegMovetoAbs* >(p);
-                pathData.moveTo(FloatPoint(moveTo->x(), moveTo->y()));
-                break;
-            }
-            case SVGPathSeg::PATHSEG_LINETO_ABS:
-            {
-                SVGPathSegLinetoAbs* lineTo = static_cast<SVGPathSegLinetoAbs* >(p);
-                pathData.addLineTo(FloatPoint(lineTo->x(), lineTo->y()));
-                break;
-            }
-            case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
-            {
-                SVGPathSegCurvetoCubicAbs* curveTo = static_cast<SVGPathSegCurvetoCubicAbs* >(p);
-                pathData.addBezierCurveTo(FloatPoint(curveTo->x1(), curveTo->y1()),
-                                 FloatPoint(curveTo->x2(), curveTo->y2()),
-                                 FloatPoint(curveTo->x(), curveTo->y()));
-                break;
-            }
-            case SVGPathSeg::PATHSEG_CLOSEPATH:
-                pathData.closeSubpath();
-            default:
-                break;
-        }
-    }
-
-    return pathData;
+    return pathSegList()->toPathData();
 }
 
 }
index 639c54c..7a6fe4e 100644 (file)
@@ -1,9 +1,10 @@
 /*
     Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
                   2004, 2005 Rob Buis <buis@kde.org>
-
-    This file is part of the KDE project
-
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+    This file is part of the WebKit project
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
     License as published by the Free Software Foundation; either
 #include "config.h"
 
 #ifdef SVG_SUPPORT
-
 #include "SVGPathSegList.h"
 
+#include "FloatPoint.h"
+#include "Path.h"
+#include "PathTraversalState.h"
+#include "SVGPathSegMoveTo.h"
+#include "SVGPathSegLineTo.h"
+#include "SVGPathSegCurvetoCubic.h"
+
 namespace WebCore {
 
 SVGPathSegList::SVGPathSegList(const SVGStyledElement* context)
@@ -43,6 +50,97 @@ const SVGStyledElement* SVGPathSegList::context() const
     return m_context;
 }
 
+unsigned SVGPathSegList::getPathSegAtLength(double)
+{
+    // FIXME : to be useful this will need to support non-normalized SVGPathSegLists
+    ExceptionCode ec = 0;
+    int len = numberOfItems();
+    // FIXME: Eventually this will likely move to a "path applier"-like model, until then PathTraversalState is less useful as we could just use locals
+    PathTraversalState traversalState(PathTraversalState::TraversalSegmentAtLength);
+    for (int i = 0; i < len; ++i) {
+        SVGPathSeg* segment = getItem(i, ec).get();
+        float segmentLength = 0;
+        switch (segment->pathSegType()) {
+        case SVGPathSeg::PATHSEG_MOVETO_ABS:
+        {
+            SVGPathSegMovetoAbs* moveTo = static_cast<SVGPathSegMovetoAbs*>(segment);
+            segmentLength = traversalState.moveTo(FloatPoint(moveTo->x(), moveTo->y()));
+            break;
+        }
+        case SVGPathSeg::PATHSEG_LINETO_ABS:
+        {
+            SVGPathSegLinetoAbs* lineTo = static_cast<SVGPathSegLinetoAbs*>(segment);
+            segmentLength = traversalState.lineTo(FloatPoint(lineTo->x(), lineTo->y()));
+            break;
+        }
+        case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
+        {
+            SVGPathSegCurvetoCubicAbs* curveTo = static_cast<SVGPathSegCurvetoCubicAbs*>(segment);
+            segmentLength = traversalState.cubicBezierTo(FloatPoint(curveTo->x1(), curveTo->y1()),
+                                      FloatPoint(curveTo->x2(), curveTo->y2()),
+                                      FloatPoint(curveTo->x(), curveTo->y()));
+            break;
+        }
+        case SVGPathSeg::PATHSEG_CLOSEPATH:
+            segmentLength = traversalState.closeSubpath();
+            break;
+        default:
+            ASSERT(false); // FIXME: This only works with normalized/processed path data.
+            break;
+        }
+        traversalState.m_totalLength += segmentLength;
+        if ((traversalState.m_action == PathTraversalState::TraversalSegmentAtLength)
+            && (traversalState.m_totalLength > traversalState.m_desiredLength)) {
+            return traversalState.m_segmentIndex;
+        }
+        traversalState.m_segmentIndex++;
+    }
+    
+    return 0; // The SVG spec is unclear as to what to return when the distance is not on the path    
+}
+
+Path SVGPathSegList::toPathData()
+{
+    // FIXME : This should also support non-normalized PathSegLists
+    Path pathData;
+    ExceptionCode ec = 0;
+    int len = numberOfItems();
+    for (int i = 0; i < len; ++i) {
+        SVGPathSeg* segment = getItem(i, ec).get();;
+        switch (segment->pathSegType())
+        {
+            case SVGPathSeg::PATHSEG_MOVETO_ABS:
+            {
+                SVGPathSegMovetoAbs* moveTo = static_cast<SVGPathSegMovetoAbs*>(segment);
+                pathData.moveTo(FloatPoint(moveTo->x(), moveTo->y()));
+                break;
+            }
+            case SVGPathSeg::PATHSEG_LINETO_ABS:
+            {
+                SVGPathSegLinetoAbs* lineTo = static_cast<SVGPathSegLinetoAbs*>(segment);
+                pathData.addLineTo(FloatPoint(lineTo->x(), lineTo->y()));
+                break;
+            }
+            case SVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
+            {
+                SVGPathSegCurvetoCubicAbs* curveTo = static_cast<SVGPathSegCurvetoCubicAbs*>(segment);
+                pathData.addBezierCurveTo(FloatPoint(curveTo->x1(), curveTo->y1()),
+                                          FloatPoint(curveTo->x2(), curveTo->y2()),
+                                          FloatPoint(curveTo->x(), curveTo->y()));
+                break;
+            }
+            case SVGPathSeg::PATHSEG_CLOSEPATH:
+                pathData.closeSubpath();
+                break;
+            default:
+                ASSERT(false); // FIXME: This only works with normalized/processed path data.
+                break;
+        }
+    }
+    
+    return pathData;
+}
+
 }
 
 #endif // SVG_SUPPORT
index 2b759a2..41d4237 100644 (file)
@@ -1,8 +1,7 @@
 /*
-    Copyright (C) 2004, 2005, 2006 Nikolas Zimmermann <zimmermann@kde.org>
-                  2004, 2005 Rob Buis <buis@kde.org>
+    Copyright (C) 2007 Eric Seidel <eric@webkit.org>
 
-    This file is part of the KDE project
+    This file is part of the WebKit project
 
     This library is free software; you can redistribute it and/or
     modify it under the terms of the GNU Library General Public
@@ -30,6 +29,8 @@
 
 namespace WebCore
 {
+    class Path;
+    
     class SVGPathSegList : public SVGList<RefPtr<SVGPathSeg> >
     {
     public:
@@ -37,6 +38,9 @@ namespace WebCore
         virtual ~SVGPathSegList();
 
         const SVGStyledElement* context() const;
+        
+        unsigned getPathSegAtLength(double);
+        Path toPathData();
 
     private:
         const SVGStyledElement* m_context;
index 74f2f7e..8f096c0 100644 (file)
@@ -4,7 +4,7 @@ a
 #altGlyphItem
 animate
 animateColor
-#animateMotion
+animateMotion
 animateTransform
 circle
 clipPath
index 2bee38a..33e7e07 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2003, 2006 Apple Computer, Inc.  All rights reserved.
  *                     2006 Rob Buis <buis@kde.org>
+ * Copyright (C) 2007 Eric Seidel <eric@webkit.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,6 +44,7 @@ void pathLengthApplierFunction(void* info, const PathElement* element)
     PathTraversalState& traversalState = *static_cast<PathTraversalState*>(info);
     if (traversalState.m_success)
         return;
+    traversalState.m_previous = traversalState.m_current;
     FloatPoint* points = element->points;
     float segmentLength = 0.0f;
     switch (element->type) {
@@ -67,6 +69,13 @@ void pathLengthApplierFunction(void* info, const PathElement* element)
      && (traversalState.m_totalLength > traversalState.m_desiredLength)) {
         // FIXME: Need to actually find the exact point and change m_current
         traversalState.m_success = true;
+    } else if ((traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength)
+           && (traversalState.m_totalLength > traversalState.m_desiredLength)) {
+        FloatSize change = traversalState.m_previous - traversalState.m_current;
+        // tangent slope = -1/slope = -1/yChange/xChange = -xChange/yChange; arc-tangent converts a slope into an angle
+        static float rad2deg = 360 / 2 * M_PI;
+        traversalState.m_normalAngle = (change.height() == 0) ? 0 : (atan2(-change.width(), change.height()) * rad2deg);
+        traversalState.m_success = true;
     }
 }
 
@@ -86,6 +95,15 @@ FloatPoint Path::pointAtLength(float length, bool& ok)
     return traversalState.m_current;
 }
 
+float Path::normalAngleAtLength(float length, bool& ok)
+{
+    PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength);
+    traversalState.m_desiredLength = length;
+    apply(&traversalState, pathLengthApplierFunction);
+    ok = traversalState.m_success;
+    return traversalState.m_normalAngle;
+}
+
 Path Path::createRoundedRectangle(const FloatRect& rectangle, const FloatSize& roundingRadii)
 {
     Path path;
index c8f8667..43b5279 100644 (file)
@@ -77,6 +77,7 @@ namespace WebCore {
         
         float length();
         FloatPoint pointAtLength(float length, bool& ok);
+        float normalAngleAtLength(float length, bool& ok);
 
         void clear();
         bool isEmpty() const;
index 83e995c..8aee22a 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the WebKit open source project.
  *
- * Copyright (C) 2006 Eric Seidel (eric@webkit.org)
+ * Copyright (C) 2006, 2007 Eric Seidel (eric@webkit.org)
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -108,8 +108,14 @@ struct CubicBezier {
     FloatPoint end;
 };
 
+// FIXME: This function is possibly very slow due to the ifs required for proper path measuring
+// A simple speed-up would be to use an additional boolean template parameter to control whether
+// to use the "fast" version of this function with no PathTraversalState updating, vs. the slow
+// version which does update the PathTraversalState.  We'll have to shark it to see if that's necessary.
+// Another check which is possible up-front (to send us down the fast path) would be to check if
+// approximateDistance() + current total distance > desired distance
 template<class CurveType>
-static float curveLength(CurveType curve)
+static float curveLength(PathTraversalState& traversalState, CurveType curve)
 {
     Vector<CurveType> curveStack;
     curveStack.append(curve);
@@ -124,6 +130,13 @@ static float curveLength(CurveType curve)
             curveStack.append(right);
         } else {
             totalLength += length;
+            if (traversalState.m_action == PathTraversalState::TraversalPointAtLength
+             || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) {
+                traversalState.m_previous = curve.start;
+                traversalState.m_current = curve.end;
+                if (traversalState.m_totalLength + totalLength > traversalState.m_desiredLength)
+                    return totalLength;
+            }
             curve = curveStack.last();
             curveStack.removeLast();
         }
@@ -138,6 +151,7 @@ PathTraversalState::PathTraversalState(PathTraversalAction action)
     , m_totalLength(0.0f)
     , m_segmentIndex(0)
     , m_desiredLength(0.0f)
+    , m_normalAngle(0.0f)
 {
 }
 
@@ -163,7 +177,7 @@ float PathTraversalState::lineTo(const FloatPoint& point)
 
 float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const FloatPoint& newEnd)
 {
-    float distance = curveLength<QuadraticBezier>(QuadraticBezier(m_current, newControl, newEnd));
+    float distance = curveLength<QuadraticBezier>(*this, QuadraticBezier(m_current, newControl, newEnd));
     m_control1 = newControl;
     m_control2 = m_current = newEnd;
     return distance;
@@ -171,7 +185,7 @@ float PathTraversalState::quadraticBezierTo(const FloatPoint& newControl, const
 
 float PathTraversalState::cubicBezierTo(const FloatPoint& newControl1, const FloatPoint& newControl2, const FloatPoint& newEnd)
 {
-    float distance = curveLength<CubicBezier>(CubicBezier(m_current, newControl1, newControl2, newEnd));
+    float distance = curveLength<CubicBezier>(*this, CubicBezier(m_current, newControl1, newControl2, newEnd));
     m_control1 = m_current = newEnd;
     m_control2 = newControl2;
     return distance;
index 98cfc4f..b8bbaf8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2006 Eric Seidel <eric@webkit.org>
+ * Copyright (C) 2006, 2007 Eric Seidel <eric@webkit.org>
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,7 +38,8 @@ namespace WebCore {
         enum PathTraversalAction {
             TraversalTotalLength,
             TraversalPointAtLength,
-            TraversalSegmentAtLength, // not yet implemented
+            TraversalSegmentAtLength,
+            TraversalNormalAngleAtLength,
             TraversalPointAndAnglesForOffsets // not yet implemented
         };
         
@@ -60,9 +61,13 @@ namespace WebCore {
         FloatPoint m_control2;
         
         float m_totalLength;
-        unsigned m_segmentIndex; // for segment finding (not implemented)
+        unsigned m_segmentIndex;
         float m_desiredLength;
         
+        // For normal calculations
+        FloatPoint m_previous;
+        float m_normalAngle; // degrees
+        
         // FIXME: for (non-implemented) text-on-path layout
         Vector<float> m_offsets;
         Vector< std::pair<FloatPoint, float> > m_pointAndAngles;