You can move the red and blue points below.
001: <svg version="1.1" baseProfile="full" 002: xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 003: width="600px" height="400px" viewBox="0 0 600 400" 004: onload="updateAll()" onmousemove="drag(evt)" onmouseup="release(evt)"> 005: 006: <script> 007: //<![CDATA[ 008: 009: // Setup PPoint class for "Projective Geometry Points" 010: function PPoint (x, y, z) { 011: this.x = x; 012: this.y = y; 013: this.z = z; 014: } 015: 016: // Setup PLine class for "Projective Geometry Line" 017: function PLine (x, y, z) { 018: this.x = x; 019: this.y = y; 020: this.z = z; 021: } 022: 023: // Joins two PPoints to make a line: 024: function join(p,q) { 025: return new PLine(p.y*q.z-p.z*q.y, p.z*q.x-p.x*q.z, p.x*q.y-p.y*q.x); 026: } 027: 028: // Intersects two PPLines to get a PPpoint: 029: function intersect(p,q) { 030: return new PPoint(p.y*q.z-p.z*q.y, p.z*q.x-p.x*q.z, p.x*q.y-p.y*q.x); 031: } 032: 033: // Creates a PPoint from the data of a use element described by its id. 034: function pointFromID(useElementId) { 035: var elt = document.getElementById(useElementId); 036: var x=parseFloat(elt.getAttributeNS(null, "x")); 037: var y=parseFloat(elt.getAttributeNS(null, "y")); 038: return new PPoint(x, y, 1); 039: } 040: 041: 042: ///////////// Functions that interface with the SVG graphic: 043: 044: // Use the coordinates of two use elements to update a line element 045: function updateLine(lineId, useId1, useId2) { 046: var line = document.getElementById(lineId); 047: var p1 = document.getElementById(useId1); 048: var p2 = document.getElementById(useId2); 049: line.setAttributeNS(null, "x1", p1.getAttributeNS(null, "x")); 050: line.setAttributeNS(null, "y1", p1.getAttributeNS(null, "y")); 051: line.setAttributeNS(null, "x2", p2.getAttributeNS(null, "x")); 052: line.setAttributeNS(null, "y2", p2.getAttributeNS(null, "y")); 053: } 054: 055: // Updates all line segments and C* points: 056: function updateAll() { 057: updateLine("lA", "A1", "A2"); 058: updateLine("lB", "B1", "B2"); 059: var lA = join(pointFromID("A1"), pointFromID("A2")); 060: var lB = join(pointFromID("B1"), pointFromID("B2")); 061: var pC = intersect(lA, lB); 062: 063: var elt = document.getElementById("C1"); 064: elt.setAttributeNS(null, "x", ""+(pC.x/pC.z)); 065: elt.setAttributeNS(null, "y", ""+(pC.y/pC.z)); 066: } 067: 068: // Global variables for dragging 069: var dragging=false; 070: var drag_elt; 071: var lastx, lasty; 072: 073: // Initiate a drag given a mouse event and the id of a use element. 074: function startDrag(evt,id) { 075: dragging=true; 076: drag_elt=document.getElementById(id); 077: 078: // Get the mouse location: 079: var position = document.rootElement.createSVGPoint(); 080: position.x = evt.clientX; 081: position.y = evt.clientY; 082: var matrix = drag_elt.getScreenCTM(); 083: var correctPosition=position.matrixTransform(matrix.inverse()); 084: lastx = correctPosition.x; 085: lasty = correctPosition.y; 086: } 087: 088: 089: function drag(evt) { 090: if (dragging) { 091: // Get the mouse location: 092: var position = document.rootElement.createSVGPoint(); 093: position.x = evt.clientX; 094: position.y = evt.clientY; 095: var matrix = drag_elt.getScreenCTM(); 096: var correctPosition = position.matrixTransform(matrix.inverse()); 097: newx = correctPosition.x; 098: newy = correctPosition.y; 099: 100: if ( (newx != lastx) || (newy != lasty) ) { 101: var curx=parseFloat(drag_elt.getAttributeNS(null, "x")); 102: var x=curx+newx-lastx; 103: var cury=parseFloat(drag_elt.getAttributeNS(null, "y")); 104: var y =cury+newy-lasty; 105: 106: drag_elt.setAttributeNS(null,"x",""+x); 107: drag_elt.setAttributeNS(null,"y",""+y); 108: 109: lastx=newx; 110: lasty=newy; 111: updateAll(); 112: } 113: } 114: } 115: function release(evt) { 116: dragging=false; 117: } 118: 119: // ]]> 120: </script> 121: 122: <defs> 123: <circle id="A" cx="0" cy="0" r="5" fill="red" stroke="black"/> 124: <circle id="B" cx="0" cy="0" r="5" fill="blue" stroke="black"/> 125: <circle id="C" cx="0" cy="0" r="5" fill="yellow" stroke="black"/> 126: </defs> 127: 128: <g id="lines"> 129: <line id="lA" stroke-width="1px" stroke="black"/> 130: <line id="lB" stroke-width="1px" stroke="black"/> 131: </g> 132: <g id="points"> 133: <use id="A1" xlink:href="#A" x="10" y="10" onmousedown="startDrag(evt,'A1')" cursor="pointer"/> 134: <use id="A2" xlink:href="#A" x="590" y="390" onmousedown="startDrag(evt,'A2')" cursor="pointer"/> 135: <use id="B1" xlink:href="#B" x="300" y="10" onmousedown="startDrag(evt,'B1')" cursor="pointer"/> 136: <use id="B2" xlink:href="#B" x="10" y="390" onmousedown="startDrag(evt,'B2')" cursor="pointer"/> 137: <use id="C1" xlink:href="#C"/> 138: </g> 139: </svg> 140:
The following adds some functions to the objects created in the previous example. For example, if p is a PPoint, we will be able to call p.getX() to get the Euclidean (as opposed to projective) x-coordinate.
The following example demonstrates Pappus' Theorem. This theotem states that if the red points are colinear and the blue points are colinear, then the constructed green points will be colinear.
001: <svg version="1.1" baseProfile="full" 002: xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 003: width="600px" height="400px" viewBox="0 0 600 400" 004: onload="setup()" onmousemove="drag(evt)" onmouseup="release(evt)"> 005: 006: <script> 007: //<![CDATA[ 008: 009: ///// PPoint Objects: 010: 011: // Setup PPoint class for "Projective Geometry Points" 012: function PPoint (x, y, z) { 013: this.x = x; 014: this.y = y; 015: this.z = z; 016: } 017: 018: // Add functions to the PPoint class: 019: 020: // toString() a string representation of the PPoint: 021: PPoint.prototype.toString = function() { 022: return ""+this.x+":"+this.y+":"+this.z; 023: }; 024: 025: // getX() returns the non-projective x-coordinate 026: PPoint.prototype.getX = function() { 027: return this.x/this.z; 028: }; 029: 030: // getY() returns the non-projective y-coordinate 031: PPoint.prototype.getY = function() { 032: return this.y/this.z; 033: }; 034: // updateUse(useElementId) updates the x- and y-coordinates 035: // of the use element associated to an id to match the point. 036: PPoint.prototype.updateUse = function(useElementId) { 037: var elt = document.getElementById(useElementId); 038: elt.setAttributeNS(null, "x", this.getX()); 039: elt.setAttributeNS(null, "y", this.getY()); 040: }; 041: 042: 043: ///////////////// PLine objects: 044: // Setup PLine class for "Projective Geometry Line" 045: function PLine (x, y, z) { 046: this.x = x; 047: this.y = y; 048: this.z = z; 049: } 050: 051: ///////// Extra Functions for working with PPoints and PLines: 052: 053: // Creates a PPoint from the data of a use element described by its id. 054: function pointFromID(useElementId) { 055: var elt = document.getElementById(useElementId); 056: var x=parseFloat(elt.getAttributeNS(null, "x")); 057: var y=parseFloat(elt.getAttributeNS(null, "y")); 058: return new PPoint(x, y, 1); 059: } 060: // Joins two points to make a line: 061: function join(p,q) { 062: return new PLine(p.y*q.z-p.z*q.y, p.z*q.x-p.x*q.z, p.x*q.y-p.y*q.x); 063: } 064: // Intersects two lines to get a point: 065: function intersect(p,q) { 066: return new PPoint(p.y*q.z-p.z*q.y, p.z*q.x-p.x*q.z, p.x*q.y-p.y*q.x); 067: } 068: 069: 070: 071: ///////////// Functions that interface with the SVG graphic: 072: 073: // Use the coordinates of two use elements to update a line element 074: function updateLine(lineId, useId1, useId2) { 075: var line = document.getElementById(lineId); 076: var p1 = document.getElementById(useId1); 077: var p2 = document.getElementById(useId2); 078: line.setAttributeNS(null, "x1", p1.getAttributeNS(null, "x")); 079: line.setAttributeNS(null, "y1", p1.getAttributeNS(null, "y")); 080: line.setAttributeNS(null, "x2", p2.getAttributeNS(null, "x")); 081: line.setAttributeNS(null, "y2", p2.getAttributeNS(null, "y")); 082: } 083: 084: function setup() { 085: updateAll(); 086: } 087: 088: // Updates all line segments and C* points: 089: function updateAll() { 090: c3=intersect(join(pointFromID("A1"),pointFromID("B2")),join(pointFromID("A2"),pointFromID("B1"))); 091: c3.updateUse("C3"); 092: c2=intersect(join(pointFromID("A1"),pointFromID("B3")),join(pointFromID("A3"),pointFromID("B1"))); 093: c2.updateUse("C2"); 094: c1=intersect(join(pointFromID("A3"),pointFromID("B2")),join(pointFromID("A2"),pointFromID("B3"))); 095: c1.updateUse("C1"); 096: 097: updateLine("A1B2","A1","B2"); 098: updateLine("A1B3","A1","B3"); 099: updateLine("A2B1","A2","B1"); 100: updateLine("A2B3","A2","B3"); 101: updateLine("A3B1","A3","B1"); 102: updateLine("A3B2","A3","B2"); 103: 104: updateLine("A1A3","A1","A3"); 105: updateLine("B1B3","B1","B3"); 106: updateLine("C1C3","C1","C3"); 107: } 108: 109: // Global variables for dragging 110: var dragging=false; 111: var drag_elt; 112: var lastx, lasty; 113: 114: function startDrag(evt,id) { 115: dragging=true; 116: drag_elt=document.getElementById(id); 117: 118: // Get the mouse location: 119: var position = document.rootElement.createSVGPoint(); 120: position.x = evt.clientX; 121: position.y = evt.clientY; 122: var matrix = drag_elt.getScreenCTM(); 123: var correctPosition=position.matrixTransform(matrix.inverse()); 124: lastx = correctPosition.x; 125: lasty = correctPosition.y; 126: } 127: 128: 129: function drag(evt) { 130: if (dragging) { 131: // Get the mouse location: 132: var position = document.rootElement.createSVGPoint(); 133: position.x = evt.clientX; 134: position.y = evt.clientY; 135: var matrix = drag_elt.getScreenCTM(); 136: var correctPosition = position.matrixTransform(matrix.inverse()); 137: newx = correctPosition.x; 138: newy = correctPosition.y; 139: 140: if ( (newx != lastx) || (newy != lasty) ) { 141: var curx=parseFloat(drag_elt.getAttributeNS(null, "x")); 142: var x=curx+newx-lastx; 143: var cury=parseFloat(drag_elt.getAttributeNS(null, "y")); 144: var y =cury+newy-lasty; 145: 146: drag_elt.setAttributeNS(null,"x",""+x); 147: drag_elt.setAttributeNS(null,"y",""+y); 148: 149: lastx=newx; 150: lasty=newy; 151: updateAll(); 152: } 153: } 154: } 155: function release(evt) { 156: dragging=false; 157: } 158: 159: // ]]> 160: </script> 161: 162: 163: <defs> 164: <circle id="A" cx="0" cy="0" r="5" fill="red" stroke="black"/> 165: <circle id="B" cx="0" cy="0" r="5" fill="blue" stroke="black"/> 166: <circle id="C" cx="0" cy="0" r="5" fill="green" stroke="black"/> 167: </defs> 168: 169: <g> 170: <line id="A1B2" stroke-width="1px" stroke="grey" stroke-dasharray="3 3"/> 171: <line id="A1B3" stroke-width="1px" stroke="grey" stroke-dasharray="3 3"/> 172: <line id="A2B1" stroke-width="1px" stroke="grey" stroke-dasharray="3 3"/> 173: <line id="A2B3" stroke-width="1px" stroke="grey" stroke-dasharray="3 3"/> 174: <line id="A3B1" stroke-width="1px" stroke="grey" stroke-dasharray="3 3"/> 175: <line id="A3B2" stroke-width="1px" stroke="grey" stroke-dasharray="3 3"/> 176: 177: <line id="A1A3" stroke-width="1px" stroke="black"/> 178: <line id="B1B3" stroke-width="1px" stroke="black"/> 179: <line id="C1C3" stroke-width="1px" stroke="black"/> 180: </g> 181: 182: <use id="A1" xlink:href="#A" x="10" y="10" onmousedown="startDrag(evt,'A1')"/> 183: <use id="A2" xlink:href="#A" x="300" y="10" onmousedown="startDrag(evt,'A2')"/> 184: <use id="A3" xlink:href="#A" x="590" y="10" onmousedown="startDrag(evt,'A3')"/> 185: <use id="B1" xlink:href="#B" x="10" y="390" onmousedown="startDrag(evt,'B1')"/> 186: <use id="B2" xlink:href="#B" x="300" y="390" onmousedown="startDrag(evt,'B2')"/> 187: <use id="B3" xlink:href="#B" x="590" y="390" onmousedown="startDrag(evt,'B3')"/> 188: <use id="C3" xlink:href="#C" x="155" y="200"/> 189: <use id="C2" xlink:href="#C" x="300" y="200"/> 190: <use id="C1" xlink:href="#C" x="445" y="200"/> 191: </svg> 192: