Commits

Jason McKesson  committed 0fdfb8b

Added almost all of the images for Tutorial 06.

  • Participants
  • Parent commits 558b1a7

Comments (0)

Files changed (14)

File Documents/Positioning/CoordSys2D.svg

Added
New image

File Documents/Positioning/GenCoordSys2D.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+
+local subImages = SubImage.SubImage(1, 2, 300, 300, 0, 100);
+
+local coordSize = 8;
+local dotRadius = 5;
+
+local vp = Viewport.Viewport({300, 300}, {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 10;
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"));
+	
+styleLib:AddStyle(nil, "x_axis_marker",
+	SvgWriter.Style():stroke("green"):fill("none"):stroke_width("3px"));
+	
+styleLib:AddStyle(nil, "y_axis_marker",
+	SvgWriter.Style():stroke("red"):fill("none"):stroke_width("3px"));
+	
+styleLib:AddStyle(nil, "x_axis",
+	SvgWriter.Style():stroke("green"):stroke_width("3px"):marker_end(SvgWriter.uriLocalElement("big_arrow_x_axis")));
+	
+styleLib:AddStyle(nil, "y_axis",
+	SvgWriter.Style():stroke("red"):stroke_width("3px"):marker_end(SvgWriter.uriLocalElement("big_arrow_y_axis")));
+	
+styleLib:AddStyle(nil, "wide",
+	SvgWriter.Style():stroke_width("2px"));
+	
+styleLib:AddStyle(nil, "thin",
+	SvgWriter.Style():stroke_width("1px"));
+
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_none",
+	SvgWriter.Style():fill("none"));
+
+styleLib:AddStyle(nil, "arrow_end",
+	SvgWriter.Style():marker_end(SvgWriter.uriLocalElement("arrow")));
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local bigArrowheadPath = SvgWriter.Path();
+bigArrowheadPath:M{0, 0}:L{10, 10}:L{0, 20};
+
+--Generate axes.
+local axes = {};
+
+local upperBound, lowerBound = vp:Extents();
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, lowerBound[2]);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, upperBound[2]);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(upperBound[1], (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(lowerBound[1], (upperBound[2] + lowerBound[2]) / 2);
+
+--Draw Coordinate.
+local coordsX = {};
+local coordsY = {};
+
+coordsX[#coordsX + 1] = vmath.vec2(0, 0);
+coordsX[#coordsX + 1] = vmath.vec2(1, 0);
+coordsX[#coordsX + 1] = vmath.vec2(1, 0);
+coordsX[#coordsX + 1] = vmath.vec2(2, 0);
+
+coordsY[#coordsY + 1] = vmath.vec2(2, 0);
+coordsY[#coordsY + 1] = vmath.vec2(2, 1);
+coordsY[#coordsY + 1] = vmath.vec2(2, 1);
+coordsY[#coordsY + 1] = vmath.vec2(2, 2);
+
+
+local writer = SvgWriter.SvgWriter("CoordSys2D.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({pointSize, pointSize}, {pointSize/2, pointSize/2}, "auto", true, nil, "point");
+			writer:Circle({pointSize/2, pointSize/2}, pointSize/2, {"fill_black", "black"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 8}, {10, 4}, "auto", true, nil, "arrow");
+			writer:Path(arrowheadPath, {"fill_black", "thin"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_y_axis");
+			writer:Path(bigArrowheadPath, {"y_axis_marker"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_x_axis");
+			writer:Path(bigArrowheadPath, {"x_axis_marker"});
+		writer:EndMarker();
+	writer:EndDefinitions();
+
+	--Draw the axes for the regular coord system.
+	local topAxes = subImages:Transform({1, 1}, vp:Transform(axes))
+	for i=1, #axes, 2 do
+		writer:Line(topAxes[i], topAxes[i + 1], {"black", "thin", "arrow_end"});
+	end
+	
+	local topCoordX = subImages:Transform({1, 1}, vp:Transform(coordsX))
+	for i=1, #topCoordX, 2 do
+		writer:Line(topCoordX[i], topCoordX[i + 1], {"x_axis"});
+	end
+	
+	local topCoordY = subImages:Transform({1, 1}, vp:Transform(coordsY))
+	writer:Circle(topCoordY[#topCoordY], dotRadius, {"fill_black", "black"});
+	for i=1, #topCoordY, 2 do
+		writer:Line(topCoordY[i], topCoordY[i + 1], {"y_axis"});
+	end
+	
+
+	
+	--Setup the skewed coordinate system.
+	local basisVectors = {};
+	local tempTrans = Viewport.Transform2D()
+	tempTrans:Rotate(-15);
+	basisVectors[1] = tempTrans:Matrix():Transform(vmath.vec2(1, 0));
+	tempTrans:Identity();
+	tempTrans:Rotate(15);
+	basisVectors[2] = tempTrans:Matrix():Transform(vmath.vec2(0, 1));
+	tempTrans:Identity();
+	
+	local transformMatrix = tempTrans:Matrix();
+	tempTrans:Identity();
+	transformMatrix:SetCol(1, vmath.vec3(basisVectors[1], 0.0));
+	transformMatrix:SetCol(2, vmath.vec3(basisVectors[2], 0.0));
+	
+	--Draw the axes for the skewed coord system.
+	trans2:MultMatrix(transformMatrix);
+	local botAxes = subImages:Transform({1, 2}, vp:Transform(axes))
+	for i=1, #axes, 2 do
+		writer:Line(botAxes[i], botAxes[i + 1], {"black", "thin", "arrow_end"});
+	end
+	
+	local botCoordX = subImages:Transform({1, 2}, vp:Transform(coordsX))
+	for i=1, #botCoordX, 2 do
+		writer:Line(botCoordX[i], botCoordX[i + 1], {"x_axis"});
+	end
+	
+	local botCoordY = subImages:Transform({1, 2}, vp:Transform(coordsY))
+	writer:Circle(botCoordY[#topCoordY], dotRadius, {"fill_black", "black"});
+	for i=1, #botCoordY, 2 do
+		writer:Line(botCoordY[i], botCoordY[i + 1], {"y_axis"});
+	end
+	
+writer:Close();

File Documents/Positioning/GenRotationTransform.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+
+local subImages = SubImage.SubImage(2, 1, 300, 300, 100, 0);
+
+local coordSize = 8;
+
+local vp = Viewport.Viewport({300, 300}, {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 10;
+
+local coordAxisWidth = "2px";
+local xCoordAxisColor = "green";
+local yCoordAxisColor = "red";
+local objectColor = "#AFAFFF";
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"));
+	
+styleLib:AddStyle(nil, "grid",
+	SvgWriter.Style():stroke("#CCC"):fill("none"):stroke_width("1px"));
+
+styleLib:AddStyle(nil, "axes",
+	SvgWriter.Style():stroke("black"):fill("none"):stroke_width("2px")
+		:marker_end(SvgWriter.uriLocalElement("arrow")));
+
+styleLib:AddStyle(nil, "point",
+	SvgWriter.Style():stroke(objectColor):fill(objectColor));
+	
+styleLib:AddStyle(nil, "triangle",
+	SvgWriter.Style():stroke(objectColor):stroke_width("2px"):fill("none")
+		:marker(SvgWriter.uriLocalElement("m_point")));
+	
+styleLib:AddStyle(nil, "x_axis",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+		:marker_end(SvgWriter.uriLocalElement("big_arrow_x_axis")));
+	
+styleLib:AddStyle(nil, "y_axis",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+		:marker_end(SvgWriter.uriLocalElement("big_arrow_y_axis")));
+	
+styleLib:AddStyle(nil, "x_axis_marker",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "y_axis_marker",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "arrow_end",
+	SvgWriter.Style():marker_end(SvgWriter.uriLocalElement("arrow")));
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local bigArrowheadPath = SvgWriter.Path();
+bigArrowheadPath:M{0, 0}:L{10, 10}:L{0, 20};
+
+--Generate grid.
+local upperBound, lowerBound = vp:Extents();
+
+local gridPath = SvgWriter.Path();
+
+--Vertical lines.
+for i = lowerBound[1], upperBound[1] do
+	local points =
+	{
+		vmath.vec2(i, lowerBound[2]);
+		vmath.vec2(i, upperBound[2]);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):V(points[2][2]);
+end
+
+--Horizontal lines.
+for i = lowerBound[2], upperBound[2] do
+	local points =
+	{
+		vmath.vec2(lowerBound[1], i);
+		vmath.vec2(upperBound[1], i);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):H(points[2][1]);
+end
+
+local axesPath = SvgWriter.Path();
+
+local axes = {}
+
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, lowerBound[2]);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, upperBound[2]);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(upperBound[1], (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(lowerBound[1], (upperBound[2] + lowerBound[2]) / 2);
+
+axes = vp:Transform(axes)
+
+--Draw coodinate axes and objects.
+local coordAxes =
+{
+	vmath.vec2(0, 0),
+	vmath.vec2(1, 0),
+	vmath.vec2(0, 1),
+}
+
+local triangle =
+{
+	vmath.vec2(0.5, 2.5),
+	vmath.vec2(2.0, -1),
+	vmath.vec2(-3, -0.5),
+}
+
+local writer = SvgWriter.SvgWriter("RotationTransform.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({pointSize, pointSize}, {pointSize/2, pointSize/2}, "auto", true, nil, "m_point");
+			writer:Circle({pointSize/2, pointSize/2}, pointSize/2, {"point"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 8}, {10, 4}, "auto", true, nil, "arrow");
+			writer:Path(arrowheadPath, {"fill_black", "thin"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_y_axis");
+			writer:Path(bigArrowheadPath, {"y_axis_marker"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_x_axis");
+			writer:Path(bigArrowheadPath, {"x_axis_marker"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_axes");
+			writer:Path(gridPath, {"grid"});
+			for i=1, #axes, 2 do
+				writer:Line(axes[i], axes[i+1], {"axes"});
+			end
+		writer:EndGroup();
+	writer:EndDefinitions();
+
+	--Left side.
+	writer:Use("g_axes", subImages:Offset(1, 1), subImages:SubSize());
+	
+	local origCoordAxes = subImages:Transform({1, 1}, vp:Transform(coordAxes))
+	writer:Line(origCoordAxes[1], origCoordAxes[2], {"x_axis"});
+	writer:Line(origCoordAxes[1], origCoordAxes[3], {"y_axis"});
+
+	local origTriangle = subImages:Transform({1, 1}, vp:Transform(triangle))
+	writer:Polygon(origTriangle, {"triangle"});
+
+	--Right side.
+	writer:Use("g_axes", subImages:Offset(2, 1), subImages:SubSize());
+
+	trans2:Rotate(75);
+	local newCoordAxes = subImages:Transform({2, 1}, vp:Transform(coordAxes))
+	writer:Line(newCoordAxes[1], newCoordAxes[2], {"x_axis"});
+	writer:Line(newCoordAxes[1], newCoordAxes[3], {"y_axis"});
+
+	local newTriangle = subImages:Transform({2, 1}, vp:Transform(triangle))
+	writer:Polygon(newTriangle, {"triangle"});
+	
+writer:Close();

File Documents/Positioning/GenScaleTransform.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+
+local subImages = SubImage.SubImage(2, 1, 300, 300, 100, 0);
+
+local coordSize = 8;
+
+local vp = Viewport.Viewport({300, 300}, {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 10;
+
+local coordAxisWidth = "2px";
+local xCoordAxisColor = "green";
+local yCoordAxisColor = "red";
+local objectColor = "#AFAFFF";
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"));
+	
+styleLib:AddStyle(nil, "grid",
+	SvgWriter.Style():stroke("#CCC"):fill("none"):stroke_width("1px"));
+
+styleLib:AddStyle(nil, "axes",
+	SvgWriter.Style():stroke("black"):fill("none"):stroke_width("2px")
+		:marker_end(SvgWriter.uriLocalElement("arrow")));
+
+styleLib:AddStyle(nil, "point",
+	SvgWriter.Style():stroke(objectColor):fill(objectColor));
+	
+styleLib:AddStyle(nil, "triangle",
+	SvgWriter.Style():stroke(objectColor):stroke_width("2px"):fill("none")
+		:marker(SvgWriter.uriLocalElement("m_point")));
+	
+styleLib:AddStyle(nil, "x_axis",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+		:marker_end(SvgWriter.uriLocalElement("big_arrow_x_axis")));
+	
+styleLib:AddStyle(nil, "y_axis",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+		:marker_end(SvgWriter.uriLocalElement("big_arrow_y_axis")));
+	
+styleLib:AddStyle(nil, "x_axis_marker",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "y_axis_marker",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "arrow_end",
+	SvgWriter.Style():marker_end(SvgWriter.uriLocalElement("arrow")));
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local bigArrowheadPath = SvgWriter.Path();
+bigArrowheadPath:M{0, 0}:L{10, 10}:L{0, 20};
+
+--Generate grid.
+local upperBound, lowerBound = vp:Extents();
+
+local gridPath = SvgWriter.Path();
+
+--Vertical lines.
+for i = lowerBound[1], upperBound[1] do
+	local points =
+	{
+		vmath.vec2(i, lowerBound[2]);
+		vmath.vec2(i, upperBound[2]);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):V(points[2][2]);
+end
+
+--Horizontal lines.
+for i = lowerBound[2], upperBound[2] do
+	local points =
+	{
+		vmath.vec2(lowerBound[1], i);
+		vmath.vec2(upperBound[1], i);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):H(points[2][1]);
+end
+
+local axesPath = SvgWriter.Path();
+
+local axes = {}
+
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, lowerBound[2]);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, upperBound[2]);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(upperBound[1], (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(lowerBound[1], (upperBound[2] + lowerBound[2]) / 2);
+
+axes = vp:Transform(axes)
+
+--Draw coodinate axes and objects.
+local coordAxes =
+{
+	vmath.vec2(0, 0),
+	vmath.vec2(1, 0),
+	vmath.vec2(0, 1),
+}
+
+local triangle =
+{
+	vmath.vec2(0.5, 2.5),
+	vmath.vec2(2.0, -1),
+	vmath.vec2(-3, -0.5),
+}
+
+
+
+
+local writer = SvgWriter.SvgWriter("ScaleTransform.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({pointSize, pointSize}, {pointSize/2, pointSize/2}, "auto", true, nil, "m_point");
+			writer:Circle({pointSize/2, pointSize/2}, pointSize/2, {"point"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 8}, {10, 4}, "auto", true, nil, "arrow");
+			writer:Path(arrowheadPath, {"fill_black", "thin"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_y_axis");
+			writer:Path(bigArrowheadPath, {"y_axis_marker"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_x_axis");
+			writer:Path(bigArrowheadPath, {"x_axis_marker"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_axes");
+			writer:Path(gridPath, {"grid"});
+			for i=1, #axes, 2 do
+				writer:Line(axes[i], axes[i+1], {"axes"});
+			end
+		writer:EndGroup();
+	writer:EndDefinitions();
+
+	--Left side.
+	writer:Use("g_axes", subImages:Offset(1, 1), subImages:SubSize());
+	
+	local origCoordAxes = subImages:Transform({1, 1}, vp:Transform(coordAxes))
+	writer:Line(origCoordAxes[1], origCoordAxes[2], {"x_axis"});
+	writer:Line(origCoordAxes[1], origCoordAxes[3], {"y_axis"});
+
+	local origTriangle = subImages:Transform({1, 1}, vp:Transform(triangle))
+	writer:Polygon(origTriangle, {"triangle"});
+
+	--Right side.
+	writer:Use("g_axes", subImages:Offset(2, 1), subImages:SubSize());
+
+	trans2:Scale(vmath.vec2(0.5, 1.2));
+	local newCoordAxes = subImages:Transform({2, 1}, vp:Transform(coordAxes))
+	writer:Line(newCoordAxes[1], newCoordAxes[2], {"x_axis"});
+	writer:Line(newCoordAxes[1], newCoordAxes[3], {"y_axis"});
+
+	local newTriangle = subImages:Transform({2, 1}, vp:Transform(triangle))
+	writer:Polygon(newTriangle, {"triangle"});
+	
+writer:Close();

File Documents/Positioning/GenTransOrderGeom.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+
+subImageSize = 250;
+
+local subImages = SubImage.SubImage(3, 2, subImageSize, subImageSize, 50, 50);
+
+local labelOffset = vmath.vec2(-35, 135);
+
+local coordSize = 14;
+
+local vp = Viewport.Viewport({subImageSize, subImageSize}, {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+local vpUpRight = Viewport.Viewport({subImageSize, subImageSize}, {0, 0}, coordSize)
+vpUpRight:SetTransform(trans2);
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 10;
+
+local coordAxisWidth = "2px";
+local xCoordAxisColor = "#20FF20";
+local yCoordAxisColor = "red";
+local objectColor = "#AFAFFF";
+
+styleLib:AddStyle(nil, "text",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"):font_size("30px"):font_family("monospace"));
+	
+styleLib:AddStyle(nil, "grid",
+	SvgWriter.Style():stroke("#CCC"):fill("none"):stroke_width("1px"));
+
+styleLib:AddStyle(nil, "axes",
+	SvgWriter.Style():stroke("black"):fill("none"):stroke_width("2px")
+		:marker_end(SvgWriter.uriLocalElement("arrow")));
+
+styleLib:AddStyle(nil, "point",
+	SvgWriter.Style():stroke(objectColor):fill(objectColor));
+	
+styleLib:AddStyle(nil, "triangle",
+	SvgWriter.Style():stroke(objectColor):stroke_width("2px"):fill("none")
+		:marker(SvgWriter.uriLocalElement("m_point")));
+	
+styleLib:AddStyle(nil, "x_axis",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+--		:marker_end(SvgWriter.uriLocalElement("big_arrow_x_axis"))
+		);
+	
+styleLib:AddStyle(nil, "y_axis",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+--		:marker_end(SvgWriter.uriLocalElement("big_arrow_y_axis"))
+		);
+	
+styleLib:AddStyle(nil, "x_axis_marker",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "y_axis_marker",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "arrow_end",
+	SvgWriter.Style():marker_end(SvgWriter.uriLocalElement("arrow")));
+
+styleLib:AddStyle(nil, "text",
+	SvgWriter.Style():font_size("30px"):font_family("monospace") );
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local bigArrowheadPath = SvgWriter.Path();
+bigArrowheadPath:M{0, 0}:L{10, 10}:L{0, 20};
+
+--Generate grid.
+local upperBound, lowerBound = vp:Extents();
+
+local gridPath = SvgWriter.Path();
+
+--Vertical lines.
+for i = lowerBound[1], upperBound[1] do
+	local points =
+	{
+		vmath.vec2(i, lowerBound[2]);
+		vmath.vec2(i, upperBound[2]);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):V(points[2][2]);
+end
+
+--Horizontal lines.
+for i = lowerBound[2], upperBound[2] do
+	local points =
+	{
+		vmath.vec2(lowerBound[1], i);
+		vmath.vec2(upperBound[1], i);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):H(points[2][1]);
+end
+
+local axesPath = SvgWriter.Path();
+
+local axes = {}
+
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, lowerBound[2]);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, upperBound[2]);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(upperBound[1], (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(lowerBound[1], (upperBound[2] + lowerBound[2]) / 2);
+
+axes = vp:Transform(axes)
+
+--Draw coodinate axes and objects.
+local coordAxes =
+{
+	vmath.vec2(0, 0),
+	vmath.vec2(1, 0),
+	vmath.vec2(0, 1),
+}
+
+local triangle =
+{
+	vmath.vec2(0.5, 3.5),
+	vmath.vec2(1.0, -2),
+	vmath.vec2(-2.0, -3.5),
+}
+
+
+local writer = SvgWriter.SvgWriter("TransOrderGeom.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({pointSize, pointSize}, {pointSize/2, pointSize/2}, "auto", true, nil, "m_point");
+			writer:Circle({pointSize/2, pointSize/2}, pointSize/2, {"point"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 8}, {10, 4}, "auto", true, nil, "arrow");
+			writer:Path(arrowheadPath, {"fill_black", "thin"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_y_axis");
+			writer:Path(bigArrowheadPath, {"y_axis_marker"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_x_axis");
+			writer:Path(bigArrowheadPath, {"x_axis_marker"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_axes");
+			writer:Path(gridPath, {"grid"});
+			for i=1, #axes, 2 do
+				writer:Line(axes[i], axes[i+1], {"axes"});
+			end
+		writer:EndGroup();
+	writer:EndDefinitions();
+
+	--Left side.
+	writer:Use("g_axes", subImages:Offset(1, 1), subImages:SubSize());
+	
+	local origCoordAxes = subImages:Transform({1, 1}, vp:Transform(coordAxes))
+	writer:Line(origCoordAxes[1], origCoordAxes[2], {"x_axis"});
+	writer:Line(origCoordAxes[1], origCoordAxes[3], {"y_axis"});
+
+	local origTriangle = subImages:Transform({1, 1}, vp:Transform(triangle))
+	writer:Polygon(origTriangle, {"triangle"});
+
+	--Middle side.
+	writer:Use("g_axes", subImages:Offset(2, 1), subImages:SubSize());
+
+	trans2:Translate(vmath.vec2(2, 1));
+	local newCoordAxes = subImages:Transform({2, 1}, vpUpRight:Transform(coordAxes))
+	writer:Line(newCoordAxes[1], newCoordAxes[2], {"x_axis"});
+	writer:Line(newCoordAxes[1], newCoordAxes[3], {"y_axis"});
+
+	local newTriangle = subImages:Transform({2, 1}, vpUpRight:Transform(triangle))
+	writer:Polygon(newTriangle, {"triangle"});
+	
+	--Right side
+	writer:Use("g_axes", subImages:Offset(3, 1), subImages:SubSize());
+
+	trans2:Identity();
+	trans2:Scale(vmath.vec2(2, 0.5));
+	trans2:Translate(vmath.vec2(2, 1));
+	local newCoordAxes = subImages:Transform({3, 1}, vpUpRight:Transform(coordAxes))
+	writer:Line(newCoordAxes[1], newCoordAxes[2], {"x_axis"});
+	writer:Line(newCoordAxes[1], newCoordAxes[3], {"y_axis"});
+
+	local newTriangle = subImages:Transform({3, 1}, vpUpRight:Transform(triangle))
+	writer:Polygon(newTriangle, {"triangle"});
+
+	trans2:Identity();
+
+	--Left side.
+	writer:Use("g_axes", subImages:Offset(1, 2), subImages:SubSize());
+	
+	local origCoordAxes = subImages:Transform({1, 2}, vp:Transform(coordAxes))
+	writer:Line(origCoordAxes[1], origCoordAxes[2], {"x_axis"});
+	writer:Line(origCoordAxes[1], origCoordAxes[3], {"y_axis"});
+
+	local origTriangle = subImages:Transform({1, 2}, vp:Transform(triangle))
+	writer:Polygon(origTriangle, {"triangle"});
+
+	--Middle side.
+	writer:Use("g_axes", subImages:Offset(2, 2), subImages:SubSize());
+
+	trans2:Scale(vmath.vec2(2, 0.5));
+	local newCoordAxes = subImages:Transform({2, 2}, vpUpRight:Transform(coordAxes))
+	writer:Line(newCoordAxes[1], newCoordAxes[2], {"x_axis"});
+	writer:Line(newCoordAxes[1], newCoordAxes[3], {"y_axis"});
+
+	local newTriangle = subImages:Transform({2, 2}, vpUpRight:Transform(triangle))
+	writer:Polygon(newTriangle, {"triangle"});
+	
+	--Right side
+	writer:Use("g_axes", subImages:Offset(3, 2), subImages:SubSize());
+
+	trans2:Identity();
+	trans2:Translate(vmath.vec2(2, 1));
+	trans2:Scale(vmath.vec2(2, 0.5));
+	local newCoordAxes = subImages:Transform({3, 2}, vpUpRight:Transform(coordAxes))
+	writer:Line(newCoordAxes[1], newCoordAxes[2], {"x_axis"});
+	writer:Line(newCoordAxes[1], newCoordAxes[3], {"y_axis"});
+
+	local newTriangle = subImages:Transform({3, 2}, vpUpRight:Transform(triangle))
+	writer:Polygon(newTriangle, {"triangle"});
+
+	--Labels
+	writer:Text("T", labelOffset + subImages:Offset(2, 1), {"text"});
+	writer:Text("S", labelOffset + subImages:Offset(3, 1), {"text"});
+	writer:Text("S", labelOffset + subImages:Offset(2, 2), {"text"});
+	writer:Text("T", labelOffset + subImages:Offset(3, 2), {"text"});
+writer:Close();

File Documents/Positioning/GenTranslationTransform.lua

+require "SvgWriter"
+require "vmath"
+require "Viewport"
+require "SubImage"
+
+local subImages = SubImage.SubImage(2, 1, 300, 300, 100, 0);
+
+local coordSize = 8;
+
+local vp = Viewport.Viewport({300, 300}, {0, 0}, coordSize)
+local trans2 = Viewport.Transform2D()
+vp:SetTransform(trans2);
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+local pointSize = 10;
+
+local coordAxisWidth = "2px";
+local xCoordAxisColor = "green";
+local yCoordAxisColor = "red";
+local objectColor = "#AFAFFF";
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"));
+	
+styleLib:AddStyle(nil, "grid",
+	SvgWriter.Style():stroke("#CCC"):fill("none"):stroke_width("1px"));
+
+styleLib:AddStyle(nil, "axes",
+	SvgWriter.Style():stroke("black"):fill("none"):stroke_width("2px")
+		:marker_end(SvgWriter.uriLocalElement("arrow")));
+
+styleLib:AddStyle(nil, "point",
+	SvgWriter.Style():stroke(objectColor):fill(objectColor));
+	
+styleLib:AddStyle(nil, "triangle",
+	SvgWriter.Style():stroke(objectColor):stroke_width("2px"):fill("none")
+		:marker(SvgWriter.uriLocalElement("m_point")));
+	
+styleLib:AddStyle(nil, "x_axis",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+		:marker_end(SvgWriter.uriLocalElement("big_arrow_x_axis")));
+	
+styleLib:AddStyle(nil, "y_axis",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none")
+		:marker_end(SvgWriter.uriLocalElement("big_arrow_y_axis")));
+	
+styleLib:AddStyle(nil, "x_axis_marker",
+	SvgWriter.Style():stroke(xCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "y_axis_marker",
+	SvgWriter.Style():stroke(yCoordAxisColor):stroke_width(coordAxisWidth):fill("none"));
+
+styleLib:AddStyle(nil, "arrow_end",
+	SvgWriter.Style():marker_end(SvgWriter.uriLocalElement("arrow")));
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local bigArrowheadPath = SvgWriter.Path();
+bigArrowheadPath:M{0, 0}:L{10, 10}:L{0, 20};
+
+--Generate grid.
+local upperBound, lowerBound = vp:Extents();
+
+local gridPath = SvgWriter.Path();
+
+--Vertical lines.
+for i = lowerBound[1], upperBound[1] do
+	local points =
+	{
+		vmath.vec2(i, lowerBound[2]);
+		vmath.vec2(i, upperBound[2]);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):V(points[2][2]);
+end
+
+--Horizontal lines.
+for i = lowerBound[2], upperBound[2] do
+	local points =
+	{
+		vmath.vec2(lowerBound[1], i);
+		vmath.vec2(upperBound[1], i);
+	};
+	
+	points = vp:Transform(points)
+	gridPath:M(points[1]):H(points[2][1]);
+end
+
+local axesPath = SvgWriter.Path();
+
+local axes = {}
+
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, lowerBound[2]);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, 0);
+axes[#axes + 1] = vmath.vec2((upperBound[1] + lowerBound[1]) / 2, upperBound[2]);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(upperBound[1], (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(0, (upperBound[2] + lowerBound[2]) / 2);
+axes[#axes + 1] = vmath.vec2(lowerBound[1], (upperBound[2] + lowerBound[2]) / 2);
+
+axes = vp:Transform(axes)
+
+--Draw coodinate axes and objects.
+local coordAxes =
+{
+	vmath.vec2(0, 0),
+	vmath.vec2(1, 0),
+	vmath.vec2(0, 1),
+}
+
+local triangle =
+{
+	vmath.vec2(0.5, 2.5),
+	vmath.vec2(2.0, -1),
+	vmath.vec2(-3, -0.5),
+}
+
+
+
+
+local writer = SvgWriter.SvgWriter("TranslationTransform.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({pointSize, pointSize}, {pointSize/2, pointSize/2}, "auto", true, nil, "m_point");
+			writer:Circle({pointSize/2, pointSize/2}, pointSize/2, {"point"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 8}, {10, 4}, "auto", true, nil, "arrow");
+			writer:Path(arrowheadPath, {"fill_black", "thin"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_y_axis");
+			writer:Path(bigArrowheadPath, {"y_axis_marker"});
+		writer:EndMarker();
+		writer:BeginMarker({10, 20}, {10, 10}, "auto", true, nil, "big_arrow_x_axis");
+			writer:Path(bigArrowheadPath, {"x_axis_marker"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_axes");
+			writer:Path(gridPath, {"grid"});
+			for i=1, #axes, 2 do
+				writer:Line(axes[i], axes[i+1], {"axes"});
+			end
+		writer:EndGroup();
+	writer:EndDefinitions();
+
+	--Left side.
+	writer:Use("g_axes", subImages:Offset(1, 1), subImages:SubSize());
+	
+	local origCoordAxes = subImages:Transform({1, 1}, vp:Transform(coordAxes))
+	writer:Line(origCoordAxes[1], origCoordAxes[2], {"x_axis"});
+	writer:Line(origCoordAxes[1], origCoordAxes[3], {"y_axis"});
+
+	local origTriangle = subImages:Transform({1, 1}, vp:Transform(triangle))
+	writer:Polygon(origTriangle, {"triangle"});
+
+	--Right side.
+	writer:Use("g_axes", subImages:Offset(2, 1), subImages:SubSize());
+
+	trans2:Translate(vmath.vec2(1, -1.5));
+	local newCoordAxes = subImages:Transform({2, 1}, vp:Transform(coordAxes))
+	writer:Line(newCoordAxes[1], newCoordAxes[2], {"x_axis"});
+	writer:Line(newCoordAxes[1], newCoordAxes[3], {"y_axis"});
+
+	local newTriangle = subImages:Transform({2, 1}, vp:Transform(triangle))
+	writer:Polygon(newTriangle, {"triangle"});
+	
+writer:Close();

File Documents/Positioning/GenWindingOrder.lua

 
 
 -- The SVG itself.
-local writer = SvgWriter.SvgWriter("testSubImage.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
+local writer = SvgWriter.SvgWriter("WindingOrder.svg", {subImages:Size().x .."px", subImages:Size().y .. "px"});
 	writer:StyleLibrary(styleLib);
 	writer:BeginDefinitions();
 		writer:BeginMarker({pointSize, pointSize}, {pointSize/2, pointSize/2}, "auto", true, nil, "point");

File Documents/Positioning/RotationTransform.svg

Added
New image

File Documents/Positioning/ScaleTransform.svg

Added
New image

File Documents/Positioning/TransOrderGeom.svg

Added
New image

File Documents/Positioning/TranslationTransform.svg

Added
New image

File Documents/Positioning/Tutorial 06.xml

                 Remember: a matrix, even a translation matrix, defines a full-fledged coordinate
                 system.</para>
             <para>So S now acts on the T-space position of the vertices. T-space has an origin,
-                defined by the matrix T. A scaling transformation matrix performs scaling based on
-                the origin point in the space of the vertices being scaled. So the scaling matrix S
-                will scale the points away from the origin point in T-space. Since what you
-                (probably) actually wanted was to scale the points away from the origin point in
-                    <emphasis>model space</emphasis>, S needs to come first.</para>
+                which in T-space is (0, 0, 0), but in model space is the translation part of the
+                matrix T. A scaling transformation matrix performs scaling based on the origin point
+                in the space of the vertices being scaled. So the scaling matrix S will scale the
+                points away from the origin point in T-space. Since what you (probably) actually
+                wanted was to scale the points away from the origin point in <emphasis>model
+                    space</emphasis>, S needs to come first.</para>
             <para>Rotation (orientation) matrices have the same issue. The orientation is always
                 local to the origin in the current space of the positions. So a rotation matrix must
                 happen before the translation matrix. Scales generally should happen before
                 if it is a uniform scale, but a non-uniform scale will be problematic.</para>
             <para>There are reasons to put a translation matrix first. If the model-space origin is
                 not the point that you wish to rotate or scale around, then you will need to perform
-                the translation first, then apply a scale or rotation. Doing this multiple times can
-                allow you to scale and rotate about two completely different points. However, this
-                gets progressively more difficult, as each new translation needs to be based on the
-                space of the rotated or scaled points.</para>
+                a translation first, so that the vertices are in the space you want to rotate from,
+                then apply a scale or rotation. Doing this multiple times can allow you to scale and
+                rotate about two completely different points.</para>
         </section>
         <section>
             <title>Transformation Spaces</title>
             orientation, you apply a transformation to this matrix, storing the result as the new
             current orientation.</para>
         <para>This means that every yaw, pitch, and roll applied to the current orientation will be
-            relative to that current orientation.</para>
+            relative to that current orientation. Which is usually exactly what you want. If the
+            user applies a positive yaw, you want that yaw to rotate them relative to where they are
+            current pointing.</para>
         <para>A downside of this approach, besides the size penalty of having to store a 4x4 matrix
             rather than 3 floating-point angles, is that floating-point math can lead to errors. If
             you keep accumulating successive transformations of an object, once every 1/30th of a
             accumulating. Eventually, the orientation stops being a pure rotation and starts
             incorporating scale and skewing characteristics.</para>
         <para>The solution here is to re-orthonormalize the matrix after applying each transform. A
-            transform (space) is said to be <glossterm>orthonormal</glossterm> if the axis vectors
+            transform (space) is said to be <glossterm>orthonormal</glossterm> if the basis vectors
             are of unit length (no scale) and each axis is perpendicular to all of the
             others.</para>
-        <para>Unfortunately, re-orthonormalize a matrix is not a simple operation. You could try to
-            normalize each of the axis vectors with typical vector normalization, but that wouldn't
-            ensure that the matrix was orthonormal. It would remove scaling, but the axes wouldn't
-            be guaranteed to be perpendicular.</para>
+        <para>Unfortunately, re-orthonormalizing a matrix is not a simple operation. You could try
+            to normalize each of the axis vectors with typical vector normalization, but that
+            wouldn't ensure that the matrix was orthonormal. It would remove scaling, but the axes
+            wouldn't be guaranteed to be perpendicular.</para>
         <para>The proper solution is to use <link
                 xlink:href="http://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation">unit
                 quaternions</link> to store orientations, which can be orthonormalized much more

File Documents/Tools/SubImage.lua

 
 	offset.x = (self.subImageSize.x + self.spacing.x) * x;
 	offset.y = (self.subImageSize.y + self.spacing.y) * y;
-	
+
 	return offset;
 end
 
 
 --Points must be in pixel coordinates, relative to the upper-left.
 function ClassMembers:Transform(image, points)
+	local offset = self:Offset(image[1], image[2]);
 	if(vmath.vtype(points) == "table") then
 		local ret = {};
-		local offset = self:Offset(image.x, image.y);
 		for i, realPoint in ipairs(points) do
-			ret[i] = vmath.vec2(points) + offset;
+			ret[i] = vmath.vec2(realPoint) + offset;
 		end
 		return ret;
 	end
 	
-	return vmath.vec2(points) + self:Offset(image.x, image.y);
+	return vmath.vec2(points) + offset;
 end
 
 

File Documents/Tools/Viewport.lua

 
 
 --2D Viewport.
-local View2D = {}
+local View = {}
 
-function View2D:Size()
+function View:Size()
 	return self.pixelSize;
 end
 
 --Takes points in viewport space, returns points in pixel space.
-function View2D:Transform(points)
+--Applies the current transform
+function View:Transform(points)
 	if(vmath.vtype(points) == "table") then
 		local ret = {};
 		for i, realPoint in ipairs(points) do
 		return ret;
 	end
 	
-	local point = vmath.vec2(points);
-	if(self.transform) then point = self.transform:Matrix():Transform(point) end;
+	local point = points;
+	if(self.transform) then
+		point = self.transform:Matrix():Transform(point)
+	end;
 	
+	point = vmath.vec2(point);
+	point = point - self.vpOrigin;
 	point = point / self.vpSize;
 	point = point * vmath.vec2(1, -1);
 	point = point * self.pixelSize;
-	point = point + self.pxOrigin;
+	point = point + self.pxCenter;
 	
 	return point;
 end
 
-function View2D:SetTransform(transform)
+--Takes points in viewport space, returns points in pixel space.
+--Does not apply the current transform
+function View:ViewportTransform(points)
+	local transform = self.transform;
+	self.transform = nil;
+	local ret = self:Transform(points);
+	self.transform = transform;
+	return ret;
+end
+
+function View:SetTransform(transform)
 	self.transform = transform;
 end
 
-
--- 3D Viewport
-local View3D = {}
-
-function View3D:Size()
-	return self.pixelSize;
+--Returns the top-right and bottom-left corners of the viewport.
+function View:Extents()
+	local halfSize = self.vpSize / 2;
+	local upperBound = self.vpOrigin + halfSize;
+	local lowerBound = self.vpOrigin - halfSize;
+	return upperBound, lowerBound;
 end
 
---Takes points in 3D viewport space, returns points in 2D pixel space.
-function View3D:Transform(points)
-	if(vmath.vtype(points) == "table") then
-		local ret = {};
-		for i, realPoint in ipairs(points) do
-			ret[i] = self:Transform(realPoint);
-		end
-		return ret;
-	end
+function Viewport(pixelSize, vpOrigin, vpSize)
+	local viewport = {};
 	
-	local point = vmath.vec3(points);
-	if(self.transform) then point = self.transform:Matrix():Transform(point) end;
+	viewport.pixelSize = vmath.vec2(pixelSize);
+	viewport.vpOrigin = vmath.vec2(vpOrigin);
+	viewport.pxCenter = viewport.pixelSize / 2;
+	if(type(vpSize) == "number") then vpSize = vmath.vec2(vpSize, vpSize) end;
+	viewport.vpSize = vmath.vec2(vpSize);
 	
-	point = point / self.vpSize;
-	point = point * vmath.vec2(1, -1);
-	point = point * self.pixelSize;
-	point = point + self.pxOrigin;
-	
-	return vmath.vec2(point);
+	AddMembers(viewport, View);
+	return viewport;
 end
 
-function View3D:SetTransform(transform)
-	self.transform = transform;
-end
-
-
 
 -- Transform 2D.
 local function Identity3()
 	self.currMatrix = self.currMatrix * rotation;
 end
 
+function Trans2D:MultMatrix(matrix)
+	self.currMatrix = self.currMatrix * matrix;
+end
+
 function Trans2D:Push()
 	if(not self.stack) then
 		self.stack = {};
 	return self.currMatrix;
 end
 
+function Trans2D:Vector(point)
+	return vmath.vec2(point);
+end
+
+function Transform2D()
+	local transform = {};
+	transform.currMatrix = Identity3();
+	AddMembers(transform, Trans2D);
+	return transform;
+end
+
 
 -- Transform 3D.
 local function Identity4()
 	self.currMatrix = self.currMatrix * rotation;
 end
 
+Trans3D.MultMatrix = Trans2D.MultMatrix;
+
 function Trans3D:Identity()
 	self.currMatrix = Identity4();
 end
 	return self.currMatrix;
 end
 
-
-
-function Transform2D()
-	local transform = {};
-	transform.currMatrix = Identity3();
-	AddMembers(transform, Trans2D);
-	return transform;
+function Trans3D:Vector(point)
+	return vmath.vec3(point);
 end
 
 function Transform3D()
 end
 
 
-function Viewport2D(pixelSize, pxOrigin, vpSize)
-	local viewport = {};
-	
-	viewport.pixelSize = vmath.vec2(pixelSize);
-	viewport.pxOrigin = vmath.vec2(pxOrigin);
-	if(type(vpSize) == "number") then vpSize = vmath.vec2(vpSize, vpSize) end;
-	viewport.vpSize = vmath.vec2(vpSize);
-	
-	AddMembers(viewport, View2D);
-	return viewport;
-end
-
-function Viewport3D(pixelSize, vpOrigin, vpScale)
-	local viewport = {};
-	
-	viewport.pixelSize = vmath.vec2(pixelSize);
-	viewport.vpOrigin = vmath.vec3(vpOrigin);
-	if(type(vpScale) == "number") then vpSize = vmath.vec3(vpSize, vpSize, vpSize) end;
-	viewport.vpScale = vmath.vec3(vpSize);
-	
-	AddMembers(viewport, View3D);
-	return viewport;
-end