diff options
Diffstat (limited to 'js/lib/geom/sub-path.js')
-rwxr-xr-x | js/lib/geom/sub-path.js | 1288 |
1 files changed, 1288 insertions, 0 deletions
diff --git a/js/lib/geom/sub-path.js b/js/lib/geom/sub-path.js new file mode 100755 index 00000000..ab54d1e9 --- /dev/null +++ b/js/lib/geom/sub-path.js | |||
@@ -0,0 +1,1288 @@ | |||
1 | /* <copyright> | ||
2 | This file contains proprietary software owned by Motorola Mobility, Inc.<br/> | ||
3 | No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/> | ||
4 | (c) Copyright 2011 Motorola Mobility, Inc. All Rights Reserved. | ||
5 | </copyright> */ | ||
6 | |||
7 | |||
8 | var VecUtils = require("js/helper-classes/3D/vec-utils").VecUtils; | ||
9 | |||
10 | var GeomObj = require("js/lib/geom/geom-obj").GeomObj; | ||
11 | var AnchorPoint = require("js/lib/geom/anchor-point").AnchorPoint; | ||
12 | var MaterialsModel = require("js/models/materials-model").MaterialsModel; | ||
13 | |||
14 | // TODO Those function do not seems to be used. We should remove them | ||
15 | function SubpathOffsetPoint(pos, mapPos) { | ||
16 | this.Pos = [pos[0],pos[1],pos[2]]; | ||
17 | this.CurveMapPos = [mapPos[0], mapPos[1], mapPos[2]]; | ||
18 | } | ||
19 | |||
20 | function SubpathOffsetTriangle(v0, v1, v2) { | ||
21 | this.v0 = v0; | ||
22 | this.v1 = v1; | ||
23 | this.v2 = v2; | ||
24 | this.n = [0,0,1]; //replace with the actual cross product later | ||
25 | } | ||
26 | |||
27 | function sortNumberAscending(a,b){ | ||
28 | return a-b; | ||
29 | } | ||
30 | function sortNumberDescending(a,b){ | ||
31 | return b-a; | ||
32 | } | ||
33 | function SegmentIntersections(){ | ||
34 | this.paramArray = []; | ||
35 | } | ||
36 | |||
37 | /////////////////////////////////////////////////////////////////////// | ||
38 | // Class GLSubpath | ||
39 | // representation a sequence of cubic bezier curves. | ||
40 | // Derived from class GeomObj | ||
41 | /////////////////////////////////////////////////////////////////////// | ||
42 | |||
43 | var GLSubpath = function GLSubpath() { | ||
44 | /////////////////////////////////////////////////// | ||
45 | // Instance variables | ||
46 | /////////////////////////////////////////////////// | ||
47 | this._Anchors = []; | ||
48 | this._BBoxMin = [0, 0, 0]; | ||
49 | this._BBoxMax = [0, 0, 0]; | ||
50 | this._isClosed = false; | ||
51 | |||
52 | this._samples = []; //polyline representation of this curve | ||
53 | this._sampleParam = []; //parametric distance of samples, within [0, N], where N is # of Bezier curves (=# of anchor points if closed, =#anchor pts -1 if open) | ||
54 | this._anchorSampleIndex = []; //index within _samples corresponding to anchor points | ||
55 | |||
56 | this._UnprojectedAnchors = []; | ||
57 | |||
58 | //initially set the _dirty bit so we will construct samples | ||
59 | this._dirty = true; | ||
60 | |||
61 | //whether or not to use the canvas drawing to stroke/fill | ||
62 | this._useCanvasDrawing = true; | ||
63 | |||
64 | //the X and Y location of this subpath's canvas in stage world space of Ninja | ||
65 | this._canvasX = 0; | ||
66 | this._canvasY = 0; | ||
67 | |||
68 | //stroke information | ||
69 | this._strokeWidth = 0.0; | ||
70 | this._strokeColor = [0.4, 0.4, 0.4, 1.0]; | ||
71 | this._strokeMaterial = null | ||
72 | this._strokeStyle = "Solid"; | ||
73 | this._materialAmbient = [0.2, 0.2, 0.2, 1.0]; | ||
74 | this._materialDiffuse = [0.4, 0.4, 0.4, 1.0]; | ||
75 | this._materialSpecular = [0.4, 0.4, 0.4, 1.0]; | ||
76 | this._fillColor = [0.4, 0.4, 0.4, 1.0]; | ||
77 | this._fillMaterial = null; | ||
78 | this._DISPLAY_ANCHOR_RADIUS = 5; | ||
79 | //drawing context | ||
80 | this._world = null; | ||
81 | |||
82 | //tool that owns this subpath | ||
83 | this._drawingTool = null; | ||
84 | this._planeMat = null; | ||
85 | this._planeMatInv = null; | ||
86 | this._planeCenter = null; | ||
87 | |||
88 | //used to query what the user selected, OR-able for future extensions | ||
89 | this.SEL_NONE = 0; //nothing was selected | ||
90 | this.SEL_ANCHOR = 1; //anchor point was selected | ||
91 | this.SEL_PREV = 2; //previous handle of anchor point was selected | ||
92 | this.SEL_NEXT = 4; //next handle of anchor point was selected | ||
93 | this.SEL_PATH = 8; //the path itself was selected | ||
94 | this._selectMode = this.SEL_NONE; | ||
95 | this._selectedAnchorIndex = -1; | ||
96 | |||
97 | this._SAMPLING_EPSILON = 0.5; //epsilon used for sampling the curve | ||
98 | this._DEFAULT_STROKE_WIDTH = 20; //use only if stroke width not specified | ||
99 | this._MAX_OFFSET_ANGLE = 10; //max angle (in degrees) between consecutive vectors from curve to offset path | ||
100 | |||
101 | // (current GeomObj complains if buildBuffers/render is added to GLSubpath prototype) | ||
102 | //buildBuffers | ||
103 | // Build the stroke vertices, normals, textures and colors | ||
104 | // Add that array data to the GPU using OpenGL data binding | ||
105 | this.buildBuffers = function () { | ||
106 | // return; //no need to do anything for now | ||
107 | }; | ||
108 | |||
109 | //render | ||
110 | // specify how to render the subpath in Canvas2D | ||
111 | this.render = function () { | ||
112 | // get the world | ||
113 | var world = this.getWorld(); | ||
114 | if (!world) throw( "null world in subpath render" ); | ||
115 | |||
116 | // get the context | ||
117 | var ctx = world.get2DContext(); | ||
118 | if (!ctx) throw ("null context in subpath render") | ||
119 | |||
120 | var numAnchors = this.getNumAnchors(); | ||
121 | if (numAnchors === 0) { | ||
122 | return; //nothing to do for empty paths | ||
123 | } | ||
124 | |||
125 | ctx.save(); | ||
126 | |||
127 | this.createSamples(); //dirty bit checked in this function...will generate a polyline representation | ||
128 | var bboxMin = this.getBBoxMin(); | ||
129 | var bboxMax = this.getBBoxMax(); | ||
130 | var bboxWidth = bboxMax[0] - bboxMin[0]; | ||
131 | var bboxHeight = bboxMax[1] - bboxMin[1]; | ||
132 | var bboxMid = [0.5 * (bboxMax[0] + bboxMin[0]), 0.5 * (bboxMax[1] + bboxMin[1]), 0.5 * (bboxMax[2] + bboxMin[2])]; | ||
133 | |||
134 | ctx.clearRect(0, 0, bboxWidth, bboxHeight); | ||
135 | |||
136 | ctx.lineWidth = this._strokeWidth; | ||
137 | ctx.strokeStyle = "black"; | ||
138 | if (this._strokeColor) { | ||
139 | ctx.strokeStyle = MathUtils.colorToHex( this._strokeColor ); | ||
140 | } | ||
141 | |||
142 | ctx.fillStyle = "white"; | ||
143 | if (this._fillColor){ | ||
144 | //ctx.fillStyle = MathUtils.colorToHex( this._fillColor ); | ||
145 | var fillColorStr = "rgba("+parseInt(255*this._fillColor[0])+","+parseInt(255*this._fillColor[1])+","+parseInt(255*this._fillColor[2])+","+this._fillColor[3]+")"; | ||
146 | ctx.fillStyle = fillColorStr; | ||
147 | } | ||
148 | var lineCap = ['butt','round','square']; | ||
149 | ctx.lineCap = lineCap[1]; | ||
150 | ctx.beginPath(); | ||
151 | |||
152 | /* | ||
153 | commenting this out for now because of Chrome bug where coincident endpoints of bezier curve cause the curve to not be rendered | ||
154 | var prevAnchor = this.getAnchor(0); | ||
155 | ctx.moveTo(prevAnchor.getPosX()-bboxMin[0],prevAnchor.getPosY()-bboxMin[1]); | ||
156 | for (var i = 1; i < numAnchors; i++) { | ||
157 | var currAnchor = this.getAnchor(i); | ||
158 | 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]); | ||
159 | prevAnchor = currAnchor; | ||
160 | } | ||
161 | if (this._isClosed === true) { | ||
162 | var currAnchor = this.getAnchor(0); | ||
163 | 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]); | ||
164 | prevAnchor = currAnchor; | ||
165 | ctx.fill(); | ||
166 | } | ||
167 | */ | ||
168 | |||
169 | |||
170 | var numPoints = this._samples.length/3; | ||
171 | ctx.moveTo(this._samples[0]-bboxMin[0],this._samples[1]-bboxMin[1]); | ||
172 | for (var i=0;i<numPoints;i++){ | ||
173 | ctx.lineTo(this._samples[3*i]-bboxMin[0],this._samples[3*i + 1]-bboxMin[1]); | ||
174 | } | ||
175 | if (this._isClosed === true) { | ||
176 | ctx.lineTo(this._samples[0]-bboxMin[0],this._samples[1]-bboxMin[1]); | ||
177 | } | ||
178 | ctx.fill(); | ||
179 | ctx.stroke(); | ||
180 | ctx.restore(); | ||
181 | }; //render() | ||
182 | |||
183 | this.geomType = function () { | ||
184 | return this.GEOM_TYPE_CUBIC_BEZIER; | ||
185 | }; | ||
186 | |||
187 | this.setWidth = function (newW) { | ||
188 | if (newW<1) { | ||
189 | newW=1; //clamp minimum width to 1 | ||
190 | } | ||
191 | |||
192 | //scale the contents of this subpath to lie within this width | ||
193 | //determine the scale factor by comparing with the old width | ||
194 | var oldWidth = this._BBoxMax[0]-this._BBoxMin[0]; | ||
195 | if (oldWidth<1) { | ||
196 | oldWidth=1; | ||
197 | } | ||
198 | |||
199 | var scaleX = newW/oldWidth; | ||
200 | if (scaleX===1) { | ||
201 | return; //no need to do anything | ||
202 | } | ||
203 | |||
204 | //scale the anchor point positions such that the width of the bbox is the newW | ||
205 | var origX = this._BBoxMin[0]; | ||
206 | var numAnchors = this._Anchors.length; | ||
207 | for (var i=0;i<numAnchors;i++){ | ||
208 | //compute the distance from the bboxMin | ||
209 | var oldW = this._Anchors[i].getPosX() - origX; | ||
210 | var prevW = this._Anchors[i].getPrevX() - origX; | ||
211 | var nextW = this._Anchors[i].getNextX() - origX; | ||
212 | |||
213 | this._Anchors[i].setPos(origX + oldW*scaleX,this._Anchors[i].getPosY(),this._Anchors[i].getPosZ()); | ||
214 | this._Anchors[i].setPrevPos(origX + prevW*scaleX,this._Anchors[i].getPrevY(),this._Anchors[i].getPrevZ()); | ||
215 | this._Anchors[i].setNextPos(origX + nextW*scaleX,this._Anchors[i].getNextY(),this._Anchors[i].getNextZ()); | ||
216 | } | ||
217 | this.makeDirty(); | ||
218 | }; | ||
219 | |||
220 | this.setHeight = function (newH) { | ||
221 | if (newH<1) { | ||
222 | newH=1; //clamp minimum width to 1 | ||
223 | } | ||
224 | //scale the contents of this subpath to lie within this height | ||
225 | //determine the scale factor by comparing with the old height | ||
226 | var oldHeight = this._BBoxMax[1]-this._BBoxMin[1]; | ||
227 | if (oldHeight<1){ | ||
228 | oldHeight=1; | ||
229 | } | ||
230 | |||
231 | var scaleY = newH/oldHeight; | ||
232 | if (scaleY===1){ | ||
233 | return; //no need to do anything | ||
234 | } | ||
235 | |||
236 | //scale the anchor point positions such that the height of the bbox is the newH | ||
237 | var origY = this._BBoxMin[1]; | ||
238 | var numAnchors = this._Anchors.length; | ||
239 | for (var i=0;i<numAnchors;i++){ | ||
240 | //compute the distance from the bboxMin | ||
241 | var oldW = this._Anchors[i].getPosY() - origY; | ||
242 | var prevW = this._Anchors[i].getPrevY() - origY; | ||
243 | var nextW = this._Anchors[i].getNextY() - origY; | ||