Adding scale constraint.
diff --git a/Source/Actor.cpp b/Source/Actor.cpp
index da18315..6b01296 100644
--- a/Source/Actor.cpp
+++ b/Source/Actor.cpp
@@ -3,6 +3,7 @@
 #include "ActorRootBone.hpp"
 #include "ActorIKTarget.hpp"
 #include "ActorIKConstraint.hpp"
+#include "ActorScaleConstraint.hpp"
 #include "ActorEvent.hpp"
 #include "ActorNodeSolo.hpp"
 #include "CustomProperty.hpp"
@@ -457,6 +458,9 @@
 			case BlockType::ActorIKConstraint:
 				component = ActorIKConstraint::read(this, componentBlock);
 				break;
+			case BlockType::ActorScaleConstraint:
+				component = ActorScaleConstraint::read(this, componentBlock);
+				break;
 			default:
 				// Not handled/expected block.
 				break;
diff --git a/Source/ActorAxisConstraint.cpp b/Source/ActorAxisConstraint.cpp
new file mode 100644
index 0000000..f3bd2d6
--- /dev/null
+++ b/Source/ActorAxisConstraint.cpp
@@ -0,0 +1,87 @@
+#include "ActorAxisConstraint.hpp"
+#include "Actor.hpp"
+#include <cmath>
+
+using namespace nima;
+
+ActorAxisConstraint::ActorAxisConstraint(Actor* actor, ComponentType type) : Base(actor, type),
+        m_CopyX(0.0f),
+        m_CopyY(0.0f),
+        m_ScaleX(1.0f),
+        m_ScaleY(1.0f),
+        m_EnableMinX(false),
+        m_EnableMinY(false),
+        m_EnableMaxX(false),
+        m_EnableMaxY(false),
+        m_MaxX(0.0f),
+        m_MaxY(0.0f),
+        m_MinX(0.0f),
+        m_MinY(0.0f),
+        m_Offset(false),
+        m_SourceSpace(TransformSpace::World),
+        m_DestSpace(TransformSpace::World),
+        m_MinMaxSpace(TransformSpace::World)
+{
+}
+
+void ActorAxisConstraint::copy(ActorAxisConstraint* constraint, Actor* resetActor)
+{
+    Base::copy(constraint, resetActor);
+
+    m_CopyX = constraint->m_CopyX;
+    m_CopyY = constraint->m_CopyY;
+    m_ScaleX = constraint->m_ScaleX;
+    m_ScaleY = constraint->m_ScaleY;
+    m_EnableMinX = constraint->m_EnableMinX;
+    m_EnableMinY = constraint->m_EnableMinY;
+    m_EnableMaxX = constraint->m_EnableMaxX;
+    m_EnableMaxY = constraint->m_EnableMaxY;
+    m_MinX = constraint->m_MinX;
+    m_MinY = constraint->m_MinY;
+    m_MaxX = constraint->m_MaxX;
+    m_MaxY = constraint->m_MaxY;
+    m_Offset = constraint->m_Offset;
+    m_SourceSpace = constraint->m_SourceSpace;
+    m_DestSpace = constraint->m_DestSpace;
+    m_MinMaxSpace = constraint->m_MinMaxSpace;
+}
+
+ActorAxisConstraint* ActorAxisConstraint::read(Actor* actor, BlockReader* reader, ActorAxisConstraint* constraint)
+{
+    Base::read(actor, reader, constraint);
+
+    // X Axis
+    if((constraint->m_CopyX = reader->readByte() == 1))
+    {
+        constraint->m_ScaleX = reader->readFloat();
+    }
+    if((constraint->m_EnableMinX = reader->readByte() == 1))
+    {
+        constraint->m_MinX = reader->readFloat();
+    }
+    if((constraint->m_EnableMaxX = reader->readByte() == 1))
+    {
+        constraint->m_MaxX = reader->readFloat();
+    }
+
+    // Y Axis
+    if((constraint->m_CopyY = reader->readByte() == 1))
+    {
+        constraint->m_ScaleY = reader->readFloat();
+    }
+    if((constraint->m_EnableMinY = reader->readByte() == 1))
+    {
+        constraint->m_MinY = reader->readFloat();
+    }
+    if((constraint->m_EnableMaxY = reader->readByte() == 1))
+    {
+        constraint->m_MaxY = reader->readFloat();
+    }
+
+    constraint->m_Offset = reader->readByte() == 1;
+    constraint->m_SourceSpace = (TransformSpace)reader->readByte();
+    constraint->m_DestSpace = (TransformSpace)reader->readByte();
+    constraint->m_MinMaxSpace = (TransformSpace)reader->readByte();
+
+    return constraint;
+}
\ No newline at end of file
diff --git a/Source/ActorAxisConstraint.hpp b/Source/ActorAxisConstraint.hpp
new file mode 100644
index 0000000..577bd6b
--- /dev/null
+++ b/Source/ActorAxisConstraint.hpp
@@ -0,0 +1,45 @@
+#ifndef _NIMA_ACTORAXISCONSTRAINT_HPP_
+#define _NIMA_ACTORAXISCONSTRAINT_HPP_
+
+#include "ActorTargetedConstraint.hpp"
+#include "ActorBone.hpp"
+#include "TransformSpace.hpp"
+#include "nima/Mat2D.hpp"
+#include "nima/TransformComponents.hpp"
+#include <vector>
+
+namespace nima
+{
+	class Actor;
+
+    class ActorAxisConstraint : public ActorTargetedConstraint
+	{
+        typedef ActorTargetedConstraint Base;
+
+		protected:
+			bool m_CopyX;
+            bool m_CopyY;
+            float m_ScaleX;
+            float m_ScaleY;
+            bool m_EnableMinX;
+            bool m_EnableMinY;
+            bool m_EnableMaxX;
+            bool m_EnableMaxY;
+            float m_MaxX;
+            float m_MaxY;
+            float m_MinX;
+            float m_MinY;
+            bool m_Offset;
+            TransformSpace m_SourceSpace;
+            TransformSpace m_DestSpace;
+            TransformSpace m_MinMaxSpace;
+
+        public:
+            ActorAxisConstraint(Actor* actor, ComponentType type);
+			void copy(ActorAxisConstraint* node, Actor* resetActor);
+
+			static ActorAxisConstraint* read(Actor* actor, BlockReader* reader, ActorAxisConstraint* node);
+    };
+}
+
+#endif
\ No newline at end of file
diff --git a/Source/ActorConstraint.cpp b/Source/ActorConstraint.cpp
index 8f861c2..44e297f 100644
--- a/Source/ActorConstraint.cpp
+++ b/Source/ActorConstraint.cpp
@@ -30,6 +30,11 @@
     m_Parent->markTransformDirty();
 }
 
