1 /* 2 Copyright 2008-2011 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 * @fileoverview In this file the geometry object Arc is defined. Arc stores all 28 * style and functional properties that are required to draw an arc on a board. 29 */ 30 31 /** 32 * @class An arc is a segment of the circumference of a circle. It is defined by a center, one point that 33 * defines the radius, and a third point that defines the angle of the arc. 34 * @pseudo 35 * @name Arc 36 * @augments Curve 37 * @constructor 38 * @type JXG.Curve 39 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 40 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The result will be an arc of a circle around p1 through p2. The arc is drawn 41 * counter-clockwise from p2 to p3. 42 * @example 43 * // Create an arc out of three free points 44 * var p1 = board.create('point', [2.0, 2.0]); 45 * var p2 = board.create('point', [1.0, 0.5]); 46 * var p3 = board.create('point', [3.5, 1.0]); 47 * 48 * var a = board.create('arc', [p1, p2, p3]); 49 * </pre><div id="114ef584-4a5e-4686-8392-c97501befb5b" style="width: 300px; height: 300px;"></div> 50 * <script type="text/javascript"> 51 * (function () { 52 * var board = JXG.JSXGraph.initBoard('114ef584-4a5e-4686-8392-c97501befb5b', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 53 * p1 = board.create('point', [2.0, 2.0]), 54 * p2 = board.create('point', [1.0, 0.5]), 55 * p3 = board.create('point', [3.5, 1.0]), 56 * 57 * a = board.create('arc', [p1, p2, p3]); 58 * })(); 59 * </script><pre> 60 */ 61 JXG.createArc = function(board, parents, attributes) { 62 var el, attr, i; 63 64 65 // this method is used to create circumccirclearcs, too. if a circumcirclearc is created we get a fourth 66 // point, that's why we need to check that case, too. 67 if(!(parents = JXG.checkParents('arc', parents, [ 68 [JXG.OBJECT_CLASS_POINT, JXG.OBJECT_CLASS_POINT, JXG.OBJECT_CLASS_POINT], 69 [JXG.OBJECT_CLASS_POINT, JXG.OBJECT_CLASS_POINT, JXG.OBJECT_CLASS_POINT, JXG.OBJECT_CLASS_POINT]]))) { 70 throw new Error("JSXGraph: Can't create Arc with parent types '" + 71 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + 72 (typeof parents[2]) + "'." + 73 "\nPossible parent types: [point,point,point]"); 74 } 75 76 attr = JXG.copyAttributes(attributes, board.options, 'arc'); 77 el = board.create('curve', [[0],[0]], attr); 78 79 el.elType = 'arc'; 80 81 el.parents = []; 82 for (i = 0; i < parents.length; i++) { 83 if (parents[i].id) { 84 el.parents.push(parents[i].id); 85 } 86 } 87 88 /** 89 * documented in JXG.GeometryElement 90 * @ignore 91 */ 92 el.type = JXG.OBJECT_TYPE_ARC; 93 94 /** 95 * Center of the arc. 96 * @memberOf Arc.prototype 97 * @name center 98 * @type JXG.Point 99 */ 100 el.center = JXG.getReference(board, parents[0]); 101 102 /** 103 * Point defining the arc's radius. 104 * @memberOf Arc.prototype 105 * @name radiuspoint 106 * @type JXG.Point 107 */ 108 el.radiuspoint = JXG.getReference(board, parents[1]); 109 el.point2 = el.radiuspoint; 110 111 /** 112 * The point defining the arc's angle. 113 * @memberOf Arc.prototype 114 * @name anglepoint 115 * @type JXG.Point 116 */ 117 el.anglepoint = JXG.getReference(board, parents[2]); 118 el.point3 = el.anglepoint; 119 120 // Add arc as child to defining points 121 el.center.addChild(el); 122 el.radiuspoint.addChild(el); 123 el.anglepoint.addChild(el); 124 125 /** 126 * TODO 127 */ 128 el.useDirection = attr['usedirection']; // useDirection is necessary for circumCircleArcs 129 130 // documented in JXG.Curve 131 el.updateDataArray = function() { 132 var A = this.radiuspoint, 133 B = this.center, 134 C = this.anglepoint, 135 beta, co, si, matrix, 136 phi = JXG.Math.Geometry.rad(A,B,C), 137 i, 138 n = Math.ceil(phi/Math.PI*90)+1, 139 delta = phi/n, 140 x = B.X(), 141 y = B.Y(), 142 v, 143 det, p0c, p1c, p2c; 144 145 if (this.useDirection) { // This is true for circumCircleArcs. In that case there is 146 // a fourth parent element: [center, point1, point3, point2] 147 p0c = parents[1].coords.usrCoords; 148 p1c = parents[3].coords.usrCoords; 149 p2c = parents[2].coords.usrCoords; 150 det = (p0c[1]-p2c[1])*(p0c[2]-p1c[2]) - (p0c[2]-p2c[2])*(p0c[1]-p1c[1]); 151 if(det < 0) { 152 this.radiuspoint = parents[1]; 153 this.anglepoint = parents[2]; 154 } else { 155 this.radiuspoint = parents[2]; 156 this.anglepoint = parents[1]; 157 } 158 } 159 this.dataX = [A.X()]; 160 this.dataY = [A.Y()]; 161 162 for (beta=delta,i=1; i<=n; i++, beta+=delta) { 163 co = Math.cos(beta); 164 si = Math.sin(beta); 165 matrix = [[1, 0, 0], 166 [x*(1-co)+y*si,co,-si], 167 [y*(1-co)-x*si,si, co]]; 168 v = JXG.Math.matVecMult(matrix,A.coords.usrCoords); 169 this.dataX.push(v[1]/v[0]); 170 this.dataY.push(v[2]/v[0]); 171 } 172 173 this.updateStdform(); 174 this.updateQuadraticform(); 175 }; 176 177 /** 178 * Determines the arc's current radius. I.e. the distance between {@link Arc#center} and {@link Arc#radiuspoint}. 179 * @memberOf Arc.prototype 180 * @name Radius 181 * @function 182 * @returns {Number} The arc's radius 183 */ 184 el.Radius = function() { 185 return this.radiuspoint.Dist(this.center); 186 }; 187 188 /** 189 * @deprecated Use {@link Arc#Radius} 190 * @memberOf Arc.prototype 191 * @name getRadius 192 * @function 193 * @returns {Number} 194 */ 195 el.getRadius = function() { 196 return this.Radius(); 197 }; 198 199 // documented in geometry element 200 el.hasPoint = function (x, y) { 201 var prec = this.board.options.precision.hasPoint/(this.board.unitX), 202 checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board), 203 r = this.Radius(), 204 dist = this.center.coords.distance(JXG.COORDS_BY_USER,checkPoint), 205 has = (Math.abs(dist-r) < prec), 206 angle; 207 208 if(has) { 209 angle = JXG.Math.Geometry.rad(this.radiuspoint,this.center,checkPoint.usrCoords.slice(1)); 210 if (angle>JXG.Math.Geometry.rad(this.radiuspoint,this.center,this.anglepoint)) { has = false; } 211 } 212 return has; 213 }; 214 215 /** 216 * Checks whether (x,y) is within the sector defined by the arc. 217 * @memberOf Arc.prototype 218 * @name hasPointSector 219 * @function 220 * @param {Number} x Coordinate in x direction, screen coordinates. 221 * @param {Number} y Coordinate in y direction, screen coordinates. 222 * @returns {Boolean} True if (x,y) is within the sector defined by the arc, False otherwise. 223 */ 224 el.hasPointSector = function (x, y) { 225 var checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board), 226 r = this.Radius(), 227 dist = this.center.coords.distance(JXG.COORDS_BY_USER,checkPoint), 228 has = (dist<r), 229 angle; 230 231 if(has) { 232 angle = JXG.Math.Geometry.rad(this.radiuspoint,this.center,checkPoint.usrCoords.slice(1)); 233 if (angle>JXG.Math.Geometry.rad(this.radiuspoint,this.center,this.anglepoint)) { has = false; } 234 } 235 return has; 236 }; 237 238 // documented in geometry element 239 el.getTextAnchor = function() { 240 return this.center.coords; 241 }; 242 243 // documented in geometry element 244 el.getLabelAnchor = function() { 245 var angle = JXG.Math.Geometry.rad(this.radiuspoint, this.center, this.anglepoint), 246 dx = 10/(this.board.unitX), 247 dy = 10/(this.board.unitY), 248 p2c = this.point2.coords.usrCoords, 249 pmc = this.center.coords.usrCoords, 250 bxminusax = p2c[1] - pmc[1], 251 byminusay = p2c[2] - pmc[2], 252 coords, vecx, vecy, len; 253 254 if(this.label.content != null) { 255 this.label.content.relativeCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0],this.board); 256 } 257 258 coords = new JXG.Coords(JXG.COORDS_BY_USER, 259 [pmc[1]+ Math.cos(angle*0.5)*bxminusax - Math.sin(angle*0.5)*byminusay, 260 pmc[2]+ Math.sin(angle*0.5)*bxminusax + Math.cos(angle*0.5)*byminusay], 261 this.board); 262 263 vecx = coords.usrCoords[1] - pmc[1]; 264 vecy = coords.usrCoords[2] - pmc[2]; 265 266 len = Math.sqrt(vecx*vecx+vecy*vecy); 267 vecx = vecx*(len+dx)/len; 268 vecy = vecy*(len+dy)/len; 269 270 return new JXG.Coords(JXG.COORDS_BY_USER, [pmc[1]+vecx,pmc[2]+vecy], this.board); 271 }; 272 273 /** 274 * TODO description 275 */ 276 el.updateQuadraticform = function () { 277 var m = this.center, 278 mX = m.X(), mY = m.Y(), r = this.Radius(); 279 this.quadraticform = [[mX*mX+mY*mY-r*r,-mX,-mY], 280 [-mX,1,0], 281 [-mY,0,1] 282 ]; 283 }; 284 285 /** 286 * TODO description 287 */ 288 el.updateStdform = function () { 289 this.stdform[3] = 0.5; 290 this.stdform[4] = this.Radius(); 291 this.stdform[1] = -this.center.coords.usrCoords[1]; 292 this.stdform[2] = -this.center.coords.usrCoords[2]; 293 this.normalize(); 294 }; 295 296 el.prepareUpdate().update(); 297 return el; 298 }; 299 300 JXG.JSXGraph.registerElement('arc', JXG.createArc); 301 302 /** 303 * @class A semicircle is a special arc defined by two points. The arc hits both points. 304 * @pseudo 305 * @name Semicircle 306 * @augments Arc 307 * @constructor 308 * @type Arc 309 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 310 * @param {JXG.Point_JXG.Point} p1,p2 The result will be a composition of an arc drawn clockwise from <tt>p1</tt> and 311 * <tt>p2</tt> and the midpoint of <tt>p1</tt> and <tt>p2</tt>. 312 * @example 313 * // Create an arc out of three free points 314 * var p1 = board.create('point', [4.5, 2.0]); 315 * var p2 = board.create('point', [1.0, 0.5]); 316 * 317 * var a = board.create('semicircle', [p1, p2]); 318 * </pre><div id="5385d349-75d7-4078-b732-9ae808db1b0e" style="width: 300px; height: 300px;"></div> 319 * <script type="text/javascript"> 320 * (function () { 321 * var board = JXG.JSXGraph.initBoard('5385d349-75d7-4078-b732-9ae808db1b0e', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 322 * p1 = board.create('point', [4.5, 2.0]), 323 * p2 = board.create('point', [1.0, 0.5]), 324 * 325 * sc = board.create('semicircle', [p1, p2]); 326 * })(); 327 * </script><pre> 328 */ 329 JXG.createSemicircle = function(board, parents, attributes) { 330 var el, mp, attr; 331 332 333 // we need 2 points 334 if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) ) { 335 336 attr = JXG.copyAttributes(attributes, board.options, 'semicircle', 'midpoint'); 337 mp = board.create('midpoint', [parents[0], parents[1]], attr); 338 339 mp.dump = false; 340 341 attr = JXG.copyAttributes(attributes, board.options, 'semicircle'); 342 el = board.create('arc', [mp, parents[1], parents[0]], attr); 343 344 el.elType = 'semicircle'; 345 el.parents = [parents[0].id, parents[1].id]; 346 el.subs = { 347 midpoint: mp 348 }; 349 350 /** 351 * The midpoint of the two defining points. 352 * @memberOf Semicircle.prototype 353 * @name midpoint 354 * @type Midpoint 355 */ 356 el.midpoint = mp; 357 } else 358 throw new Error("JSXGraph: Can't create Semicircle with parent types '" + 359 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 360 "\nPossible parent types: [point,point]"); 361 362 return el; 363 }; 364 365 JXG.JSXGraph.registerElement('semicircle', JXG.createSemicircle); 366 367 /** 368 * @class A circumcircle arc is an {@link Arc} defined by three points. All three points lie on the arc. 369 * @pseudo 370 * @name CircumcircleArc 371 * @augments Arc 372 * @constructor 373 * @type Arc 374 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 375 * @param {JXG.Point_JXG.Point_JXG.Point} p1,p2,p3 The result will be a composition of an arc of the circumcircle of 376 * <tt>p1</tt>, <tt>p2</tt>, and <tt>p3</tt> and the midpoint of the circumcircle of the three points. The arc is drawn 377 * counter-clockwise from <tt>p1</tt> over <tt>p2</tt> to <tt>p3</tt>. 378 * @example 379 * // Create a circum circle arc out of three free points 380 * var p1 = board.create('point', [2.0, 2.0]); 381 * var p2 = board.create('point', [1.0, 0.5]); 382 * var p3 = board.create('point', [3.5, 1.0]); 383 * 384 * var a = board.create('arc', [p1, p2, p3]); 385 * </pre><div id="87125fd4-823a-41c1-88ef-d1a1369504e3" style="width: 300px; height: 300px;"></div> 386 * <script type="text/javascript"> 387 * (function () { 388 * var board = JXG.JSXGraph.initBoard('87125fd4-823a-41c1-88ef-d1a1369504e3', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}), 389 * p1 = board.create('point', [2.0, 2.0]), 390 * p2 = board.create('point', [1.0, 0.5]), 391 * p3 = board.create('point', [3.5, 1.0]), 392 * 393 * cca = board.create('circumcirclearc', [p1, p2, p3]); 394 * })(); 395 * </script><pre> 396 */ 397 JXG.createCircumcircleArc = function(board, parents, attributes) { 398 var el, mp, attr; 399 400 // We need three points 401 if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) && (JXG.isPoint(parents[2]))) { 402 403 attr = JXG.copyAttributes(attributes, board.options, 'circumcirclearc', 'center'); 404 mp = board.create('circumcenter',[parents[0], parents[1], parents[2]], attr); 405 406 mp.dump = false; 407 408 attr = JXG.copyAttributes(attributes, board.options, 'circumcirclearc'); 409 attr.usedirection = true; 410 el = board.create('arc', [mp, parents[0], parents[2], parents[1]], attr); 411 412 el.elType = 'circumcirclearc'; 413 el.parents = [parents[0].id, parents[1].id, parents[2].id]; 414 el.subs = { 415 center: mp 416 }; 417 418 /** 419 * The midpoint of the circumcircle of the three points defining the circumcircle arc. 420 * @memberOf CircumcircleArc.prototype 421 * @name center 422 * @type Circumcenter 423 */ 424 el.center = mp; 425 } else 426 throw new Error("JSXGraph: create Circumcircle Arc with parent types '" + 427 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 428 "\nPossible parent types: [point,point,point]"); 429 430 return el; 431 }; 432 433 JXG.JSXGraph.registerElement('circumcirclearc', JXG.createCircumcircleArc); 434