1 /* 2 Copyright 2008-2010 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 This element is used to provide a constructor for arc elements. 33 * @pseudo 34 * @description An is a segment of the circumference of a circle. 35 * @name Arc 36 * @augments Curve 37 * @constructor 38 * @type JXG.Curve 39 * @throws {Exception} 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 * var arex1_board = JXG.JSXGraph.initBoard('114ef584-4a5e-4686-8392-c97501befb5b', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 52 * var arex1_p1 = arex1_board.create('point', [2.0, 2.0]); 53 * var arex1_p2 = arex1_board.create('point', [1.0, 0.5]); 54 * var arex1_p3 = arex1_board.create('point', [3.5, 1.0]); 55 * 56 * var arex1_a = arex1_board.create('arc', [arex1_p1, arex1_p2, arex1_p3]); 57 * </script><pre> 58 */ 59 JXG.createArc = function(board, parents, attributes) { 60 var el, defaults, key, options; 61 62 // Alles 3 Punkte? 63 if ( !(JXG.isPoint(parents[0]) && JXG.isPoint(parents[1]) && JXG.isPoint(parents[2]))) { 64 throw new Error("JSXGraph: Can't create Arc with parent types '" + 65 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + 66 (typeof parents[2]) + "'." + 67 "\nPossible parent types: [point,point,point]"); 68 } 69 70 // Read the default values from Options and use them in case they are not set by the user 71 // in attributes 72 defaults = { 73 withLabel: JXG.readOption(board.options,'elements','withLabel'), 74 layer: JXG.readOption(board.options,'layer','arc'), 75 useDirection:false // useDirection is necessary for circumCircleArcs 76 }; 77 defaults['strokeWidth'] = board.options.elements['strokeWidth']; 78 options = board.options.arc; 79 for (key in options) { 80 defaults[key] = options[key]; 81 } 82 attributes = JXG.checkAttributes(attributes, defaults); 83 84 el = board.create('curve',[[0],[0]],attributes); 85 el.type = JXG.OBJECT_TYPE_ARC; 86 87 /** 88 * Midpoint of the arc. 89 * @type JXG.Point 90 */ 91 el.midpoint = JXG.getReference(board, parents[0]); 92 93 /** 94 * Point defining the arcs circle. 95 * @type JXG.Point 96 */ 97 el.point2 = JXG.getReference(board, parents[1]); 98 99 /** 100 * The point defining the angle of the arc. 101 * @type JXG.Point 102 */ 103 el.point3 = JXG.getReference(board, parents[2]); 104 105 /* Add arc as child to defining points */ 106 el.midpoint.addChild(el); 107 el.point2.addChild(el); 108 el.point3.addChild(el); 109 110 el.useDirection = attributes['useDirection']; // useDirection is necessary for circumCircleArcs 111 112 el.updateDataArray = function() { 113 var A = this.point2, 114 B = this.midpoint, 115 C = this.point3, 116 beta, co, si, matrix, 117 phi = JXG.Math.Geometry.rad(A,B,C), 118 i, 119 //n = 100, 120 n = Math.ceil(phi/Math.PI*90)+1, 121 delta = phi/n, //Math.PI/90.0, 122 x = B.X(), 123 y = B.Y(), 124 v, 125 det, p0c, p1c, p2c; 126 127 if (this.useDirection) { // This is true for circumCircleArcs. In that case there is 128 // a fourth parent element: [midpoint, point1, point3, point2] 129 p0c = parents[1].coords.usrCoords; 130 p1c = parents[3].coords.usrCoords; 131 p2c = parents[2].coords.usrCoords; 132 det = (p0c[1]-p2c[1])*(p0c[2]-p1c[2]) - (p0c[2]-p2c[2])*(p0c[1]-p1c[1]); 133 if(det < 0) { 134 this.point2 = parents[1]; 135 this.point3 = parents[2]; 136 } else { 137 this.point2 = parents[2]; 138 this.point3 = parents[1]; 139 } 140 } 141 this.dataX = [A.X()]; 142 this.dataY = [A.Y()]; 143 144 for (beta=delta,i=1; i<=n; i++, beta+=delta) { 145 co = Math.cos(beta); 146 si = Math.sin(beta); 147 matrix = [[1, 0, 0], 148 [x*(1-co)+y*si,co,-si], 149 [y*(1-co)-x*si,si, co]]; 150 v = JXG.Math.matVecMult(matrix,A.coords.usrCoords); 151 this.dataX.push(v[1]/v[0]); 152 this.dataY.push(v[2]/v[0]); 153 } 154 }; 155 156 /** 157 * Calculates the arcs radius. 158 * @returns {Number} The arcs radius 159 */ 160 el.Radius = function() { 161 return this.point2.Dist(this.midpoint); 162 }; 163 164 /** 165 * @deprecated Use Radius() 166 */ 167 el.getRadius = function() { 168 return this.Radius(); 169 }; 170 171 /** 172 *Checks whether (x,y) is near the arc. 173 * @param {Number} x Coordinate in x direction, screen coordinates. 174 * @param {Number} y Coordinate in y direction, screen coordinates. 175 * @returns {Boolean} True if (x,y) is near the arc, False otherwise. 176 */ 177 el.hasPoint = function (x, y) { 178 var prec = this.board.options.precision.hasPoint/(this.board.stretchX), 179 checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board), 180 r = this.Radius(), 181 dist = this.midpoint.coords.distance(JXG.COORDS_BY_USER,checkPoint), 182 has = (Math.abs(dist-r) < prec), 183 angle; 184 185 if(has) { 186 angle = JXG.Math.Geometry.rad(this.point2,this.midpoint,checkPoint.usrCoords.slice(1)); 187 if (angle>JXG.Math.Geometry.rad(this.point2,this.midpoint,this.point3)) { has = false; } 188 } 189 return has; 190 }; 191 192 /** 193 * Checks whether (x,y) is within the sector defined by the arc. 194 * @param {Number} x Coordinate in x direction, screen coordinates. 195 * @param {Number} y Coordinate in y direction, screen coordinates. 196 * @returns {Boolean} True if (x,y) is within the sector defined by the arc, False otherwise. 197 */ 198 el.hasPointSector = function (x, y) { 199 var checkPoint = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board), 200 r = this.Radius(), 201 dist = this.midpoint.coords.distance(JXG.COORDS_BY_USER,checkPoint), 202 has = (dist<r), 203 angle; 204 205 if(has) { 206 angle = JXG.Math.Geometry.rad(this.point2,this.midpoint,checkPoint.usrCoords.slice(1)); 207 if (angle>JXG.Math.Geometry.rad(this.point2,this.midpoint,this.point3)) { has = false; } 208 } 209 return has; 210 }; 211 212 /** 213 * @returns {JXG.Coords} Coordinates of the text's anchor. 214 */ 215 el.getTextAnchor = function() { 216 return this.midpoint.coords; 217 }; 218 219 /** 220 * @returns {JXG.Coords} Coordinates of the label's anchor 221 */ 222 el.getLabelAnchor = function() { 223 var angle = JXG.Math.Geometry.rad(this.point2, this.midpoint, this.point3), 224 dx = 10/(this.board.stretchX), 225 dy = 10/(this.board.stretchY), 226 p2c = this.point2.coords.usrCoords, 227 pmc = this.midpoint.coords.usrCoords, 228 bxminusax = p2c[1] - pmc[1], 229 byminusay = p2c[2] - pmc[2], 230 coords, vecx, vecy, len; 231 232 if(this.label.content != null) { 233 this.label.content.relativeCoords = new JXG.Coords(JXG.COORDS_BY_SCREEN, [0,0],this.board); 234 } 235 236 coords = new JXG.Coords(JXG.COORDS_BY_USER, 237 [pmc[1]+ Math.cos(angle*0.5)*bxminusax - Math.sin(angle*0.5)*byminusay, 238 pmc[2]+ Math.sin(angle*0.5)*bxminusax + Math.cos(angle*0.5)*byminusay], 239 this.board); 240 241 vecx = coords.usrCoords[1] - pmc[1]; 242 vecy = coords.usrCoords[2] - pmc[2]; 243 244 len = Math.sqrt(vecx*vecx+vecy*vecy); 245 vecx = vecx*(len+dx)/len; 246 vecy = vecy*(len+dy)/len; 247 248 return new JXG.Coords(JXG.COORDS_BY_USER, [pmc[1]+vecx,pmc[2]+vecy],this.board); 249 }; 250 251 el.prepareUpdate().update(); 252 return el; 253 }; 254 255 JXG.JSXGraph.registerElement('arc', JXG.createArc); 256 257 /** TODO: Documentation of those two elements below are to be written like the one above for ARC. 258 /** 259 * Creates a new semicircle. The semicircle is drawn clock-wise between the first and the second defining point. 260 * @param {JXG.Board} board The board the semicircle is put on. 261 * @param {Array} parents Array of two opposite points defining the semicircle. 262 * @param {Object} attributes Object containing properties for the element such as stroke-color and visibility. See @see JXG.GeometryElement#setProperty 263 * @returns {JXG.Arc} Reference to the created object. 264 */ 265 JXG.createSemicircle = function(board, parents, attributes) { 266 var el, mp, idmp = ''; 267 268 attributes = JXG.checkAttributes(attributes,{}); 269 if(attributes['id'] != null) { 270 idmp = attributes['id']+'_mp'; 271 } 272 // Alles 2 Punkte? 273 if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) ) { 274 mp = board.create('midpoint', [parents[0], parents[1]], {id:idmp, withLabel:false, visible:false}); 275 el = board.create('arc',[mp, parents[1], parents[0]],attributes); 276 } // Ansonsten eine fette Exception um die Ohren hauen 277 else 278 throw new Error("JSXGraph: Can't create Semicircle with parent types '" + 279 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 280 "\nPossible parent types: [point,point]"); 281 282 return el; 283 }; 284 285 JXG.JSXGraph.registerElement('semicircle', JXG.createSemicircle); 286 287 /** 288 * Creates a new circumcircle arc through three defining points. 289 * @param {JXG.Board} board The board the arc is put on. 290 * @param {Array} parents Array of three points defining the circumcircle arc. 291 * @param {Object} attributes Object containing properties for the element such as stroke-color and visibility. See @see JXG.GeometryElement#setProperty 292 * @returns {JXG.Arc} Reference to the created object. 293 */ 294 JXG.createCircumcircleArc = function(board, parents, attributes) { 295 var el, mp, idmp; 296 297 attributes = JXG.checkAttributes(attributes,{withLabel:JXG.readOption(board.options,'arc','withLabel'), layer:null}); 298 if(attributes['id'] != null) { 299 idmp = attributes['id']+'_mp'; 300 } 301 302 // Alles 3 Punkte? 303 if ( (JXG.isPoint(parents[0])) && (JXG.isPoint(parents[1])) && (JXG.isPoint(parents[2]))) { 304 mp = board.create('circumcirclemidpoint',[parents[0], parents[1], parents[2]], {id:idmp, withLabel:false, visible:false}); 305 attributes.useDirection = true; 306 el = board.create('arc', [mp,parents[0],parents[2],parents[1]], attributes); 307 } // Ansonsten eine fette Exception um die Ohren hauen 308 else 309 throw new Error("JSXGraph: create Circumcircle Arc with parent types '" + 310 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "' and '" + (typeof parents[2]) + "'." + 311 "\nPossible parent types: [point,point,point]"); 312 313 314 return el; 315 }; 316 317 JXG.JSXGraph.registerElement('circumcirclearc', JXG.createCircumcircleArc); 318