+void ActorConstraint::onDirty(unsigned char dirt)
+{
+    m_Parent->markTransformDirty();
+}
+
 void ActorConstraint::resolveComponentIndices(ActorComponent** components)
 {
     Base::resolveComponentIndices(components);
diff --git a/Source/ActorConstraint.hpp b/Source/ActorConstraint.hpp
index 62a6527..853af21 100644
--- a/Source/ActorConstraint.hpp
+++ b/Source/ActorConstraint.hpp
@@ -19,7 +19,8 @@
             bool isEnabled() const;
             float strength() const;
             void strength(float value);
-
+            
+            void onDirty(unsigned char dirt) override;
             virtual void constrain(ActorNode* node) = 0;
             void resolveComponentIndices(ActorComponent** components) override;
             void copy(ActorConstraint* constraint, Actor* resetActor);
diff --git a/Source/ActorIKConstraint.hpp b/Source/ActorIKConstraint.hpp
index fa53f5a..5d08ca0 100644
--- a/Source/ActorIKConstraint.hpp
+++ b/Source/ActorIKConstraint.hpp
@@ -10,7 +10,6 @@
 namespace nima
 {
 	class Actor;
-    class ActorIKConstraint;
 
     class ActorIKConstraint : public ActorTargetedConstraint
 	{
diff --git a/Source/ActorScaleConstraint.cpp b/Source/ActorScaleConstraint.cpp
new file mode 100644
index 0000000..f82b36f
--- /dev/null
+++ b/Source/ActorScaleConstraint.cpp
@@ -0,0 +1,147 @@
+#include "ActorScaleConstraint.hpp"
+
+using namespace nima;
+
+ActorScaleConstraint::ActorScaleConstraint() : Base(nullptr, ComponentType::ActorScaleConstraint)
+{
+
+}
+
+ActorComponent* ActorScaleConstraint::makeInstance(Actor* resetActor)
+{
+	ActorScaleConstraint* instanceNode = new ActorScaleConstraint();
+	instanceNode->copy(this, resetActor);
+	return instanceNode;
+}
+
+ActorScaleConstraint* ActorScaleConstraint::read(Actor* actor, BlockReader* reader, ActorScaleConstraint* constraint)
+{
+    if(constraint == nullptr)
+    {
+        constraint = new ActorScaleConstraint();
+    }
+
+    Base::read(actor, reader, constraint);
+
+    return constraint;
+}
+
+void ActorScaleConstraint::constrain(ActorNode* node)
+{
+    // TODO: Should ActorTargetedConstraint just store targets as nodes?
+    ActorNode* target = static_cast<ActorNode*>(m_Target);
+    ActorNode* grandParent = m_Parent->parent();
+
+    const Mat2D& transformA = m_Parent->worldTransform();
+    Mat2D transformB;
+    Mat2D::decompose(m_ComponentsA, transformA);
+    if(target == nullptr)
+    {
+        Mat2D::copy(transformB, transformA);
+        TransformComponents::copy(m_ComponentsB, m_ComponentsA);
+    }
+    else
+    {
+        Mat2D::copy(transformB, target->worldTransform());
+        if(m_SourceSpace == TransformSpace::Local)
+        {
+            ActorNode* sourceGrandParent = target->parent();
+            if(sourceGrandParent != nullptr)
+            {
+                Mat2D inverse;
+                if(!Mat2D::invert(inverse, sourceGrandParent->worldTransform()))
+                {
+                    return;
+                }
+                Mat2D::multiply(transformB, inverse, transformB);
+            }
+        }
+        Mat2D::decompose(m_ComponentsB, transformB);
+
+        if(!m_CopyX)
+        {
+            m_ComponentsB.scaleX(m_DestSpace == TransformSpace::Local ? 1.0f : m_ComponentsA.scaleX());
+        }
+        else
+        {
+            m_ComponentsB.scaleX(m_ComponentsB.scaleX() * m_ScaleX);
+            if(m_Offset)
+            {
+                m_ComponentsB.scaleX(m_ComponentsB.scaleX() * m_Parent->scaleX());
+            }
+        }
+
+        if(!m_CopyY)
+        {
+            m_ComponentsB.scaleY(m_DestSpace == TransformSpace::Local ? 0.0f : m_ComponentsA.scaleY());
+        }
+        else
+        {
+            m_ComponentsB.scaleY(m_ComponentsB.scaleY() * m_ScaleY);
+            if(m_Offset)
+            {
+                m_ComponentsB.scaleY(m_ComponentsB.scaleY() * m_Parent->scaleY());
+            }
+        }
+
+        if(m_DestSpace == TransformSpace::Local)
+        {
+            // Destination space is in parent transform coordinates.
+            // Recompose the parent local transform and get it in world, then decompose the world for interpolation.
+            if(grandParent != nullptr)
+            {
+                Mat2D::compose(transformB, m_ComponentsB);
+                Mat2D::multiply(transformB, grandParent->worldTransform(), transformB);
+                Mat2D::decompose(m_ComponentsB, transformB);
+            }
+        }
+    }
+
+    bool clampLocal = m_MinMaxSpace == TransformSpace::Local && grandParent != nullptr;
+    if(clampLocal)
+    {
+        // Apply min max in local space, so transform to local coordinates first.
+        Mat2D::compose(transformB, m_ComponentsB);
+        Mat2D inverse;
+        if(!Mat2D::invert(inverse, grandParent->worldTransform()))
+        {
+            return;
+        }
+        Mat2D::multiply(transformB, inverse, transformB);
+        Mat2D::decompose(m_ComponentsB, transformB);
+    }
+    if(m_EnableMaxX && m_ComponentsB.scaleX() > m_MaxX)
+    {
+        m_ComponentsB.scaleX(m_MaxX);
+    }
+    if(m_EnableMinX && m_ComponentsB.scaleX() < m_MinX)
+    {
+        m_ComponentsB.scaleX(m_MinX);
+    }
+    if(m_EnableMaxY && m_ComponentsB.scaleY() > m_MaxY)
+    {
+        m_ComponentsB.scaleY(m_MaxY);
+    }
+    if(m_EnableMinY && m_ComponentsB.scaleY() < m_MinY)
+    {
+        m_ComponentsB.scaleY(m_MinY);
+    }
+    if(clampLocal)
+    {
+        // Transform back to world.
+        Mat2D::compose(transformB, m_ComponentsB);
+        Mat2D::multiply(transformB, grandParent->worldTransform(), transformB);
+        Mat2D::decompose(m_ComponentsB, transformB);
+    }
+
+    float ti = 1.0f-m_Strength;
+
+    m_ComponentsB.rotation(m_ComponentsA.rotation());
+    m_ComponentsB.x(m_ComponentsA.x());
+    m_ComponentsB.y(m_ComponentsA.y());
+    m_ComponentsB.scaleX(m_ComponentsA.scaleX() * ti + m_ComponentsB.scaleX() * m_Strength);
+    m_ComponentsB.scaleY(m_ComponentsA.scaleY() * ti + m_ComponentsB.scaleY() * m_Strength);
+    m_ComponentsB.skew(m_ComponentsA.skew());
+
+    Mat2D::compose(m_Parent->mutableWorldTransform(), m_ComponentsB);
+}
diff --git a/Source/ActorScaleConstraint.hpp b/Source/ActorScaleConstraint.hpp
new file mode 100644
index 0000000..62dedfe
--- /dev/null
+++ b/Source/ActorScaleConstraint.hpp
@@ -0,0 +1,23 @@
+#ifndef _NIMA_ACTORSCALECONSTRAINT_HPP_
+#define _NIMA_ACTORSCALECONSTRAINT_HPP_
+
+#include "ActorAxisConstraint.hpp"
+
+namespace nima
+{
+    class ActorScaleConstraint : public ActorAxisConstraint
+    {
+        typedef ActorAxisConstraint Base;
+        private:
+            TransformComponents m_ComponentsA;
+            TransformComponents m_ComponentsB;
+
+        public:
+            ActorScaleConstraint();
+            ActorComponent* makeInstance(Actor* resetActor) override;
+            static ActorScaleConstraint* read(Actor* actor, BlockReader* reader, ActorScaleConstraint* constraint = nullptr);
+            void constrain(ActorNode* node) override;
+    };
+}
+
+#endif
\ No newline at end of file
diff --git a/Source/TransformSpace.hpp b/Source/TransformSpace.hpp
new file mode 100644
index 0000000..6b73af2
--- /dev/null
+++ b/Source/TransformSpace.hpp
@@ -0,0 +1,13 @@
+#ifndef _NIMA_TRANSFORMSPACE_HPP_
+#define _NIMA_TRANSFORMSPACE_HPP_
+
+namespace nima
+{
+     enum class TransformSpace
+    {
+        Local = 0,
+        World = 1
+    };
+}
+
+#endif
\ No newline at end of file