(* Content-type: application/mathematica *) (*** Wolfram Notebook File ***) (* http://www.wolfram.com/nb *) (* CreatedBy='Mathematica 6.0' *) (*CacheID: 234*) (* Internal cache information: NotebookFileLineBreakTest NotebookFileLineBreakTest NotebookDataPosition[ 145, 7] NotebookDataLength[ 15098, 450] NotebookOptionsPosition[ 14770, 435] NotebookOutlinePosition[ 15108, 450] CellTagsIndexPosition[ 15065, 447] WindowFrame->Normal ContainsDynamic->False*) (* Beginning of Notebook Content *) Notebook[{ Cell[TextData[{ "If you're comfortable with linear algebra (matrix mathematics), you can \ represent any arbitrary combination of 2-D scalings, skews, rotations and \ translations using standard matrix operations.\n\nThe idea is that you can \ represent a point as a column vector\n\t", Cell[BoxData[ FormBox[ RowBox[{ OverscriptBox["p", "^"], "=", RowBox[{"(", GridBox[{ {"x"}, {"y"}, {"1"} }], ")"}]}], TraditionalForm]]], ",\nand any 2-D scaling, skew, rotation or translation operation ", Cell[BoxData[ FormBox["\[ScriptCapitalS]", TraditionalForm]]], " by a 3x3 matrix with this form:\n\t", Cell[BoxData[ FormBox[ RowBox[{"\[DoubleStruckCapitalS]", " ", "=", RowBox[{"(", GridBox[{ {"a", "c", RowBox[{"t", "\[VeryThinSpace]", "x"}]}, {"b", "d", RowBox[{"t", "\[VeryThinSpace]", "y"}]}, {"0", "0", "1"} }], ")"}]}], TraditionalForm]]], ". \nThen the operation ", Cell[BoxData[ FormBox[ RowBox[{"\[ScriptCapitalS]", "(", OverscriptBox["p", "^"], ")"}], TraditionalForm]]], " is just the standard matrix-times-vector ", Cell[BoxData[ FormBox[ RowBox[{"\[DoubleStruckCapitalS]", "\[Times]", OverscriptBox["p", "^"]}], TraditionalForm]]], ":\n \t", Cell[BoxData[ FormBox[ RowBox[{ RowBox[{ RowBox[{"(", GridBox[{ {"a", "c", RowBox[{"t", "\[VeryThinSpace]", "x"}]}, {"b", "d", RowBox[{"t", "\[VeryThinSpace]", "y"}]}, {"0", "0", "1"} }], ")"}], "\[Times]", RowBox[{"(", GridBox[{ {"x"}, {"y"}, {"1"} }], ")"}]}], "=", RowBox[{"(", "\[NoBreak]", GridBox[{ { RowBox[{ RowBox[{"a", " ", "x"}], "+", RowBox[{"c", " ", "y"}], "+", RowBox[{"t", "\[VeryThinSpace]", "x"}]}]}, { RowBox[{ RowBox[{"b", " ", "x"}], "+", RowBox[{"d", " ", "y"}], "+", RowBox[{"t", "\[VeryThinSpace]", "y"}]}]}, {"1"} }, GridBoxAlignment->{ "Columns" -> {{Left}}, "ColumnsIndexed" -> {}, "Rows" -> {{Baseline}}, "RowsIndexed" -> {}}, GridBoxSpacings->{"Columns" -> { Offset[0.27999999999999997`], { Offset[0.7]}, Offset[0.27999999999999997`]}, "ColumnsIndexed" -> {}, "Rows" -> { Offset[0.2], { Offset[0.4]}, Offset[0.2]}, "RowsIndexed" -> {}}], "\[NoBreak]", ")"}]}], TraditionalForm]]], ".\nWe throw out the '1' at the bottom (which will always be 1) to get the \ transformed point.\n\nCompositions (i.e. apply one operation, then another) \ are multiplications of the corresponding matrices. If you want to transform \ by S and then T:\n", StyleBox["\tpublic var composition:Matrix = S.clone;\n\t\ composition.concat(T);\n\tfoo.transform.matrix = composition;", "Code"], "\nyou can multiply the two matrices ", Cell[BoxData[ FormBox[ RowBox[{"\[DoubleStruckCapitalT]", "\[Times]", "\[DoubleStruckCapitalS]"}], TraditionalForm]]], ":\n \t", Cell[BoxData[ FormBox[ RowBox[{ RowBox[{ RowBox[{"(", "\[NoBreak]", GridBox[{ { SubscriptBox["a", "t"], SubscriptBox["c", "t"], RowBox[{"t", "\[VeryThinSpace]", SubscriptBox["x", "t"]}]}, { SubscriptBox["b", "t"], SubscriptBox["d", "t"], RowBox[{"t", "\[VeryThinSpace]", SubscriptBox["y", "t"]}]}, {"0", "0", "1"} }, GridBoxAlignment->{ "Columns" -> {{Left}}, "ColumnsIndexed" -> {}, "Rows" -> {{Baseline}}, "RowsIndexed" -> {}}, GridBoxSpacings->{"Columns" -> { Offset[0.27999999999999997`], { Offset[0.7]}, Offset[0.27999999999999997`]}, "ColumnsIndexed" -> {}, "Rows" -> { Offset[0.2], { Offset[0.4]}, Offset[0.2]}, "RowsIndexed" -> {}}], "\[NoBreak]", ")"}], "\[Times]", RowBox[{"(", "\[NoBreak]", GridBox[{ { SubscriptBox["a", "s"], SubscriptBox["c", "s"], RowBox[{"t", "\[VeryThinSpace]", SubscriptBox["x", "s"]}]}, { SubscriptBox["b", "s"], SubscriptBox["d", "s"], RowBox[{"t", "\[VeryThinSpace]", SubscriptBox["y", "s"]}]}, {"0", "0", "1"} }, GridBoxAlignment->{ "Columns" -> {{Left}}, "ColumnsIndexed" -> {}, "Rows" -> {{Baseline}}, "RowsIndexed" -> {}}, GridBoxSpacings->{"Columns" -> { Offset[0.27999999999999997`], { Offset[0.7]}, Offset[0.27999999999999997`]}, "ColumnsIndexed" -> {}, "Rows" -> { Offset[0.2], { Offset[0.4]}, Offset[0.2]}, "RowsIndexed" -> {}}], "\[NoBreak]", ")"}]}], "=", RowBox[{"(", "\[NoBreak]", GridBox[{ { RowBox[{ RowBox[{ SubscriptBox["a", "t"], SubscriptBox["a", "s"]}], "+", RowBox[{ SubscriptBox["c", "t"], SubscriptBox["b", "s"]}]}], " ", RowBox[{ RowBox[{ SubscriptBox["a", "t"], SubscriptBox["c", "s"]}], "+", RowBox[{ SubscriptBox["c", "t"], " ", SubscriptBox["d", "s"]}]}], " ", RowBox[{ RowBox[{ SubscriptBox["a", "t"], " ", "t", "\[VeryThinSpace]", SubscriptBox["x", "s"]}], "+", RowBox[{ SubscriptBox["c", "t"], " ", "t", "\[VeryThinSpace]", SubscriptBox["y", "s"]}], "+", RowBox[{"t", "\[VeryThinSpace]", SubscriptBox["x", "t"]}]}]}, { RowBox[{ RowBox[{ SubscriptBox["b", "t"], SubscriptBox["a", "s"]}], "+", RowBox[{ SubscriptBox["d", "t"], SubscriptBox["b", "s"]}]}], " ", RowBox[{ RowBox[{ SubscriptBox["b", "t"], " ", SubscriptBox["c", "s"]}], "+", RowBox[{ SubscriptBox["d", "s"], " ", SubscriptBox["d", "t"]}]}], " ", RowBox[{ RowBox[{ SubscriptBox["b", "t"], " ", "t", "\[VeryThinSpace]", SubscriptBox["x", "s"]}], "+", RowBox[{ SubscriptBox["d", "t"], " ", "t", "\[VeryThinSpace]", SubscriptBox["y", "s"]}], "+", RowBox[{"t", "\[VeryThinSpace]", SubscriptBox["y", "t"]}]}]}, {"0", " ", "0", " ", "1"} }, GridBoxAlignment->{ "Columns" -> {{Left}}, "ColumnsIndexed" -> {}, "Rows" -> {{Baseline}}, "RowsIndexed" -> {}}, GridBoxSpacings->{"Columns" -> { Offset[0.27999999999999997`], { Offset[0.7]}, Offset[0.27999999999999997`]}, "ColumnsIndexed" -> {}, "Rows" -> { Offset[0.2], { Offset[0.4]}, Offset[0.2]}, "RowsIndexed" -> {}}], "\[NoBreak]", ")"}]}], TraditionalForm]]], "\n [Yikes! well, see my demo for an Actionscript implementation. It's not \ so bad.] Notice the order: since we want to end up applying \"S and then T\" \ we use ", Cell[BoxData[ FormBox[ RowBox[{"\[DoubleStruckCapitalT]", "\[Times]", "\[DoubleStruckCapitalS]"}], TraditionalForm]]], " -- applied to a point it gives \n \t", Cell[BoxData[ FormBox[ RowBox[{ RowBox[{ RowBox[{"(", RowBox[{"\[ScriptCapitalT]", "\[SmallCircle]", "\[ScriptCapitalS]"}], ")"}], RowBox[{"(", OverscriptBox["p", "^"], ")"}]}], "=", RowBox[{ RowBox[{ RowBox[{"(", RowBox[{ "\[DoubleStruckCapitalT]", "\[Times]", "\[DoubleStruckCapitalS]"}], ")"}], "\[Times]", OverscriptBox["p", "^"]}], "=", RowBox[{ RowBox[{"T", "\[Times]", RowBox[{"(", RowBox[{"\[DoubleStruckCapitalS]", "\[Times]", OverscriptBox["p", "^"]}], ")"}]}], "=", RowBox[{"\[ScriptCapitalT]", "(", RowBox[{"\[ScriptCapitalS]", "(", OverscriptBox["p", "^"], ")"}], ")"}]}]}]}], TraditionalForm]]], " \n as desired.\n \n An inverse transformation is the standard matrix \ inverse: the actionscript\n", StyleBox["\tpublic var sInv:Matrix = S.clone;\n\tsInv.invert();\n\t\ foo.transform.matrix = sInv;", "Code"], "\nis pronounced in math as\n \t", Cell[BoxData[ FormBox[ RowBox[{ SuperscriptBox[ RowBox[{"(", GridBox[{ {"a", "c", RowBox[{"t", "\[VeryThinSpace]", "x"}]}, {"b", "d", RowBox[{"t", "\[VeryThinSpace]", "y"}]}, {"0", "0", "1"} }], ")"}], RowBox[{"-", "1"}]], "=", RowBox[{"(", "\[NoBreak]", GridBox[{ { RowBox[{" ", RowBox[{"d", "/", "det"}]}], RowBox[{ RowBox[{"-", "c"}], "/", "det"}], RowBox[{ RowBox[{"(", RowBox[{ RowBox[{"c", " ", "t", "\[VeryThinSpace]", "y"}], "-", RowBox[{"d", " ", "t", "\[VeryThinSpace]", "x"}]}], ")"}], "/", "det"}]}, { RowBox[{ RowBox[{"-", "b"}], "/", "det"}], RowBox[{" ", RowBox[{"a", "/", "det"}]}], RowBox[{ RowBox[{"(", RowBox[{ RowBox[{"b", " ", "t", "\[VeryThinSpace]", "x"}], "-", RowBox[{"a", " ", "t", "\[VeryThinSpace]", "y"}]}], ")"}], "/", "det"}]}, {"0", "0", "1"} }, GridBoxAlignment->{ "Columns" -> {{Left}}, "ColumnsIndexed" -> {}, "Rows" -> {{Baseline}}, "RowsIndexed" -> {}}, GridBoxSpacings->{"Columns" -> { Offset[0.27999999999999997`], { Offset[0.7]}, Offset[0.27999999999999997`]}, "ColumnsIndexed" -> {}, "Rows" -> { Offset[0.2], { Offset[0.4]}, Offset[0.2]}, "RowsIndexed" -> {}}], "\[NoBreak]", ")"}]}], TraditionalForm]]], "\nwhere ", Cell[BoxData[ FormBox[ RowBox[{"det", " ", "=", " ", RowBox[{"(", RowBox[{ RowBox[{"a", " ", "d"}], "-", RowBox[{"b", " ", "c"}]}], ")"}]}], TraditionalForm]]], ". (Note in passing that the last column is the deltaPointTransform of the \ inverse matrix on the ", Cell[BoxData[ FormBox[ RowBox[{"(", RowBox[{ RowBox[{"t", "\[VeryThinSpace]", "x"}], ",", RowBox[{"t", "\[VeryThinSpace]", "y"}]}], ")"}], TraditionalForm]]], " column.)\n\nAn important point: if you look at what Actionscript actually \ does with pointTransform()s, concat()s and invert()s, the Matrix object is \ consistent with the transformation matrix\n\t", Cell[BoxData[ FormBox[ RowBox[{"\[DoubleStruckCapitalS]", " ", "=", RowBox[{"(", GridBox[{ {"a", "c", RowBox[{"t", "\[VeryThinSpace]", "x"}]}, {"b", "d", RowBox[{"t", "\[VeryThinSpace]", "y"}]}, {"0", "0", "1"} }], ")"}]}], TraditionalForm]]], "\nand not with ", StyleBox["b", FontSlant->"Italic"], " and ", StyleBox["c", FontSlant->"Italic"], " switched as it appears in the documentation. Also, if you read the \ excellent tutorial (with much better pictures) at \n\t", ButtonBox["http://www.senocular.com/flash/tutorials/transformmatrix/", BaseStyle->"Hyperlink", ButtonData->{ URL["http://www.senocular.com/flash/tutorials/transformmatrix/"], None}], ", \nplease note that you have to represent ", StyleBox["points as row vectors", FontWeight->"Bold"], " and ", StyleBox["operations as multiplying on the right", FontWeight->"Bold"], ": ", Cell[BoxData[ FormBox[ RowBox[{"\[ScriptCapitalS]", "(", OverscriptBox["p", "^"], ")"}], TraditionalForm]]], " is ", Cell[BoxData[ FormBox[ RowBox[{ OverscriptBox["p", "^"], "\[Times]", "\[DoubleStruckCapitalS]"}], TraditionalForm]]], " and ", Cell[BoxData[ FormBox[ RowBox[{"\[ScriptCapitalT]", "(", RowBox[{"\[ScriptCapitalS]", "(", OverscriptBox["p", "^"], ")"}], ")"}], TraditionalForm]]], " is ", Cell[BoxData[ FormBox[ RowBox[{ OverscriptBox["p", "^"], "\[Times]", "\[DoubleStruckCapitalS]", "\[Times]", "\[DoubleStruckCapitalT]"}], TraditionalForm]]], ". The way I've done it above is more consistent with how a tranformation \ matrix is ", ButtonBox["usually", BaseStyle->"Hyperlink", ButtonData->{ URL["http://en.wikipedia.org/wiki/Transformation_matrix#Other_kinds_of_\ transformations"], None}], " ", ButtonBox["represented", BaseStyle->"Hyperlink", ButtonData->{ URL["http://vamos.sourceforge.net/matrixfaq.htm#Q41"], None}], ", but the mathematics at senocular is consistent. (If you remember how the \ ", ButtonBox["transpose of a matrix", BaseStyle->"Hyperlink", ButtonData->{ URL["http://en.wikipedia.org/wiki/Transpose#Properties"], None}], " works, he's just working with transposed matrices." }], "Text", CellChangeTimes->{{3.3966297900968733`*^9, 3.396629812548975*^9}, { 3.396630430715719*^9, 3.396630634664414*^9}, {3.3966307063407993`*^9, 3.396630710865323*^9}, {3.39663080248201*^9, 3.396630960707801*^9}, { 3.3966309952291317`*^9, 3.396631142687811*^9}, {3.396631202216382*^9, 3.3966314418566217`*^9}, {3.396631475775092*^9, 3.396631605505777*^9}, { 3.396631699539222*^9, 3.396632058962461*^9}, {3.396632102357029*^9, 3.396632196623261*^9}, {3.396632239630574*^9, 3.3966322446296864`*^9}, { 3.3966322962502537`*^9, 3.396632414622645*^9}, {3.396632446516798*^9, 3.396632792506167*^9}, {3.3966328331664753`*^9, 3.3966328450647497`*^9}, { 3.396632875084526*^9, 3.396632939148185*^9}, {3.396633011350504*^9, 3.396633478424919*^9}, {3.396633530572453*^9, 3.3966335407128983`*^9}, { 3.39663358490709*^9, 3.396633621632859*^9}}], Cell[BoxData[ FormBox[ RowBox[{"(", GridBox[{ {"a", "c", RowBox[{"t", "\[VeryThinSpace]", "x"}]}, {"b", "d", RowBox[{"t", "\[VeryThinSpace]", "y"}]}, {"0", "0", "1"} }], ")"}], TraditionalForm]], "Text", CellChangeTimes->{{3.396634905861718*^9, 3.396634915215102*^9}, 3.396634951427985*^9}], Cell[BoxData[ FormBox[ RowBox[{"(", GridBox[{ {"a", "b", RowBox[{"t", "\[VeryThinSpace]", "x"}]}, {"c", "d", RowBox[{"t", "\[VeryThinSpace]", "y"}]}, {"0", "0", "1"} }], ")"}], TraditionalForm]], "Text"] }, WindowSize->{970, 1137}, WindowMargins->{{Automatic, 325}, {Automatic, 6}}, FrontEndVersion->"6.0 for Mac OS X x86 (32-bit) (April 20, 2007)", StyleDefinitions->"Default.nb" ] (* End of Notebook Content *) (* Internal cache information *) (*CellTagsOutline CellTagsIndex->{} *) (*CellTagsIndex CellTagsIndex->{} *) (*NotebookFileOutline Notebook[{ Cell[568, 21, 13622, 390, 1240, "Text"], Cell[14193, 413, 331, 10, 85, "Text"], Cell[14527, 425, 239, 8, 85, "Text"] } ] *) (* End of internal cache information *)