1 /* 2 Copyright 2008,2009 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software: you can redistribute it and/or modify 13 it under the terms of the GNU Lesser General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 JSXGraph is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public License 23 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 24 25 */ 26 /** 27 * Creates a new instance of Sector. 28 * @class Sector stores all style and functional properties that are required 29 * to draw a sector on a board. 30 * @param {JXG.Board} board Reference to the board the sector is drawn on. 31 * @param {JXG.Point} p1 Midpoint of the sector. 32 * @param {JXG.Point} p2 Point defining the sectors radius 33 * @param {JXG.Point} p3 This point defines the angle of the sectors section. 34 * @param {Array} ids Unique identifiers for the derived objects (arc, endpoint of the arc, line from p1 to p2, line from p1 to the endpoint of the arc) . 35 * Must be Strings. If null or an empty string is given to any of these, an unique id will be generated. 36 * @param {Array} names Names for the derived objects (arc, endpoint of the arc, line from p1 to p2, line from p1 to the endpoint of the arc) . 37 * Must be Strings. If null or an empty string is given to any of these, an unique id will be generated. 38 * @param {String} id Unique identifier for this object. If null or an empty string is given, 39 * an unique id will be generated by Board 40 * @param {String} name Not necessarily unique name, displayed on the board. If null or an 41 * empty string is given, an unique name will be generated. 42 * @see JXG.Board#addSector 43 * @constructor 44 * @extends JXG.GeometryElement 45 */ 46 47 JXG.createSector = function(board, parents, attributes) { 48 var el, defaults, key, options; 49 50 // Alles 3 Punkte? 51 if ( !(JXG.isPoint(parents[0]) && JXG.isPoint(parents[1]) && JXG.isPoint(parents[2]))) { 52 throw new Error("JSXGraph: Can't create Sector with parent types '" + 53 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + 54 (typeof parents[2]) + "'."); 55 } 56 57 // Read the default values from Options and use them in case they are not set by the user 58 // in attributes 59 defaults = {withLabel:JXG.readOption(board.options,'elements','withLabel'), 60 layer:JXG.readOption(board.options,'layer','sector'), 61 useDirection:false}; // useDirection is necessary for circumCircleSectors 62 defaults['strokeWidth'] = board.options.elements['strokeWidth']; 63 options = board.options.sector; 64 for (key in options) { 65 defaults[key] = options[key]; 66 } 67 attributes = JXG.checkAttributes(attributes, defaults); 68 69 el = board.create('curve',[[0],[0]],attributes); 70 el.type = JXG.OBJECT_TYPE_SECTOR; 71 /** 72 * Midpoint of the sector. 73 * @type JXG.Point 74 */ 75 el.point1 = JXG.getReference(board, parents[0]); 76 el.midpoint = el.point1; 77 /** 78 * Point defining the arcs circle. 79 * @type JXG.Point 80 */ 81 el.point2 = JXG.getReference(board, parents[1]); 82 /** 83 * The point defining the angle of the arc. 84 * @type JXG.Point 85 */ 86 el.point3 = JXG.getReference(board, parents[2]); 87 /* Add arc as child to defining points */ 88 el.point1.addChild(el); 89 el.point2.addChild(el); 90 el.point3.addChild(el); 91 92 el.useDirection = attributes['useDirection']; // useDirection is necessary for circumCircleSectors 93 94 el.updateDataArray = function() { 95 var A = this.point2, 96 B = this.point1, 97 C = this.point3, 98 beta, co, si, matrix, 99 phi = JXG.Math.Geometry.rad(A,B,C), 100 i, 101 //n = 100, 102 n = Math.ceil(phi/Math.PI*90)+1, 103 delta = phi/n, //Math.PI/90.0, 104 x = B.X(), 105 y = B.Y(), 106 v, 107 det, p0c, p1c, p2c; 108 109 if (this.useDirection) { // This is true for circumCircleArcs. In that case there is 110 // a fourth parent element: [midpoint, point1, point3, point2] 111 var det, 112 p0c = parents[1].coords.usrCoords, 113 p1c = parents[3].coords.usrCoords, 114 p2c = parents[2].coords.usrCoords; 115 det = (p0c[1]-p2c[1])*(p0c[2]-p1c[2]) - (p0c[2]-p2c[2])*(p0c[1]-p1c[1]); 116 if(det < 0) { 117 this.point2 = parents[1]; 118 this.point3 = parents[2]; 119 } 120 else { 121 this.point2 = parents[2]; 122 this.point3 = parents[1]; 123 } 124 } 125 this.dataX = [B.X(),A.X()]; 126 this.dataY = [B.Y(),A.Y()]; 127 for (beta=delta,i=1; i<=n; i++, beta+=delta) { 128 co = Math.cos(beta); 129 si = Math.sin(beta); 130 matrix = [[1, 0, 0], 131 [x*(1-co)+y*si,co,-si], 132 [y*(1-co)-x*si,si, co]]; 133 v = JXG.Math.matVecMult(matrix,A.coords.usrCoords); 134 this.dataX.push(v[1]/v[0]); 135 this.dataY.push(v[2]/v[0]); 136 } 137 this.dataX.push(B.X()); 138 this.dataY.push(B.Y()); 139 }; 140 141 /** 142 * Calculates the arcs radius. 143 * @type float 144 * @return The arcs radius 145 */ 146 el.Radius = function() { 147 return this.point2.Dist(this.point1); 148 }; 149 150 /** 151 * @deprecated 152 */ 153 el.getRadius = function() { 154 return this.Radius(); 155 }; 156 157 /** 158 * Checks whether (x,y) is within the sector defined by the arc. 159 * @param {int} x Coordinate in x direction, screen coordinates. 160 * @param {int} y Coordinate in y direction, screen coordinates. 161 * @return {bool} True if (x,y) is within the sector defined by the arc, False otherwise. 162 */ 163 el.hasPointSector = function (x, y) { 164 var checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board), 165 r = this.Radius(), 166 dist = this.point1.coords.distance(JXG.COORDS_BY_USER,checkPoint), 167 has = (dist<r), 168 angle; 169 170 if(has) { 171 angle = JXG.Math.Geometry.rad(this.point2,this.point1,checkPoint.usrCoords.slice(1)); 172 if (angle>JXG.Math.Geometry.rad(this.point2,this.point1,this.point3)) { has = false; } 173 } 174 return has; 175 }; 176 177 /** 178 * return TextAnchor 179 */ 180 el.getTextAnchor = function() { 181 return this.point1.coords; 182 }; 183 184 /** 185 * return LabelAnchor 186 */ 187 el.getLabelAnchor = function() { 188 var angle = JXG.Math.Geometry.rad(this.point2, this.point1, this.point3), 189 dx = 10/(this.board.stretchX), 190 dy = 10/(this.board.stretchY), 191 p2c = this.point2.coords.usrCoords, 192 pmc = this.point1.coords.usrCoords, 193 bxminusax = p2c[1] - pmc[1], 194 byminusay = p2c[2] - pmc[2], 195 coords, vecx, vecy, len; 196 197 if(this.label.content != null) { 198 this.label.content.relativeCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0],this.board); 199 } 200 201 coords = new JXG.Coords(JXG.COORDS_BY_USER, 202 [pmc[1]+ Math.cos(angle*0.5)*bxminusax - Math.sin(angle*0.5)*byminusay, 203 pmc[2]+ Math.sin(angle*0.5)*bxminusax + Math.cos(angle*0.5)*byminusay], 204 this.board); 205 206 vecx = coords.usrCoords[1] - pmc[1]; 207 vecy = coords.usrCoords[2] - pmc[2]; 208 209 len = Math.sqrt(vecx*vecx+vecy*vecy); 210 vecx = vecx*(len+dx)/len; 211 vecy = vecy*(len+dy)/len; 212 213 return new JXG.Coords(JXG.COORDS_BY_USER, [pmc[1]+vecx,pmc[2]+vecy],this.board); 214 }; 215 216 el.prepareUpdate().update(); 217 218 return el; 219 }; 220 221 JXG.JSXGraph.registerElement('sector', JXG.createSector); 222 223 224 /** 225 * Creates a new circumcircle sector through three defining points. 226 * @param {JXG.Board} board The board the sector is put on. 227 * @param {Array} parents Array of three points defining the circumcircle sector. 228 * @param {Object} attributs Object containing properties for the element such as stroke-color and visibility. See @see JXG.GeometryElement#setProperty 229 * @type JXG.Sector 230 * @return Reference to the created arc object. 231 */ 232 JXG.createCircumcircleSector = function(board, parents, attributes) { 233 var el, mp, idmp='', det; 234 235 attributes = JXG.checkAttributes(attributes,{withLabel:JXG.readOption(board.options,'sector','withLabel'), layer:null}); 236 if(attributes['id'] != null) { 237 idmp = attributes['id']+'_mp'; 238 } 239 240 // Alles 3 Punkte? 241 if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) && (JXG.isPoint(parents[2]))) { 242 mp = board.create('circumcirclemidpoint',[parents[0], parents[1], parents[2]], {id:idmp, withLabel:false, visible:false}); 243 attributes.useDirection = true; 244 el = board.create('sector', [mp,parents[0],parents[2],parents[1]], attributes); 245 } // Ansonsten eine fette Exception um die Ohren hauen 246 else { 247 throw new Error("JSXGraph: Can't create circumcircle sector with parent types '" + 248 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'."); 249 } 250 251 return el; 252 }; 253 254 JXG.JSXGraph.registerElement('circumcirclesector', JXG.createCircumcircleSector); 255 256 257 /** 258 * Creates a new angle. 259 * @param {JXG.Board} board The board the angle is put on. 260 * @param {Array} parents Array of three points defining the angle. 261 * @param {Object} attributes Object containing properties for the element such as stroke-color and visibility. @see JXG.GeometryElement#setProperty 262 * @type JXG.Angle 263 * @return Reference to the created angle object. 264 */ 265 JXG.createAngle = function(board, parents, attributes) { 266 var el, p, defaults, options, key, text, 267 possibleNames = ['α', 'β', 'γ', 'δ', 'ε', 'ζ', '&eta', 'θ', 268 'ι', 'κ', 'λ', 'μ', 'ν', 'ξ', 'ο', 'π', 'ρ', 269 'ς', 'σ', 'τ', 'υ', 'φ', 'χ', 'ψ', 'ω'], 270 i = 0, tmp, 271 j, x, pre, post, found; 272 273 274 defaults = {withLabel:JXG.readOption(board.options,'elements','withLabel'), 275 layer:JXG.readOption(board.options,'layer','angle'), 276 radius:JXG.readOption(board.options,'angle','radius'), 277 text:''}; 278 options = board.options.angle; 279 for (key in options) { 280 defaults[key] = options[key]; 281 } 282 attributes = JXG.checkAttributes(attributes, defaults); 283 284 // Alles 3 Punkte? 285 if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) && (JXG.isPoint(parents[2]))) { 286 // If empty, create a new name 287 text = attributes.text; 288 if(text == '') { 289 while(i < possibleNames.length) { 290 j=i; 291 x = possibleNames[i]; 292 for(el in board.objects) { 293 if(board.objects[el].type == JXG.OBJECT_TYPE_ANGLE) { 294 if(board.objects[el].text == x) { 295 i++; 296 break; 297 } 298 } 299 } 300 if(i==j) { 301 text = x; 302 i = possibleNames.length+1; 303 } 304 } 305 if(i == possibleNames.length) { 306 pre = 'α_{'; 307 post = '}'; 308 found = false; 309 j=0; 310 while(!found) { 311 for(el in board.objects) { 312 if(board.objects[el].type == JXG.OBJECT_TYPE_ANGLE) { 313 if(board.objects[el].text == (pre+j+post)) { 314 found = true; 315 break; 316 } 317 } 318 } 319 if(found) { 320 found= false; 321 } 322 else { 323 found = true; 324 text = (pre+j+post); 325 } 326 } 327 } 328 } 329 p = board.create('point', [ 330 function(){ 331 var A = parents[0], B = parents[1], 332 r = attributes.radius, 333 d = B.Dist(A); 334 return [B.X()+(A.X()-B.X())*r/d,B.Y()+(A.Y()-B.Y())*r/d]; 335 }], {withLabel:false, visible:false}); 336 for (i=0;i<3;i++) { 337 JXG.getReference(board,parents[i]).addChild(p); 338 } 339 el = board.create('sector', [parents[1],p,parents[2]],attributes); 340 el.type = JXG.OBJECT_TYPE_ANGLE; 341 if (el.withLabel) { 342 el.label.content.setText(text); 343 } 344 JXG.getReference(board,parents[0]).addChild(el); 345 346 /** 347 * return LabelAnchor 348 */ 349 el.getLabelAnchor = function() { 350 var angle = JXG.Math.Geometry.rad(this.point2, this.point1, this.point3), 351 dx = 10/(this.board.stretchX), 352 dy = 10/(this.board.stretchY), 353 p2c = this.point2.coords.usrCoords, 354 pmc = this.point1.coords.usrCoords, 355 bxminusax = p2c[1] - pmc[1], 356 byminusay = p2c[2] - pmc[2], 357 coords, vecx, vecy, len; 358 359 if(this.label.content != null) { 360 this.label.content.relativeCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0],this.board); 361 } 362 363 coords = new JXG.Coords(JXG.COORDS_BY_USER, 364 [pmc[1]+ Math.cos(angle*0.5*1.125)*bxminusax - Math.sin(angle*0.5*1.125)*byminusay, 365 pmc[2]+ Math.sin(angle*0.5*1.125)*bxminusax + Math.cos(angle*0.5*1.125)*byminusay], 366 this.board); 367 368 vecx = coords.usrCoords[1] - pmc[1]; 369 vecy = coords.usrCoords[2] - pmc[2]; 370 371 len = Math.sqrt(vecx*vecx+vecy*vecy); 372 vecx = vecx*(len+dx)/len; 373 vecy = vecy*(len+dy)/len; 374 375 return new JXG.Coords(JXG.COORDS_BY_USER, [pmc[1]+vecx,pmc[2]+vecy],this.board); 376 }; 377 378 } // Ansonsten eine fette Exception um die Ohren hauen 379 else { 380 throw new Error("JSXGraph: Can't create angle with parent types '" + 381 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'."); 382 } 383 384 return el; 385 }; 386 387 JXG.JSXGraph.registerElement('angle', JXG.createAngle); 388 389