diff options
Diffstat (limited to 'js/helper-classes/RDGE/GLSubpath.js')
-rw-r--r-- | js/helper-classes/RDGE/GLSubpath.js | 2331 |
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 | |||
37 | function GLSubpath() { | 38 | function 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 | ///////////////////////////////////////////////////////// | ||
175 | GLSubpath.prototype.setWorld = function (world) { this._world = world; } | ||
176 | GLSubpath.prototype.getWorld = function () { return this._world; } | ||
177 | GLSubpath.prototype.makeDirty = function () {this._dirty = true;} | ||
178 |