aboutsummaryrefslogtreecommitdiff
path: root/js/helper-classes/RDGE/GLSubpath.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/helper-classes/RDGE/GLSubpath.js')
-rw-r--r--js/helper-classes/RDGE/GLSubpath.js2331
1 files changed, 848 insertions, 1483 deletions
diff --git a/js/helper-classes/RDGE/GLSubpath.js b/js/helper-classes/RDGE/GLSubpath.js
index 2e4c1631..c4ca1b20 100644
--- a/js/helper-classes/RDGE/GLSubpath.js
+++ b/js/helper-classes/RDGE/GLSubpath.js
@@ -34,6 +34,7 @@ function SegmentIntersections(){
34// representation a sequence of cubic bezier curves. 34// representation a sequence of cubic bezier curves.
35// Derived from class GLGeomObj 35// Derived from class GLGeomObj
36/////////////////////////////////////////////////////////////////////// 36///////////////////////////////////////////////////////////////////////
37
37function GLSubpath() { 38function GLSubpath() {
38 /////////////////////////////////////////////////// 39 ///////////////////////////////////////////////////
39 // Instance variables 40 // Instance variables
@@ -49,14 +50,6 @@ function GLSubpath() {
49 50
50 this._UnprojectedAnchors = []; 51 this._UnprojectedAnchors = [];
51 52
52 //offset path samples and the points on the input path they map to
53 this._offsetPointsLeft = [];
54 this._offsetPointsRight = [];
55
56 //triangles determined by the offset points
57 this._offsetTrianglesLeft = [];
58 this._offsetTrianglesRight = [];
59
60 //initially set the _dirty bit so we will construct samples 53 //initially set the _dirty bit so we will construct samples
61 this._dirty = true; 54 this._dirty = true;
62 55
@@ -87,7 +80,6 @@ function GLSubpath() {
87 this._planeMatInv = null; 80 this._planeMatInv = null;
88 this._planeCenter = null; 81 this._planeCenter = null;
89 82
90 // initialize the inherited members
91 this.inheritedFrom = GLGeomObj; 83 this.inheritedFrom = GLGeomObj;
92 this.inheritedFrom(); 84 this.inheritedFrom();
93 85
@@ -104,728 +96,438 @@ function GLSubpath() {
104 this._DEFAULT_STROKE_WIDTH = 20; //use only if stroke width not specified 96 this._DEFAULT_STROKE_WIDTH = 20; //use only if stroke width not specified
105 this._MAX_OFFSET_ANGLE = 10; //max angle (in degrees) between consecutive vectors from curve to offset path 97 this._MAX_OFFSET_ANGLE = 10; //max angle (in degrees) between consecutive vectors from curve to offset path
106 98
107 ///////////////////////////////////////////////////////// 99 // (current GLGeomObj complains if buildBuffers/render is added to GLSubpath prototype)
108 // Property Accessors/Setters 100 //buildBuffers
109 ///////////////////////////////////////////////////////// 101 // Build the stroke vertices, normals, textures and colors
110 this.setWorld = function (world) { this._world = world; } 102 // Add that array data to the GPU using OpenGL data binding
111 this.getWorld = function () { return this._world; } 103 this.buildBuffers = function () {
112 this.makeDirty = function () {this._dirty = true;} 104 return; //no need to do anything for now
113 this.geomType = function () { return this.GEOM_TYPE_CUBIC_BEZIER; } 105 }//buildBuffers()
114 this.setDrawingTool = function (tool) {this._drawingTool = tool;}
115 this.getDrawingTool = function () {return this._drawingTool;}
116 this.setPlaneMatrix = function(planeMat){this._planeMat = planeMat;}
117 this.setPlaneMatrixInverse = function(planeMatInv){this._planeMatInv = planeMatInv;}
118 this.setPlaneCenter = function(pc){this._planeCenter = pc;}
119
120 this.getCanvasX = function(){return this._canvasX;}
121 this.getCanvasY = function(){return this._canvasY;}
122 this.setCanvasX = function(cx){this._canvasX=cx;}
123 this.setCanvasY = function(cy){this._canvasY=cy;}
124
125 this.getIsClosed = function () {return this._isClosed;}
126 this.setIsClosed = function (isClosed) {
127 if (this._isClosed !== isClosed) {
128 this._isClosed = isClosed;
129 this._dirty = true;
130 }
131 }
132 this.getNumAnchors = function () { return this._Anchors.length; }
133 this.getAnchor = function (index) { return this._Anchors[index]; }
134 this.addAnchor = function (anchorPt) {
135 this._Anchors.push(anchorPt);
136 this._selectedAnchorIndex = this._Anchors.length-1;
137 this._dirty = true;
138 }
139 106
140 this.insertAnchor = function(anchorPt, index){ 107 //render
141 this._Anchors.splice(index, 0, anchorPt); 108 // specify how to render the subpath in Canvas2D
142 } 109 this.render = function () {
110 // get the world
111 var world = this.getWorld();
112 if (!world) throw( "null world in subpath render" );
143 113
144 //remove and return anchor at specified index, return null on error 114 // get the context
145 this.removeAnchor = function (index) { 115 var ctx = world.get2DContext();
146 var retAnchor = null; 116 if (!ctx) throw ("null context in subpath render")
147 if (index < this._Anchors.length) {
148 retAnchor = this._Anchors.splice(index, 1);
149 this._dirty = true;
150 }
151 //deselect the removed anchor if necessary
152 if (this._selectedAnchorIndex === index){
153 this._selectedAnchorIndex = -1;
154 }
155 return retAnchor;
156 }
157 117
158 this.deselectAnchorPoint = function(){ 118 var numAnchors = this.getNumAnchors();
159 this._selectedAnchorIndex = -1; 119 if (numAnchors === 0)
160 } 120 return; //nothing to do for empty paths
161 121
162 this.reversePath = function() { 122 ctx.save();
163 var revAnchors = []; 123
164 var numAnchors = this._Anchors.length; 124 this.createSamples(); //dirty bit checked in this function...will generate a polyline representation
165 var lastIndex = numAnchors-1; 125 var bboxMin = this.getBBoxMin();
166 if (lastIndex<0){ 126 var bboxMax = this.getBBoxMax();
167 return; //cannot reverse empty path 127 var bboxWidth = bboxMax[0] - bboxMin[0];
128 var bboxHeight = bboxMax[1] - bboxMin[1];
129 var bboxMid = Vector.create([0.5 * (bboxMax[0] + bboxMin[0]), 0.5 * (bboxMax[1] + bboxMin[1]), 0.5 * (bboxMax[2] + bboxMin[2])]);
130
131 ctx.clearRect(0, 0, bboxWidth, bboxHeight);
132
133
134 ctx.lineWidth = this._strokeWidth;
135 ctx.strokeStyle = "black";
136 if (this._strokeColor)
137 ctx.strokeStyle = MathUtils.colorToHex( this._strokeColor );
138 ctx.fillStyle = "white";
139 if (this._fillColor)
140 ctx.fillStyle = MathUtils.colorToHex( this._fillColor );
141 var lineCap = ['butt','round','square'];
142 ctx.lineCap = lineCap[1];
143 ctx.beginPath();
144 var prevAnchor = this.getAnchor(0);
145 ctx.moveTo(prevAnchor.getPosX()-bboxMin[0],prevAnchor.getPosY()-bboxMin[1]);
146 for (var i = 1; i < numAnchors; i++) {
147 var currAnchor = this.getAnchor(i);
148 ctx.bezierCurveTo(prevAnchor.getNextX()-bboxMin[0],prevAnchor.getNextY()-bboxMin[1], currAnchor.getPrevX()-bboxMin[0], currAnchor.getPrevY()-bboxMin[1], currAnchor.getPosX()-bboxMin[0], currAnchor.getPosY()-bboxMin[1]);
149 prevAnchor = currAnchor;
168 } 150 }
169 for (var i=lastIndex;i>=0;i--) { 151 if (this._isClosed === true) {
170 var newAnchor = new GLAnchorPoint(); 152 var currAnchor = this.getAnchor(0);
171 var oldAnchor = this._Anchors[i]; 153 ctx.bezierCurveTo(prevAnchor.getNextX()-bboxMin[0],prevAnchor.getNextY()-bboxMin[1], currAnchor.getPrevX()-bboxMin[0], currAnchor.getPrevY()-bboxMin[1], currAnchor.getPosX()-bboxMin[0], currAnchor.getPosY()-bboxMin[1]);
172 newAnchor.setPos(oldAnchor.getPosX(),oldAnchor.getPosY(),oldAnchor.getPosZ()); 154 prevAnchor = currAnchor;
173 newAnchor.setPrevPos(oldAnchor.getNextX(),oldAnchor.getNextY(),oldAnchor.getNextZ());
174 newAnchor.setNextPos(oldAnchor.getPrevX(),oldAnchor.getPrevY(),oldAnchor.getPrevZ());
175 revAnchors.push(newAnchor);
176 } 155 }
177 if (this._selectedAnchorIndex >= 0){ 156 if (this._isClosed){
178 this._selectedAnchorIndex = (numAnchors-1) - this._selectedAnchorIndex; 157 ctx.fill();
179 } 158 }
180 this._Anchors = revAnchors; 159 ctx.stroke();
181 this._dirty=true;
182 }
183 160
184 //remove all the anchor points 161 ctx.restore();
185 this.clearAllAnchors = function () { 162 } //render()
186 this._Anchors = [];
187 this._isClosed = false;
188 this._dirty = true;
189 }
190 163
191 this.insertAnchorAtParameter = function(index, param) { 164 this.geomType = function () { return this.GEOM_TYPE_CUBIC_BEZIER; }
192 if (index+1 >= this._Anchors.length && !this._isClosed) { 165} //function GLSubpath ...class definition
193 return;
194 }
195 //insert an anchor after the specified index using the parameter, using De Casteljau subdivision
196 var nextIndex = (index+1)%this._Anchors.length;
197 166
198 //build the De Casteljau points
199 var P0P1 = VecUtils.vecInterpolate(3, this._Anchors[index].getPos(), this._Anchors[index].getNext(), param);
200 var P1P2 = VecUtils.vecInterpolate(3, this._Anchors[index].getNext(), this._Anchors[nextIndex].getPrev(), param);
201 var P2P3 = VecUtils.vecInterpolate(3, this._Anchors[nextIndex].getPrev(), this._Anchors[nextIndex].getPos(), param);
202 167
203 var P0P1P2 = VecUtils.vecInterpolate(3, P0P1, P1P2, param);
204 var P1P2P3 = VecUtils.vecInterpolate(3, P1P2, P2P3, param);
205 var anchorPos = VecUtils.vecInterpolate(3, P0P1P2, P1P2P3, param);
206 168
207 169
208 //update the next of the anchor at index and prev of anchor at nextIndex
209 var isPrevCoincident = false;
210 var isNextCoincident = false;
211 if (VecUtils.vecDist( 3, P0P1, this._Anchors[index].getNext()) < this._SAMPLING_EPSILON) {
212 //no change to the next point
213 isPrevCoincident = true;
214 } else {
215 this._Anchors[index].setNextPos(P0P1[0], P0P1[1], P0P1[2]);
216 }
217 170
218 if (VecUtils.vecDist( 3, P2P3, this._Anchors[nextIndex].getPrev()) < this._SAMPLING_EPSILON) {
219 //no change to the prev point
220 isNextCoincident = true;
221 } else {
222 this._Anchors[nextIndex].setPrevPos(P2P3[0], P2P3[1], P2P3[2]);
223 }
224 171
225 //create a new anchor point 172 /////////////////////////////////////////////////////////
226 var newAnchor = new GLAnchorPoint(); 173 // Property Accessors/Setters
174 /////////////////////////////////////////////////////////
175GLSubpath.prototype.setWorld = function (world) { this._world = world; }
176GLSubpath.prototype.getWorld = function () { return this._world; }
177GLSubpath.prototype.makeDirty = function () {this._dirty = true;}
178