Jason McKesson avatar Jason McKesson committed 79148ac

Tutorial 4 has most images complete.

Comments (0)

Files changed (18)

Documents/Basics/GenNormDeviceCoord.lua

 	return ret;
 end
 
---Negate the Z to get into a left-handed system.
 local viewportMatrix = vmath.mat4(
 	vmath.vec4(imageWidth / 2, 0, 0, imageWidth / 2),
 	vmath.vec4(0, imageHeight / 2, 0, imageHeight / 2),
Add a comment to this file

Documents/Basics/NormDeviceCoord.svg

Old
Old image
New
New image
      x1="187.5"
      class="black arrowended"
      id="line39" />
-  <flowRoot
+  <text
      xml:space="preserve"
-     id="flowRoot2851"
      style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
-     transform="translate(7.4905379,4.4943228)"><flowRegion
-       id="flowRegion2853"><rect
-         id="rect2855"
-         width="47.190388"
-         height="68.912949"
-         x="13.482968"
-         y="192.88794"
-         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial;-inkscape-font-specification:Arial" /></flowRegion><flowPara
-       id="flowPara2857">-X</flowPara></flowRoot>  <flowRoot
+     x="20.959288"
+     y="217.42308"
+     id="text2853"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       x="20.959288"
+       y="217.42308"
+       id="tspan2855"><tspan
+         x="20.959288"
+         y="217.42308"
+         id="tspan2857">-X</tspan></tspan></text>
+  <text
      xml:space="preserve"
-     id="flowRoot2859"
      style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
-     transform="translate(0,-4.4943228)"><flowRegion
-       id="flowRegion2861"><rect
-         id="rect2863"
-         width="41.947014"
-         height="50.935658"
-         x="449.43228"
-         y="288.01776"
-         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Arial;-inkscape-font-specification:Arial" /></flowRegion><flowPara
-       id="flowPara2865">+X</flowPara></flowRoot>  <flowRoot
+     x="449.4375"
+     y="303.5907"
+     id="text2871"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       x="449.4375"
+       y="303.5907"
+       id="tspan2873"><tspan
+         x="449.4375"
+         y="303.5907"
+         id="tspan2875">+X</tspan></tspan></text>
+  <text
      xml:space="preserve"
-     id="flowRoot2867"
      style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
-     transform="translate(-5.9924304,-0.74905379)"><flowRegion
-       id="flowRegion2869"><rect
-         id="rect2871"
-         width="36.703636"
-         height="38.201744"
-         x="446.43607"
-         y="169.66728" /></flowRegion><flowPara
-       id="flowPara2873">+Z</flowPara></flowRoot>  <flowRoot
+     x="440.44507"
+     y="188.96095"
+     id="text2883"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       x="440.44507"
+       y="188.96095"
+       id="tspan2885"><tspan
+         x="440.44507"
+         y="188.96095"
+         id="tspan2887">+Z</tspan></tspan></text>
+  <text
      xml:space="preserve"
-     id="flowRoot2875"
      style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
-     transform="translate(-1.4981076,-4.4943228)"><flowRegion
-       id="flowRegion2877"><rect
-         id="rect2879"
-         width="55.429981"
-         height="27.714991"
-         x="261.41977"
-         y="16.111248" /></flowRegion><flowPara
-       id="flowPara2881">+Y</flowPara></flowRoot>  <flowRoot
+     x="259.90814"
+     y="31.684441"
+     id="text2877"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       x="259.90814"
+       y="31.684441"
+       id="tspan2879"><tspan
+         x="259.90814"
+         y="31.684441"
+         id="tspan2881">+Y</tspan></tspan></text>
+  <text
      xml:space="preserve"
-     id="flowRoot2883"
      style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
-     transform="translate(16.479183,0.74905379)"><flowRegion
-       id="flowRegion2885"><rect
-         id="rect2887"
-         width="35.205528"
-         height="39.699852"
-         x="20.973507"
-         y="309.74033" /></flowRegion><flowPara
-       id="flowPara2889">-Z</flowPara></flowRoot>  <flowRoot
+     x="37.447933"
+     y="330.55283"
+     id="text2859"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       x="37.447933"
+       y="330.55283"
+       id="tspan2861"><tspan
+         x="37.447933"
+         y="330.55283"
+         id="tspan2863">-Z</tspan></tspan></text>
+  <text
      xml:space="preserve"
-     id="flowRoot2891"
-     style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"><flowRegion
-       id="flowRegion2893"><rect
-         id="rect2895"
-         width="41.19796"
-         height="28.464045"
-         x="266.66315"
-         y="469.28879" /></flowRegion><flowPara
-       id="flowPara2897">-Y</flowPara></flowRoot></svg>
+     style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+     x="266.65625"
+     y="489.33502"
+     id="text2865"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       x="266.65625"
+       y="489.33502"
+       id="tspan2867"><tspan
+         x="266.65625"
+         y="489.33502"
+         id="tspan2869">-Y</tspan></tspan></text>
+</svg>

Documents/Outline.xml

                         glDepthRange and the depth portion of the viewport transform.</para>
                 </listitem>
                 <listitem>
-                    <para>Clipping. Show how things are clipped against the view frustum.</para>
+                    <para>Clipping. Show how things are clipped against the view frustum. Note that
+                        clipping happens in <emphasis>clip-space</emphasis>, not NDC space. Thus,
+                        the clipped vertices will <emphasis>never</emphasis> have a W of 0.</para>
                 </listitem>
             </itemizedlist>
         </section>
Add a comment to this file

Documents/Positioning/CameraToPerspective.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" height="450px" width="900px" >
+	<style type="text/css" ><![CDATA[.fill_black
+{
+	fill: black;
+}
+
+.axis_label
+{
+	font-size: 30px;
+	stroke: black;
+	font-family: monospace;
+}
+
+.object_lines
+{
+	stroke: #00C000;
+	stroke-width: 1px;
+	fill: none;
+}
+
+.double_arrowheaded
+{
+	marker-end: url(#arrow);
+	marker-start: url(#arrow);
+}
+
+.arrow_ended
+{
+	marker-end: url(#arrow);
+}
+
+.fill_none
+{
+	fill: none;
+}
+
+.pointed
+{
+	marker: url(#point);
+}
+
+.stroke_none
+{
+	stroke: none;
+}
+
+.image_label
+{
+	font-size: 40px;
+	stroke: black;
+	text-anchor: middle;
+	font-family: serif;
+}
+
+.arrows
+{
+	marker-end: url(#arrow);
+	marker-mid: url(#arrow);
+}
+
+.wide_black
+{
+	stroke-width: 3px;
+	stroke: black;
+}
+
+.text
+{
+	font-size: 30px;
+	font-family: monospace;
+}
+
+.fill_frustum
+{
+	fill: #E0E0E0;
+}
+
+.black
+{
+	stroke-width: 1px;
+	stroke: black;
+}
+
+.object_circles
+{
+	stroke: black;
+	stroke-width: 0.5px;
+	fill: #00C000;
+}
+
+.radial_eye
+{
+	stroke-dasharray: 3,3;
+	stroke: black;
+}]]></style>
+	<defs >
+		<marker markerWidth="10" markerHeight="10" refX="5" refY="5" markerUnits="userSpaceOnUse" orient="auto" id="point" >
+			<circle r="5" cy="5" cx="5" class="fill_black black" />
+		</marker>
+		<marker markerWidth="16" markerHeight="12" refX="16" refY="6" markerUnits="userSpaceOnUse" orient="auto" id="arrow" >
+			<path d="M 16 6 L 0 0 L 0 12 Z" class="fill_black black" />
+		</marker>
+	</defs>
+	<path d="M 100 250 L 300 250 L 400 150 L 400 50 L 0 50 L 0 150 Z" class="stroke_none fill_frustum" />
+	<rect y="100" x="600" height="200" width="200" class="stroke_none fill_frustum" />
+	<line x2="400" y2="150" y1="350" x1="200" class="radial_eye" />
+	<line x2="0" y2="150" y1="350" x1="200" class="radial_eye" />
+	<line x2="200" y2="0" y1="350" x1="200" class="black arrow_ended" />
+	<line x2="200" y2="400" y1="350" x1="200" class="black arrow_ended" />
+	<line x2="0" y2="350" y1="350" x1="200" class="black arrow_ended" />
+	<line x2="400" y2="350" y1="350" x1="200" class="black arrow_ended" />
+	<line x2="192" y2="50" y1="50" x1="208" class="black" />
+	<line x2="188" y2="100" y1="100" x1="212" class="black" />
+	<line x2="192" y2="150" y1="150" x1="208" class="black" />
+	<line x2="188" y2="200" y1="200" x1="212" class="black" />
+	<line x2="192" y2="250" y1="250" x1="208" class="black" />
+	<line x2="188" y2="300" y1="300" x1="212" class="black" />
+	<line x2="192" y2="350" y1="350" x1="208" class="black" />
+	<line x2="50" y2="342" y1="358" x1="50" class="black" />
+	<line x2="100" y2="338" y1="362" x1="100" class="black" />
+	<line x2="150" y2="342" y1="358" x1="150" class="black" />
+	<line x2="200" y2="338" y1="362" x1="200" class="black" />
+	<line x2="250" y2="342" y1="358" x1="250" class="black" />
+	<line x2="300" y2="338" y1="362" x1="300" class="black" />
+	<line x2="350" y2="342" y1="358" x1="350" class="black" />
+	<line x2="700" y2="0" y1="200" x1="700" class="black arrow_ended" />
+	<line x2="700" y2="400" y1="200" x1="700" class="black arrow_ended" />
+	<line x2="500" y2="200" y1="200" x1="700" class="black arrow_ended" />
+	<line x2="900" y2="200" y1="200" x1="700" class="black arrow_ended" />
+	<line x2="692" y2="50" y1="50" x1="708" class="black" />
+	<line x2="688" y2="100" y1="100" x1="712" class="black" />
+	<line x2="692" y2="150" y1="150" x1="708" class="black" />
+	<line x2="688" y2="200" y1="200" x1="712" class="black" />
+	<line x2="692" y2="250" y1="250" x1="708" class="black" />
+	<line x2="688" y2="300" y1="300" x1="712" class="black" />
+	<line x2="692" y2="350" y1="350" x1="708" class="black" />
+	<line x2="550" y2="192" y1="208" x1="550" class="black" />
+	<line x2="600" y2="188" y1="212" x1="600" class="black" />
+	<line x2="650" y2="192" y1="208" x1="650" class="black" />
+	<line x2="700" y2="188" y1="212" x1="700" class="black" />
+	<line x2="750" y2="192" y1="208" x1="750" class="black" />
+	<line x2="800" y2="188" y1="212" x1="800" class="black" />
+	<line x2="850" y2="192" y1="208" x1="850" class="black" />
+	<path d="M 350 225 L 350 275 L 250 275 L 250 225 M 350 175 L 350 225 L 250 225 L 250 175 M 350 125 L 350 175 L 250 175 L 250 125 M 350 75 L 350 125 L 250 125 L 250 75 M 350 25 L 350 75 L 250 75 L 250 25 Z" class="object_lines" />
+	<path d="M 820 160 L 900 0 L 766.66666666667 0 L 740 160 M 785.71428571429 228.57142857143 L 820 160 L 740 160 L 728.57142857143 228.57142857143 M 766.66666666667 266.66666666667 L 785.71428571429 228.57142857143 L 728.57142857143 228.57142857143 L 722.22222222222 266.66666666667 M 754.54545454545 290.90909090909 L 766.66666666667 266.66666666667 L 722.22222222222 266.66666666667 L 718.18181818182 290.90909090909 M 746.15384615385 307.69230769231 L 754.54545454545 290.90909090909 L 718.18181818182 290.90909090909 L 715.38461538462 307.69230769231 Z" class="object_lines" />
+	<circle r="5" cy="275" cx="350" class="object_circles" />
+	<circle r="5" cy="0" cx="900" class="object_circles" />
+	<circle r="5" cy="275" cx="250" class="object_circles" />
+	<circle r="5" cy="0" cx="766.66666666667" class="object_circles" />
+	<circle r="5" cy="225" cx="350" class="object_circles" />
+	<circle r="5" cy="160" cx="820" class="object_circles" />
+	<circle r="5" cy="225" cx="250" class="object_circles" />
+	<circle r="5" cy="160" cx="740" class="object_circles" />
+	<circle r="5" cy="175" cx="350" class="object_circles" />
+	<circle r="5" cy="228.57142857143" cx="785.71428571429" class="object_circles" />
+	<circle r="5" cy="175" cx="250" class="object_circles" />
+	<circle r="5" cy="228.57142857143" cx="728.57142857143" class="object_circles" />
+	<circle r="5" cy="125" cx="350" class="object_circles" />
+	<circle r="5" cy="266.66666666667" cx="766.66666666667" class="object_circles" />
+	<circle r="5" cy="125" cx="250" class="object_circles" />
+	<circle r="5" cy="266.66666666667" cx="722.22222222222" class="object_circles" />
+	<circle r="5" cy="75" cx="350" class="object_circles" />
+	<circle r="5" cy="290.90909090909" cx="754.54545454545" class="object_circles" />
+	<circle r="5" cy="75" cx="250" class="object_circles" />
+	<circle r="5" cy="290.90909090909" cx="718.18181818182" class="object_circles" />
+	<circle r="5" cy="25" cx="350" class="object_circles" />
+	<circle r="5" cy="307.69230769231" cx="746.15384615385" class="object_circles" />
+	<circle r="5" cy="25" cx="250" class="object_circles" />
+	<circle r="5" cy="307.69230769231" cx="715.38461538462" class="object_circles" />
+	<text y="340" x="355" class="axis_label" >+X</text>
+	<text y="30" x="150" class="axis_label" >-Z</text>
+	<text y="190" x="855" class="axis_label" >+X</text>
+	<text y="30" x="650" class="axis_label" >-Z</text>
+	<text y="440" x="200" class="image_label" >Camera Space</text>
+	<text y="440" x="700" class="image_label" >Norm. Device Coord.</text>
+</svg>

Documents/Positioning/GenCameraToPerspective.lua

+require "SvgWriter"
+require "vmath"
+
+vec2 = vmath.vec2;
+
+-- Sizing
+local numSubImages = 2;
+local subImageWidth, subImageHeight = 400, 400;
+local subImageSpacing = 100;
+local belowImageSpaceing = 50;
+
+local imageWidth = (subImageWidth * numSubImages) + (subImageSpacing * (numSubImages - 1));
+local imageHeight = subImageHeight + belowImageSpaceing;
+
+local subImageSize = vmath.vec2{subImageWidth, subImageHeight};
+local pointSize = 10
+local circleRadius = subImageWidth / 8
+
+local subImagePositions = {}
+
+for i = 1, numSubImages, 1 do
+	subImagePositions[i] = vmath.vec2{(subImageWidth + subImageSpacing) * (i-1), 0};
+end
+
+local worldWidth = 4;
+local halfWorldWidth = worldWidth / 2;
+local leftWorldOffset = vmath.vec2(0, halfWorldWidth * 0.75)
+local leftWorldVertRange = vmath.vec2(-halfWorldWidth - leftWorldOffset[2], halfWorldWidth -leftWorldOffset[2])
+
+local function TransformPointToLeftWnd(tPoint)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = TransformPointToLeftWnd(realPoint)
+		end
+		return ret;
+	end
+
+	local final = vmath.vec2(tPoint);
+	final = final + leftWorldOffset;
+	final = final + (halfWorldWidth);
+	final = final * (subImageSize / worldWidth);
+	final = final + subImagePositions[1]
+	return final;
+end
+
+local function TransformPointToRightWnd(tPoint)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = TransformPointToRightWnd(realPoint)
+		end
+		return ret;
+	end
+
+	local final = vmath.vec2(tPoint);
+	--final.y = -final.y
+	final = final + (halfWorldWidth);
+	final = final * (subImageSize / worldWidth);
+	final = final + subImagePositions[2]
+	return final;
+end
+
+local zNear, zFar = -1.0, -3.0;
+local zCenter = (zFar + zNear) / 2;
+local zScale = math.abs(zFar - zNear) / 2;
+
+local function TransformToNDC(tPoint)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = TransformToNDC(realPoint)
+		end
+		return ret;
+	end
+	
+	local final = vec2(tPoint);
+	--[[
+	final.y = (final.y - zCenter);
+	final.y = final.y / zScale;
+	final.y = -final.y;
+	
+	final.x = final.x / -(tPoint.y)
+	]]
+	local near, far = -zNear, -zFar;
+	final.y = (final.y * ((far + near)/(near-far))) + ((2 * near * far)/(near-far))
+	final = final / -(tPoint.y)
+	return final;
+end
+
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "stroke_none",
+	SvgWriter.Style():stroke("none"));
+	
+styleLib:AddStyle(nil, "object_lines",
+	SvgWriter.Style():stroke("#00C000"):stroke_width("1px"):fill("none"));
+	
+styleLib:AddStyle(nil, "object_circles",
+	SvgWriter.Style():fill("#00C000"):stroke("black"):stroke_width("0.5px"));
+	
+styleLib:AddStyle(nil, "radial_eye",
+	SvgWriter.Style():stroke("black"):stroke_dasharray{3, 3});
+	
+styleLib:AddStyle(nil, "wide_black",
+	SvgWriter.Style():stroke("black"):stroke_width("3px"));
+
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_frustum",
+	SvgWriter.Style():fill("#E0E0E0"));
+	
+styleLib:AddStyle(nil, "fill_none",
+	SvgWriter.Style():fill("none"));
+	
+styleLib:AddStyle(nil, "text",
+	SvgWriter.Style():font_size("30px"):font_family("monospace") );
+	
+styleLib:AddStyle(nil, "axis_label",
+	SvgWriter.Style():stroke("black"):font_size("30px"):font_family("monospace") );
+	
+styleLib:AddStyle(nil, "image_label",
+	SvgWriter.Style():stroke("black"):font_size("40px"):font_family("serif"):text_anchor("middle") );
+	
+styleLib:AddStyle(nil, "pointed",
+	SvgWriter.Style():marker(SvgWriter.uriLocalElement("point")));
+
+styleLib:AddStyle(nil, "arrow_ended",
+	SvgWriter.Style():marker_end(SvgWriter.uriLocalElement("arrow")));
+styleLib:AddStyle(nil, "arrows",
+	SvgWriter.Style():marker_mid(SvgWriter.uriLocalElement("arrow")):marker_end(SvgWriter.uriLocalElement("arrow")));
+styleLib:AddStyle(nil, "double_arrowheaded",
+	SvgWriter.Style():marker_start(SvgWriter.uriLocalElement("arrow")):marker_end(SvgWriter.uriLocalElement("arrow")));
+
+-- Paths and other data.
+
+local arrowWidth, arrowLength = 12, 16;
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{arrowLength, arrowWidth / 2}:L{0, 0}:L{0, arrowWidth}:Z();
+
+local leftAxisLocs =
+{
+	vec2(0, 0),
+	vec2(0, leftWorldVertRange[1]),
+	vec2(0, leftWorldVertRange[2]),
+	vec2(-worldWidth/2, 0),
+	vec2(worldWidth/2, 0),
+}
+
+local numAxisHashes = 8
+local leftAxisHashes = {}
+local startPt = leftAxisLocs[2];
+local dir = leftAxisLocs[3] - leftAxisLocs[2]
+local hashSize = worldWidth / 25;
+for i = 1, numAxisHashes - 1 do
+	local leftPt = startPt + (dir * (i / numAxisHashes));
+	local rightPt = vec2(leftPt);
+	if(math.mod(i, 2) == 0) then
+		leftPt.x = leftPt.x + hashSize * 0.75
+		rightPt.x = rightPt.x - hashSize * 0.75
+	else
+		leftPt.x = leftPt.x + hashSize/2
+		rightPt.x = rightPt.x - hashSize/2
+	end
+	leftAxisHashes[#leftAxisHashes + 1] = leftPt;
+	leftAxisHashes[#leftAxisHashes + 1] = rightPt;
+end
+
+startPt = leftAxisLocs[4];
+dir = leftAxisLocs[5] - leftAxisLocs[4]
+for i = 1, numAxisHashes - 1 do
+	local botPt = startPt + (dir * (i / numAxisHashes));
+	local topPt = vec2(botPt);
+	if(math.mod(i, 2) == 0) then
+		botPt.y = botPt.y + hashSize * 0.75
+		topPt.y = topPt.y - hashSize * 0.75
+	else
+		botPt.y = botPt.y + hashSize/2
+		topPt.y = topPt.y - hashSize/2
+	end
+	leftAxisHashes[#leftAxisHashes + 1] = botPt;
+	leftAxisHashes[#leftAxisHashes + 1] = topPt;
+end
+
+leftAxisHashes = TransformPointToLeftWnd(leftAxisHashes);
+leftAxisLocs = TransformPointToLeftWnd(leftAxisLocs);
+
+
+local rightAxisLocs =
+{
+	vec2(0, 0),
+	vec2(0, -worldWidth/2),
+	vec2(0, worldWidth/2),
+	vec2(-worldWidth/2, 0),
+	vec2(worldWidth/2, 0),
+}
+
+local rightAxisHashes = {}
+local startPt = rightAxisLocs[2];
+local dir = rightAxisLocs[3] - rightAxisLocs[2]
+hashSize = worldWidth / 25;
+for i = 1, numAxisHashes - 1 do
+	local leftPt = startPt + (dir * (i / numAxisHashes));
+	local rightPt = vec2(leftPt);
+	if(math.mod(i, 2) == 0) then
+		leftPt.x = leftPt.x + hashSize * 0.75
+		rightPt.x = rightPt.x - hashSize * 0.75
+	else
+		leftPt.x = leftPt.x + hashSize/2
+		rightPt.x = rightPt.x - hashSize/2
+	end
+	rightAxisHashes[#rightAxisHashes + 1] = leftPt;
+	rightAxisHashes[#rightAxisHashes + 1] = rightPt;
+end
+
+startPt = rightAxisLocs[4];
+dir = rightAxisLocs[5] - rightAxisLocs[4]
+for i = 1, numAxisHashes - 1 do
+	local botPt = startPt + (dir * (i / numAxisHashes));
+	local topPt = vec2(botPt);
+	if(math.mod(i, 2) == 0) then
+		botPt.y = botPt.y + hashSize * 0.75
+		topPt.y = topPt.y - hashSize * 0.75
+	else
+		botPt.y = botPt.y + hashSize/2
+		topPt.y = topPt.y - hashSize/2
+	end
+	rightAxisHashes[#rightAxisHashes + 1] = botPt;
+	rightAxisHashes[#rightAxisHashes + 1] = topPt;
+end
+
+rightAxisHashes = TransformPointToRightWnd(rightAxisHashes);
+rightAxisLocs = TransformPointToRightWnd(rightAxisLocs);
+
+
+local function TransformBoth(tPoint)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = TransformBoth(realPoint)
+		end
+		return ret;
+	end
+
+	local testPtLeft = tPoint;
+	local testPtRight = TransformToNDC(testPtLeft);
+	testPtLeft = TransformPointToLeftWnd(testPtLeft);
+	testPtRight = TransformPointToRightWnd(testPtRight);
+	
+	return {testPtLeft, testPtRight};
+end
+
+local testPts =
+{
+	vec2(1.5, -0.75),
+	vec2(0.5, -0.75),
+
+	vec2(1.5, -1.25),
+	vec2(0.5, -1.25),
+
+	vec2(1.5, -1.75),
+	vec2(0.5, -1.75),
+
+	vec2(1.5, -2.25),
+	vec2(0.5, -2.25),
+
+	vec2(1.5, -2.75),
+	vec2(0.5, -2.75),
+
+	vec2(1.5, -3.25),
+	vec2(0.5, -3.25),
+};
+
+testPts = TransformBoth(testPts)
+
+local leftBlocksPath = SvgWriter.Path();
+local rightBlocksPath = SvgWriter.Path();
+
+for i = 1, #testPts - 2, 2 do
+	leftBlocksPath:M(testPts[i + 2][1]):L(testPts[i][1]):L(testPts[i + 1][1]):L(testPts[i + 3][1]);
+	rightBlocksPath:M(testPts[i + 2][2]):L(testPts[i][2]):L(testPts[i + 1][2]):L(testPts[i + 3][2]);
+end
+
+leftBlocksPath:Z();
+rightBlocksPath:Z();
+
+local frustumPoints =
+{
+	vec2(zNear, zNear),
+	vec2(-zNear, zNear),
+	vec2(worldWidth/2, -worldWidth/2),
+	vec2(worldWidth/2, zFar),
+	vec2(-worldWidth/2, zFar),
+	vec2(-worldWidth/2, -worldWidth/2),
+}
+
+frustumPoints = TransformPointToLeftWnd(frustumPoints);
+
+local leftViewArea = SvgWriter.Path()
+
+leftViewArea:M(frustumPoints[1]);
+
+for i = 2, #frustumPoints do
+	leftViewArea:L(frustumPoints[i]);
+end
+
+leftViewArea:Z();
+
+local rightViewArea = { vec2(-1, -1), vec2(1, 1); }
+rightViewArea = TransformPointToRightWnd(rightViewArea);
+
+local leftOrigin = TransformPointToLeftWnd(vec2(0, 0));
+
+local leftAxisLocations = { vec2(halfWorldWidth, 0), vec2(0, leftWorldVertRange[1]) };
+leftAxisLocations = TransformPointToLeftWnd(leftAxisLocations);
+local rightAxisLocations = { vec2(halfWorldWidth, 0), vec2(0, -halfWorldWidth) }
+rightAxisLocations = TransformPointToRightWnd(rightAxisLocations);
+
+local axisLabelPixelOffsets = { vec2(-45, -10), vec2(-50, 30) };
+
+local imageTitleLoc = {vec2(subImageWidth / 2, subImageHeight)}
+imageTitleLoc[2] = imageTitleLoc[1] + vec2(subImageWidth + subImageSpacing, 0)
+local imageTitleOffset = vec2(0, 40)
+
+
+-- The SVG itself.
+local writer = SvgWriter.SvgWriter("CameraToPerspective.svg", {imageWidth .."px", imageHeight .. "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({arrowLength, arrowWidth}, {arrowLength, arrowWidth / 2}, "auto", true, nil, "arrow");
+			writer:Path(arrowheadPath, {"fill_black", "black"});
+		writer:EndMarker();
+	writer:EndDefinitions();
+	
+	--Draw the viewing volumes.
+	writer:Path(leftViewArea, {"stroke_none", "fill_frustum"});
+	writer:Rect2Pt(rightViewArea[1], rightViewArea[2], nil, {"stroke_none", "fill_frustum"});
+	
+	--Draw the eye lines.
+	writer:Line(leftOrigin, frustumPoints[3], {"radial_eye"});
+	writer:Line(leftOrigin, frustumPoints[6], {"radial_eye"});
+	
+	--Draw the coordinate axes.
+	for i, endPt in ipairs(leftAxisLocs) do
+		if(i ~= 1) then
+			writer:Line(leftAxisLocs[1], leftAxisLocs[i], {"black", "arrow_ended"})
+		end
+	end
+	
+	for i = 1, #leftAxisHashes, 2 do
+		writer:Line(leftAxisHashes[i], leftAxisHashes[i+1], {"black"})
+	end
+
+	for i, endPt in ipairs(rightAxisLocs) do
+		if(i ~= 1) then
+			writer:Line(rightAxisLocs[1], rightAxisLocs[i], {"black", "arrow_ended"})
+		end
+	end
+	
+	for i = 1, #rightAxisHashes, 2 do
+		writer:Line(rightAxisHashes[i], rightAxisHashes[i+1], {"black"})
+	end
+
+	--Draw the objects
+	writer:Path(leftBlocksPath, {"object_lines"});
+	writer:Path(rightBlocksPath, {"object_lines"});
+	
+	for i, ptPair in ipairs(testPts) do
+		writer:Circle(ptPair[1], 5, {"object_circles"});
+		writer:Circle(ptPair[2], 5, {"object_circles"});
+	end
+	
+	--label the axes.
+	writer:Text("+X", leftAxisLocations[1] + axisLabelPixelOffsets[1], {"axis_label"})
+	writer:Text("-Z", leftAxisLocations[2] + axisLabelPixelOffsets[2], {"axis_label"})
+	writer:Text("+X", rightAxisLocations[1] + axisLabelPixelOffsets[1], {"axis_label"})
+	writer:Text("-Z", rightAxisLocations[2] + axisLabelPixelOffsets[2], {"axis_label"})
+
+	--label the images
+	writer:Text("Camera Space", imageTitleLoc[1] + imageTitleOffset, {"image_label"});
+	writer:Text("Norm. Device Coord.", imageTitleLoc[2] + imageTitleOffset, {"image_label"});
+	
+--	writer:Rect(subImagePositions[1], subImageSize, nil, {"black", "fill_none"});
+--	writer:Rect(subImagePositions[2], subImageSize, nil, {"black", "fill_none"});
+--	writer:Rect2Pt(TransformPointToLeftWnd(vmath.vec2(-1, -1)), TransformPointToLeftWnd(vmath.vec2(1, 1)), nil, {"fill_none", "black"});
+	
+writer:Close();
+
+
+

Documents/Positioning/GenOrtho2DProjection.lua

+require "SvgWriter"
+require "vmath"
+
+local imageWidth = 600;
+local imageHeight = 500;
+local imageSize = vmath.vec2(imageWidth, imageHeight);
+
+local worldBox = 4;
+
+local worldExtents = {-worldBox / 2, worldBox / 2}
+
+local function TransformPoint(tPoint)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = TransformPoint(realPoint)
+		end
+		return ret;
+	end
+
+	local final = vmath.vec2(tPoint);
+	final.y = -final.y;
+	final = final - worldExtents[1];
+	if(imageWidth > imageHeight) then
+		final = final * (imageHeight / worldBox);
+		final = final + vmath.vec2((imageWidth - imageHeight) / 2, 0.0); --Allow for centering a rectangular image size.
+	else
+		final = final * (imageWidth / worldBox);
+		final = final + vmath.vec2(0.0, (imageHeight - imageWidth) / 2); --Allow for centering a rectangular image size.
+		print(tPoint, final)
+	end
+
+	return final;
+end
+
+local function ProjectPoint(tPoint, projection)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = ProjectPoint(realPoint, projection)
+		end
+		return ret;
+	end
+
+	return vmath.vec2(tPoint.x, projection.lineLoc.y);
+end
+
+
+----------------------------------
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"));
+	
+styleLib:AddStyle(nil, "red",
+	SvgWriter.Style():stroke("red"));
+	
+styleLib:AddStyle(nil, "line_of_proj",
+	SvgWriter.Style():stroke("red"):stroke_width("1px"):stroke_dasharray({4, 8}));
+	
+styleLib:AddStyle(nil, "object",
+	SvgWriter.Style():stroke("#00C000"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "object_projected",
+	SvgWriter.Style():stroke("#00C000"):stroke_width("8px"));
+	
+styleLib:AddStyle(nil, "line_standard",
+	SvgWriter.Style():stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "projected",
+	SvgWriter.Style():stroke_width("8px"));
+	
+styleLib:AddStyle(nil, "dashed",
+	SvgWriter.Style():stroke_dasharray({4, 8}));
+	
+	
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_transluscent",
+	SvgWriter.Style():fill("blue"):fill_opacity(0.1));
+
+styleLib:AddStyle(nil, "fill_none",
+	SvgWriter.Style():fill("none"));
+	
+styleLib:AddStyle(nil, "background",
+	SvgWriter.Style():fill("#E0E0E0"):stroke("none"));
+	
+
+----------------------------------
+-- Point setup.
+
+local lineProjection = {}
+lineProjection.lineLoc = vmath.vec2(0.0, worldExtents[1] * 0.5);
+lineProjection.lineWidth = 2.0;
+lineProjection.lineEndPts =
+{
+	vmath.vec2(-lineProjection.lineWidth / 2, lineProjection.lineLoc.y),
+	vmath.vec2(lineProjection.lineWidth / 2, lineProjection.lineLoc.y),
+}
+lineProjection.finalEndPts = TransformPoint(lineProjection.lineEndPts)
+
+local sideProjLines = {}
+sideProjLines[1] = vmath.vec2(lineProjection.lineEndPts[1])
+sideProjLines[1].x = worldExtents[1];
+sideProjLines[2] = vmath.vec2(lineProjection.lineEndPts[2])
+sideProjLines[2].x = worldExtents[2];
+
+sideProjLines = TransformPoint(sideProjLines);
+
+local projectRect =
+{
+	vmath.vec2(lineProjection.finalEndPts[1].x, 0),
+	vmath.vec2(lineProjection.finalEndPts[2].x - lineProjection.finalEndPts[1].x, lineProjection.finalEndPts[2].y),
+}
+
+
+local rectShape =
+{
+	vmath.vec2(0.8, 0.0),
+	vmath.vec2(0.5, 1.5),
+}
+
+local rectProj = ProjectPoint(rectShape, lineProjection)
+
+rectShape = TransformPoint(rectShape);
+rectProj = TransformPoint(rectProj);
+
+local rectProjOffset = {}
+for i, tPoint in ipairs(rectProj) do
+	rectProjOffset[i] = tPoint + vmath.vec2(0, -1);
+end
+
+local lineShape =
+{
+	vmath.vec2(-0.3, -0.25),
+	vmath.vec2(-1.75, 1.25),
+}
+
+local lineProj = ProjectPoint(lineShape, lineProjection)
+
+lineShape = TransformPoint(lineShape)
+lineProj = TransformPoint(lineProj);
+
+
+----------------------------------------
+-- The SVG itself
+local writer = SvgWriter.SvgWriter("Ortho2DProjection.svg", {imageWidth .."px", imageHeight .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+	writer:EndDefinitions();
+	
+	--Background
+	writer:Rect(projectRect[1], projectRect[2], nil, {"background"});
+	--Projection
+	writer:Line(rectProj[1], rectShape[1], {"line_of_proj"});
+	writer:Line(rectProj[2], vmath.vec2(rectShape[2].x, rectShape[1].y), {"line_of_proj"});
+	writer:Line(rectProj[1], rectProj[2], {"object_projected"});
+	
+	writer:Line(lineShape[1], lineProj[1], {"line_of_proj"});
+	writer:Line(lineShape[2], lineProj[2], {"line_of_proj"});
+	writer:Line(lineProj[1], lineProj[2], {"object_projected"});
+
+	--Draw shapes.
+	writer:Rect(rectShape[2], rectShape[1] - rectShape[2], nil, {"object", "fill_none"});
+	writer:Line(lineShape[1], lineShape[2], {"object"});
+	
+	
+	--Draw the projection plane.
+	writer:Line(lineProjection.finalEndPts[1], lineProjection.finalEndPts[2], {"black", "line_standard"});
+	writer:Line(lineProjection.finalEndPts[1], sideProjLines[1], {"black", "line_standard", "dashed"});
+	writer:Line(lineProjection.finalEndPts[2], sideProjLines[2], {"black", "line_standard", "dashed"});
+
+--[[	
+	--Debug: Box around the world.
+	local tempPt = TransformPoint(vmath.vec2(worldExtents[1], worldExtents[2]));
+	writer:Rect(tempPt, TransformPoint(vmath.vec2(worldExtents[2], worldExtents[1])) - tempPt,
+		nil, {"black", "line_standard", "fill_none"});
+		]]
+writer:Close();
+
+	
+	
+	

Documents/Positioning/GenPersp2DProjection.lua

+require "SvgWriter"
+require "vmath"
+
+local imageWidth = 500;
+local imageHeight = 500;
+local imageSize = vmath.vec2(imageWidth, imageHeight);
+
+local worldBox = 8;
+
+local worldExtents = {-worldBox / 2, worldBox / 2}
+
+local function TransformPoint(tPoint)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = TransformPoint(realPoint)
+		end
+		return ret;
+	end
+
+	local final = vmath.vec2(tPoint);
+	final.y = -final.y;
+	final = final - worldExtents[1];
+	if(imageWidth > imageHeight) then
+		final = final * (imageHeight / worldBox);
+		final = final + vmath.vec2((imageWidth - imageHeight) / 2, 0.0); --Allow for centering a rectangular image size.
+	else
+		final = final * (imageWidth / worldBox);
+		final = final + vmath.vec2(0.0, (imageHeight - imageWidth) / 2); --Allow for centering a rectangular image size.
+	end
+
+	return final;
+end
+
+local function ProjectPoint(tPoint, projection)
+	if(vmath.vtype(tPoint) == "table") then
+		local ret = {}
+		for i, realPoint in ipairs(tPoint) do
+			ret[i] = ProjectPoint(realPoint, projection)
+		end
+		return ret;
+	end
+
+	local lineDir = tPoint - projection.eyeLoc;
+	local offset = (projection.lineLoc.y - projection.eyeLoc.y) / lineDir.y;
+	return (lineDir * offset) + projection.eyeLoc;
+end
+
+
+----------------------------------
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"));
+	
+styleLib:AddStyle(nil, "red",
+	SvgWriter.Style():stroke("red"));
+	
+styleLib:AddStyle(nil, "line_of_proj",
+	SvgWriter.Style():stroke("red"):stroke_width("1px"):stroke_dasharray({4, 8}));
+	
+styleLib:AddStyle(nil, "object",
+	SvgWriter.Style():stroke("#00C000"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "object_projected",
+	SvgWriter.Style():stroke("#00C000"):stroke_width("8px"));
+	
+styleLib:AddStyle(nil, "line_standard",
+	SvgWriter.Style():stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "projected",
+	SvgWriter.Style():stroke_width("8px"));
+	
+styleLib:AddStyle(nil, "dashed",
+	SvgWriter.Style():stroke_dasharray({4, 8}));
+	
+	
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_transluscent",
+	SvgWriter.Style():fill("blue"):fill_opacity(0.1));
+
+styleLib:AddStyle(nil, "fill_none",
+	SvgWriter.Style():fill("none"));
+	
+styleLib:AddStyle(nil, "background",
+	SvgWriter.Style():fill("#E0E0E0"):stroke("none"));
+	
+
+----------------------------------
+-- Point setup.
+
+local lineProjection = {}
+lineProjection.lineLoc = vmath.vec2(0.0, worldExtents[1] * 0.5);
+lineProjection.eyeLoc = vmath.vec2(0.0, worldExtents[1] * 0.75);
+lineProjection.lineWidth = 2.0;
+lineProjection.lineEndPts =
+{
+	vmath.vec2(-lineProjection.lineWidth / 2, lineProjection.lineLoc.y),
+	vmath.vec2(lineProjection.lineWidth / 2, lineProjection.lineLoc.y),
+}
+lineProjection.finalEndPts = TransformPoint(lineProjection.lineEndPts)
+lineProjection.finalEyeLoc = TransformPoint(lineProjection.eyeLoc)
+
+local sideProjLines = {}
+sideProjLines[1] = vmath.vec2(lineProjection.lineEndPts[1])
+sideProjLines[1].x = worldExtents[1];
+sideProjLines[2] = vmath.vec2(lineProjection.lineEndPts[2])
+sideProjLines[2].x = worldExtents[2];
+
+sideProjLines = TransformPoint(sideProjLines);
+
+local projectFrustum =
+{
+	vmath.vec2(lineProjection.lineEndPts[1]),
+	vmath.vec2(lineProjection.lineEndPts[2]),
+}
+
+do
+	local lineDir = lineProjection.lineEndPts[2] - lineProjection.eyeLoc;
+	local offset = (worldExtents[2] - lineProjection.eyeLoc.y) / lineDir.y;
+	projectFrustum[3] = (lineDir * offset) + lineProjection.eyeLoc;
+	lineDir = lineProjection.eyeLoc - lineProjection.lineEndPts[1];
+	offset = (worldExtents[2] - lineProjection.eyeLoc.y) / lineDir.y;
+	projectFrustum[4] = (lineDir * offset) + lineProjection.eyeLoc;
+end
+
+projectFrustum = TransformPoint(projectFrustum);
+
+
+local rectShape =
+{
+	vmath.vec2(0.8, 0.0),
+	vmath.vec2(0.5, 0.0),
+	vmath.vec2(0.5, 1.5),
+	vmath.vec2(0.8, 1.5),
+}
+
+local rectProj = ProjectPoint(rectShape, lineProjection)
+
+rectShape = TransformPoint(rectShape);
+rectProj = TransformPoint(rectProj);
+
+local rectProjOffset = {}
+for i, tPoint in ipairs(rectProj) do
+	rectProjOffset[i] = tPoint + vmath.vec2(0, -1);
+end
+
+local lineShape =
+{
+	vmath.vec2(-3.3, -1.5),
+	vmath.vec2(-0.5, 3.0),
+}
+
+local lineProj = ProjectPoint(lineShape, lineProjection)
+
+lineShape = TransformPoint(lineShape)
+lineProj = TransformPoint(lineProj);
+
+
+----------------------------------------
+-- The SVG itself
+local writer = SvgWriter.SvgWriter("Persp2DProjection.svg", {imageWidth .."px", imageHeight .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+	writer:EndDefinitions();
+	
+	--Background
+	writer:Polygon(projectFrustum, {"background"})
+	
+	--Projection
+	writer:Line(rectProj[1], rectProj[3], {"object_projected"});
+	
+	for i, proj in ipairs(rectProj) do
+		writer:Line(proj, rectShape[i], {"line_of_proj"});
+	end
+
+	writer:Line(lineShape[1], lineProj[1], {"line_of_proj"});
+	writer:Line(lineShape[2], lineProj[2], {"line_of_proj"});
+	writer:Line(lineProj[1], lineProj[2], {"object_projected"});
+
+	--Draw shapes.
+	writer:Polygon(rectShape, {"object", "fill_none"});
+	writer:Line(lineShape[1], lineShape[2], {"object"});
+	
+	
+	--Draw the projection plane.
+	writer:Line(lineProjection.finalEndPts[1], lineProjection.finalEndPts[2], {"black", "line_standard"});
+	writer:Line(lineProjection.finalEndPts[1], sideProjLines[1], {"black", "line_standard", "dashed"});
+	writer:Line(lineProjection.finalEndPts[2], sideProjLines[2], {"black", "line_standard", "dashed"});
+	
+	writer:Circle(lineProjection.finalEyeLoc, imageWidth / 100, {"black", "fill_black"})
+	writer:Line(lineProjection.finalEndPts[1], lineProjection.finalEyeLoc, {"black", "line_standard", "dashed"});
+	writer:Line(lineProjection.finalEndPts[2], lineProjection.finalEyeLoc, {"black", "line_standard", "dashed"});
+	
+
+--[[	
+	--Debug: Box around the world.
+	local tempPt = TransformPoint(vmath.vec2(worldExtents[1], worldExtents[2]));
+	writer:Rect(tempPt, TransformPoint(vmath.vec2(worldExtents[2], worldExtents[1])) - tempPt,
+		nil, {"black", "line_standard", "fill_none"});
+		]]
+writer:Close();
+
+	
+	
+	

Documents/Positioning/GenViewFrustum.lua

+require "SvgWriter"
+require "vmath"
+
+local imageWidth, imageHeight = 500, 500;
+
+local yAngle = math.rad(45);
+local zAngle = math.rad(20);
+
+local ySin, yCos = math.sin(yAngle), math.cos(yAngle);
+local zSin, zCos = math.sin(zAngle), math.cos(zAngle);
+
+local yMat = vmath.mat4(
+	vmath.vec4(yCos, 0, ySin, 0),
+	vmath.vec4(0, 1, 0, 0),
+	vmath.vec4(-ySin, 0, yCos, 0),
+	vmath.vec4(0, 0, 0, 1))
+	
+local zMat = vmath.mat4(
+	vmath.vec4(1, 0, 0, 0),
+	vmath.vec4(0, zCos, -zSin, 0),
+	vmath.vec4(0, zSin, zCos, 0),
+	vmath.vec4(0, 0, 0, 1))
+
+local worldScale = 0.1;
+	
+local scalingMatrix = vmath.mat4(
+	vmath.vec4(worldScale, 0, 0, 0),
+	vmath.vec4(0, worldScale, 0, 0),
+	vmath.vec4(0, 0, worldScale, 0),
+	vmath.vec4(0, 0, 0, 1))
+	
+local fullMat = (scalingMatrix * zMat) * yMat
+
+local function LocalTransform(listOfPoints)
+	local ret = {};
+	for i, point in ipairs(listOfPoints) do
+		ret[#ret + 1] = fullMat:Transform(point);
+	end
+	
+	return ret;
+end
+
+local viewportMatrix = vmath.mat4(
+	vmath.vec4(imageWidth / 2, 0, 0, imageWidth / 2),
+	vmath.vec4(0, imageHeight / 2, 0, imageHeight / 2),
+	vmath.vec4(0, 0, 1, 0),
+	vmath.vec4(0, 0, 0, 1))
+
+local function ViewportTransform(listOfPoints)
+	local ret = {};
+	for i, point in ipairs(listOfPoints) do
+		ret[#ret + 1] = vmath.vec2(viewportMatrix:Transform(point));
+	end
+	
+	return ret;
+end
+
+local initialBoxPoints = {
+	vmath.vec3(		 4.0,	 4.0,	 5.0),
+	vmath.vec3(		-4.0,	 4.0,	 5.0),
+	vmath.vec3(		 4.0,	-4.0,	 5.0),
+	vmath.vec3(		-4.0,	-4.0,	 5.0),
+	vmath.vec3(		 1.0,	 1.0,	-5.0),
+	vmath.vec3(		-1.0,	 1.0,	-5.0),
+	vmath.vec3(		 1.0,	-1.0,	-5.0),
+	vmath.vec3(		-1.0,	-1.0,	-5.0),
+}
+
+local initialAxisPoints =
+{
+	vmath.vec3(2.5, 0.0, 0.0),
+	vmath.vec3(10.0, 0.0, 0.0),
+	
+	vmath.vec3(-2.5, 0.0, 0.0),
+	vmath.vec3(-10.0, 0.0, 0.0),
+	
+	vmath.vec3(0.0, 2.5, 0.0),
+	vmath.vec3(0.0, 10.0, 0.0),
+
+	vmath.vec3(0.0, -2.5, 0.0),
+	vmath.vec3(0.0, -10.0, 0.0),
+
+	vmath.vec3(0.0, 0.0, 5.0),
+	vmath.vec3(0.0, 0.0, 10.0),
+
+	vmath.vec3(0.0, 0.0, -5.0),
+	vmath.vec3(0.0, 0.0, -10.0),
+}
+
+local boxPoints = ViewportTransform(LocalTransform(initialBoxPoints));
+local axisPoints = ViewportTransform(LocalTransform(initialAxisPoints));
+
+
+local boxIndexList =
+{
+	{2, 4, 8, 6},
+	{1, 2, 6, 5},
+	{1, 2, 4, 3},
+
+	{1, 3, 7, 5},
+	{3, 4, 8, 7},
+	{5, 6, 8, 7},
+}
+
+local boxList = {}
+
+for i, box in ipairs(boxIndexList) do
+	boxList[i] = {
+		boxPoints[box[1]],
+		boxPoints[box[2]],
+		boxPoints[box[3]],
+		boxPoints[box[4]]}
+end
+
+local styleLib = SvgWriter.StyleLibrary();
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "dashed",
+	SvgWriter.Style():stroke_dasharray({3, 3}));
+	
+	
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_transluscent",
+	SvgWriter.Style():fill("blue"):fill_opacity(0.1));
+
+styleLib:AddStyle(nil, "arrowended",
+	SvgWriter.Style():marker_end(SvgWriter.uriLocalElement("arrowhead")));
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+
+	
+local writer = SvgWriter.SvgWriter("ViewFrustum.svg", {imageWidth .."px", imageHeight .. "px"});
+	writer:StyleLibrary(styleLib);
+	writer:BeginDefinitions();
+		writer:BeginMarker({10, 8}, {10, 4}, "auto", nil, nil, "arrowhead");
+			writer:Path(arrowheadPath, "black");
+		writer:EndMarker();
+	writer:EndDefinitions();
+
+	--Draw the rear-most lines, with markers.
+	writer:Line(axisPoints[3], axisPoints[4], {"black", "arrowended"});
+	writer:Line(axisPoints[7], axisPoints[8], {"black", "arrowended"});
+	writer:Line(axisPoints[9], axisPoints[10], {"black", "arrowended"});
+	
+	--Draw the rear-most box sides.
+	writer:Polygon(boxList[1], {"black", "fill_transluscent"});
+	writer:Polygon(boxList[2], {"black", "fill_transluscent"});
+	writer:Polygon(boxList[3], {"black", "fill_transluscent"});
+	
+	--Draw the internal lines, no markers.
+	writer:Line(axisPoints[1], axisPoints[3], {"black", "dashed"});
+	writer:Line(axisPoints[5], axisPoints[7], {"black", "dashed"});
+	writer:Line(axisPoints[9], axisPoints[11], {"black", "dashed"});
+	
+	--Draw the front-most boxes.
+	writer:Polygon(boxList[4], {"black", "fill_transluscent"});
+	writer:Polygon(boxList[5], {"black", "fill_transluscent"});
+	writer:Polygon(boxList[6], {"black", "fill_transluscent"});
+	
+	--Draw the front-most lines, with markers.
+	writer:Line(axisPoints[1], axisPoints[2], {"black", "arrowended"});
+	writer:Line(axisPoints[5], axisPoints[6], {"black", "arrowended"});
+	writer:Line(axisPoints[11], axisPoints[12], {"black", "arrowended"});
+	
+writer:Close();
+
+
+

Documents/Positioning/GenWindingOrder.lua

+require "SvgWriter"
+require "vmath"
+
+-- Sizing
+local numSubImages = 2;
+local subImageWidth, subImageHeight = 300, 300;
+local subImageSpacing = 100;
+
+local imageWidth = (subImageWidth * numSubImages) + (subImageSpacing * (numSubImages - 1));
+local imageHeight = subImageHeight;
+
+local subImageSize = {subImageWidth, imageHeight};
+local pointSize = 10
+local circleRadius = subImageWidth / 8
+
+local subImagePositions = {}
+
+for i = 1, numSubImages, 1 do
+	subImagePositions[i] = {(subImageWidth + subImageSpacing) * (i-1), 0};
+end
+
+
+
+-- Styles
+local styleLib = SvgWriter.StyleLibrary();
+
+styleLib:AddStyle(nil, "black",
+	SvgWriter.Style():stroke("black"):stroke_width("1px"));
+	
+styleLib:AddStyle(nil, "wide_black",
+	SvgWriter.Style():stroke("black"):stroke_width("3px"));
+
+styleLib:AddStyle(nil, "fill_black",
+	SvgWriter.Style():fill("black"));
+	
+styleLib:AddStyle(nil, "fill_none",
+	SvgWriter.Style():fill("none"));
+	
+styleLib:AddStyle(nil, "text",
+	SvgWriter.Style():font_size("30px"):font_family("monospace") );
+	
+styleLib:AddStyle(nil, "pointed",
+	SvgWriter.Style():marker(SvgWriter.uriLocalElement("point")));
+
+styleLib:AddStyle(nil, "arrows",
+	SvgWriter.Style():marker_mid(SvgWriter.uriLocalElement("arrow")):marker_end(SvgWriter.uriLocalElement("arrow")));
+
+-- Paths and other data.
+
+local arrowheadPath = SvgWriter.Path();
+arrowheadPath:M{10, 4}:L{0, 0}:L{0, 8}:Z();
+
+local trianglePoints =
+{
+	vmath.vec2{subImageWidth * 0.3, (subImageHeight * 0.2)},
+	vmath.vec2{subImageWidth * 0.8, (subImageHeight * 0.6)},
+	vmath.vec2{subImageWidth * 0.1, (subImageHeight * 0.8)},
+}
+
+local cwLabelOffsets = 
+{
+	vmath.vec2{-7, -12},
+	vmath.vec2{9, 7},
+	vmath.vec2{-25, 25},
+}
+
+local centerPoint = vmath.vec2();
+
+for i, tPoint in ipairs(trianglePoints) do
+	centerPoint = centerPoint + tPoint;
+end
+centerPoint = centerPoint / 3.0;
+
+local unitCWCirclePoints =
+{
+	vmath.vec2{0.0, 1.0},
+	vmath.vec2{-0.866, -0.5},
+	vmath.vec2{0.866, -0.5},
+}
+
+local circleCwPath = SvgWriter.Path();
+local circleCcwPath = SvgWriter.Path();
+
+do
+	local centerCwTriPoints, centerCcwTriPoints = {}, {}
+
+	for i, tPoint in ipairs(unitCWCirclePoints) do
+		centerCwTriPoints[i] = (circleRadius * tPoint) + centerPoint;
+		centerCcwTriPoints[i] = (circleRadius * (tPoint * vmath.vec2(1.0, -1.0))) + centerPoint;
+	end
+
+	circleCwPath:M(centerCwTriPoints[#centerCwTriPoints])
+	circleCcwPath:M(centerCcwTriPoints[#centerCcwTriPoints])
+
+	for i = 1, #centerCwTriPoints do
+		circleCwPath:A({circleRadius, circleRadius}, 0, 0, 1, centerCwTriPoints[i])
+		circleCcwPath:A({circleRadius, circleRadius}, 0, 0, 0, centerCcwTriPoints[i])
+	end
+end
+
+
+-- The SVG itself.
+local writer = SvgWriter.SvgWriter("WindingOrder.svg", {imageWidth .."px", imageHeight .. "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}, {0, 4}, "auto", true, nil, "arrow");
+			writer:Path(arrowheadPath, {"fill_black", "black"});
+		writer:EndMarker();
+		writer:BeginGroup(nil, "g_triangle");
+			writer:Polygon(trianglePoints)
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_cwCircle");
+			writer:Path(circleCwPath, "arrows");
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_ccwCircle");
+			writer:Path(circleCcwPath, "arrows");
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_cwLabels");
+			for i, offset in ipairs(cwLabelOffsets) do
+				writer:Text(tostring(i), trianglePoints[i] + offset);
+			end
+		writer:EndGroup();
+		writer:BeginGroup(nil, "g_ccwLabels");
+			writer:Text("1", trianglePoints[1] + cwLabelOffsets[1]);
+			writer:Text("2", trianglePoints[3] + cwLabelOffsets[3]);
+			writer:Text("3", trianglePoints[2] + cwLabelOffsets[2]);
+		writer:EndGroup();
+	writer:EndDefinitions();
+
+	--First subimage: just the triangle.
+	writer:Use("g_triangle", subImagePositions[1], subImageSize, {"black", "fill_none", "pointed"});
+	writer:Use("g_triangle", subImagePositions[2], subImageSize, {"black", "fill_none", "pointed"});
+	writer:Use("g_cwLabels", subImagePositions[1], subImageSize, {"black", "text"});
+	writer:Use("g_cwCircle", subImagePositions[1], subImageSize, {"black", "fill_none"});
+	writer:Use("g_ccwLabels", subImagePositions[2], subImageSize, {"black", "text"});
+	writer:Use("g_ccwCircle", subImagePositions[2], subImageSize, {"black", "fill_none"});
+--	writer:Rect(subImagePositions[1], subImageSize, nil, {"black", "fill_none"});
+--	writer:Rect(subImagePositions[2], subImageSize, nil, {"black", "fill_none"});
+	
+writer:Close();
+
+
+
Add a comment to this file

Documents/Positioning/Ortho2DProjection.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" height="500px" width="600px" >
+	<style type="text/css" ><![CDATA[.dashed
+{
+	stroke-dasharray: 4,8;
+}
+
+.background
+{
+	stroke: none;
+	fill: #E0E0E0;
+}
+
+.fill_none
+{
+	fill: none;
+}
+
+.line_of_proj
+{
+	stroke: red;
+	stroke-dasharray: 4,8;
+	stroke-width: 1px;
+}
+
+.fill_transluscent
+{
+	fill-opacity: 0.1;
+	fill: blue;
+}
+
+.fill_black
+{
+	fill: black;
+}
+
+.object
+{
+	stroke-width: 1px;
+	stroke: #00C000;
+}
+
+.object_projected
+{
+	stroke-width: 8px;
+	stroke: #00C000;
+}
+
+.red
+{
+	stroke: red;
+}
+
+.black
+{
+	stroke: black;
+}
+
+.projected
+{
+	stroke-width: 8px;
+}
+
+.line_standard
+{
+	stroke-width: 1px;
+}]]></style>
+	<defs />
+	<rect y="0" x="175" height="375" width="250" class="background" />
+	<line x2="400" y2="250" y1="375" x1="400" class="line_of_proj" />
+	<line x2="362.5" y2="250" y1="375" x1="362.5" class="line_of_proj" />
+	<line x2="362.5" y2="375" y1="375" x1="400" class="object_projected" />
+	<line x2="262.5" y2="375" y1="281.25" x1="262.5" class="line_of_proj" />
+	<line x2="81.25" y2="375" y1="93.75" x1="81.25" class="line_of_proj" />
+	<line x2="81.25" y2="375" y1="375" x1="262.5" class="object_projected" />
+	<rect y="62.5" x="362.5" height="187.5" width="37.5" class="object fill_none" />
+	<line x2="81.25" y2="93.75" y1="281.25" x1="262.5" class="object" />
+	<line x2="425" y2="375" y1="375" x1="175" class="black line_standard" />
+	<line x2="50" y2="375" y1="375" x1="175" class="black line_standard dashed" />
+	<line x2="550" y2="375" y1="375" x1="425" class="black line_standard dashed" />
+</svg>
Add a comment to this file

Documents/Positioning/Persp2DProjection.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" height="500px" width="500px" >
+	<style type="text/css" ><![CDATA[.fill_none
+{
+	fill: none;
+}
+
+.red
+{
+	stroke: red;
+}
+
+.object_projected
+{
+	stroke-width: 8px;
+	stroke: #00C000;
+}
+
+.line_of_proj
+{
+	stroke: red;
+	stroke-dasharray: 4,8;
+	stroke-width: 1px;
+}
+
+.background
+{
+	stroke: none;
+	fill: #E0E0E0;
+}
+
+.fill_transluscent
+{
+	fill-opacity: 0.1;
+	fill: blue;
+}
+
+.dashed
+{
+	stroke-dasharray: 4,8;
+}
+
+.fill_black
+{
+	fill: black;
+}
+
+.line_standard
+{
+	stroke-width: 1px;
+}
+
+.projected
+{
+	stroke-width: 8px;
+}
+
+.object
+{
+	stroke-width: 1px;
+	stroke: #00C000;
+}
+
+.black
+{
+	stroke: black;
+}]]></style>
+	<defs />
+	<polygon points="187.5,375 312.5,375 687.5,0 -187.5,0" class="background" />
+	<line x2="256.94444444444" y2="375" y1="375" x1="266.66666666667" class="object_projected" />
+	<line x2="300" y2="250" y1="375" x1="266.66666666667" class="line_of_proj" />
+	<line x2="281.25" y2="250" y1="375" x1="260.41666666667" class="line_of_proj" />
+	<line x2="281.25" y2="156.25" y1="375" x1="256.94444444444" class="line_of_proj" />
+	<line x2="300" y2="156.25" y1="375" x1="261.11111111111" class="line_of_proj" />
+	<line x2="112.5" y2="375" y1="343.75" x1="43.75" class="line_of_proj" />
+	<line x2="244.79166666667" y2="375" y1="62.5" x1="218.75" class="line_of_proj" />
+	<line x2="244.79166666667" y2="375" y1="375" x1="112.5" class="object_projected" />
+	<polygon points="300,250 281.25,250 281.25,156.25 300,156.25" class="object fill_none" />
+	<line x2="218.75" y2="62.5" y1="343.75" x1="43.75" class="object" />
+	<line x2="312.5" y2="375" y1="375" x1="187.5" class="black line_standard" />
+	<line x2="0" y2="375" y1="375" x1="187.5" class="black line_standard dashed" />
+	<line x2="500" y2="375" y1="375" x1="312.5" class="black line_standard dashed" />
+	<circle r="5" cy="437.5" cx="250" class="black fill_black" />
+	<line x2="250" y2="437.5" y1="375" x1="187.5" class="black line_standard dashed" />
+	<line x2="250" y2="437.5" y1="375" x1="312.5" class="black line_standard dashed" />
+</svg>
Add a comment to this file

Documents/Positioning/PerspDiagram.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="525"
+   height="400"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="PerspDiagram.svg">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2853"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3651"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2877"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3413"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3442"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3442-9"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8284271"
+     inkscape:cx="374.89524"
+     inkscape:cy="184.95539"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1776"
+     inkscape:window-height="1050"
+     inkscape:window-x="134"
+     inkscape:window-y="-10"
+     inkscape:window-maximized="1"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <sodipodi:guide
+       orientation="0,1"
+       position="288.39855,174.75639"
+       id="guide2816" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="200,243.57143"
+       id="guide2818" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="50,153.57143"
+       id="guide2822" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="349.28571,207.85714"
+       id="guide2824" />
+    <sodipodi:guide
+       orientation="0,1"
+       position="220,50"
+       id="guide2830" />
+    <sodipodi:guide
+       orientation="0,1"
+       position="32.142857,300"
+       id="guide2834" />
+    <sodipodi:guide
+       orientation="1,0"
+       position="330,332.14286"
+       id="guide2836" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-652.36218)">
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 200,225.24361 200,58.571428"
+       id="path2820"
+       transform="translate(0,652.36218)"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 349.28571,877.60579 50,877.60579"
+       id="path2828" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
+       d="m 200,350 0,-124.75639"
+       id="path2832"
+       transform="translate(0,652.36218)"
+       sodipodi:nodetypes="cc" />
+    <path
+       sodipodi:type="arc"
+       id="pathdfasd2843"
+       sodipodi:cx="330"
+       sodipodi:cy="100.89285"
+       sodipodi:rx="5"
+       sodipodi:ry="5"
+       d="m 335,100.89285 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
+       transform="translate(0.37880721,651.46933)" />
+    <path
+       transform="translate(-130,901.86173)"
+       sodipodi:type="arc"
+       id="pathdfasd2843-1"
+       sodipodi:cx="330"
+       sodipodi:cy="100.89285"
+       sodipodi:rx="5"
+       sodipodi:ry="5"
+       d="m 335,100.89285 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
+       d="m 200,100 130,0"
+       id="path2867"
+       transform="translate(0,652.36218)" />
+    <path
+       style="fill:none;stroke:#ff0000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
+       d="M 330,100 200,350"
+       id="path2869"
+       transform="translate(0,652.36218)" />
+    <path
+       transform="translate(-64.708688,776.71294)"
+       sodipodi:type="arc"
+       id="pathdfasd2843-1-7"
+       sodipodi:cx="330"
+       sodipodi:cy="100.89285"
+       sodipodi:rx="5"
+       sodipodi:ry="5"
+       d="m 335,100.89285 a 5,5 0 1 1 -10,0 5,5 0 1 1 10,0 z"
+       style="fill:#00c000;fill-opacity:1" />
+    <text
+       xml:space="preserve"
+       style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:monospace;-inkscape-font-specification:Arial"
+       x="188.00465"
+       y="697.9895"
+       id="text2849"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="188.00465"
+         y="697.9895"
+         id="tspan2851"><tspan
+           x="188.00465"
+           y="697.9895"
+           id="tspan2853">-Z</tspan></tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman Italic"
+       x="340.42142"
+       y="744.26563"
+       id="text3689"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3691"
+         x="340.42142"
+         y="744.26563">P</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman Italic"
+       x="173.79854"
+       y="1027.4242"
+       id="text2843"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="173.79854"
+         y="1027.4242"
+         id="tspan2845"><tspan
+           x="173.79854"
+           y="1027.4242"
+           id="tspan2847"
+           style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Times New Roman;-inkscape-font-specification:Times New Roman Italic">E</tspan></tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman Italic"
+       x="247.47508"
+       y="870.37311"
+       id="text2855"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="247.47508"
+         y="870.37311"
+         id="tspan2857"><tspan
+           x="247.47508"
+           y="870.37311"
+           id="tspan2859"
+           style="font-size:28px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Times New Roman;-inkscape-font-specification:Times New Roman Italic">R</tspan></tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+       x="203.54573"
+       y="890.23267"
+       id="text2861"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2863"
+         x="203.54573"
+         y="890.23267">(0, 0, 0)</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:115.85411072px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman"
+       x="156.95186"
+       y="931.4101"
+       id="text2865"
+       sodipodi:linespacing="125%"
+       transform="scale(0.96275886,1.0386817)"><tspan
+         sodipodi:role="line"
+         id="tspan2867"
+         x="156.95186"
+         y="931.4101">{</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:115.85411072px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman"
+       x="153.92117"
+       y="809.42706"
+       id="text2865-1"
+       sodipodi:linespacing="125%"
+       transform="scale(0.96275886,1.0386817)"><tspan
+         sodipodi:role="line"
+         id="tspan2867-7"
+         x="153.92117"
+         y="809.42706">{</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman"
+       x="134.35028"
+       y="816.9967"
+       id="text2894"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2896"
+         x="134.35028"
+         y="816.9967">P</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman"
+       x="134.35028"
+       y="944.78094"
+       id="text2898"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="134.35028"
+         y="944.78094"
+         id="tspan2902">E</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman"
+       x="148.4975"
+       y="824.03223"
+       id="text2906"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2908"
+         x="148.4975"
+         y="824.03223">z</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman"
+       x="152.00258"
+       y="948.8266"
+       id="text2910"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan2912"
+         x="152.00258"
+         y="948.8266">z</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Lucida Sans Unicode;-inkscape-font-specification:Lucida Sans Unicode"
+       x="169.48975"
+       y="1012.8588"
+       id="text3430"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3432"
+         x="169.48975"
+         y="1012.8588">⇀</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Lucida Sans Unicode;-inkscape-font-specification:Lucida Sans Unicode"
+       x="242.28998"
+       y="854.80389"
+       id="text3430-4"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3432-0"
+         x="242.28998"
+         y="854.80389">⇀</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:28px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Lucida Sans Unicode;-inkscape-font-specification:Lucida Sans Unicode"
+       x="338.10294"
+       y="728.58539"
+       id="text3430-48"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3432-8"
+         x="338.10294"
+         y="728.58539">⇀</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Times New Roman;-inkscape-font-specification:Times New Roman"
+       x="352.49271"
+       y="882.65656"
+       id="text2864"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="352.49271"
+         y="882.65656"
+         id="tspan2868">Projection Plane</tspan></text>
+  </g>
+</svg>