1 /* 2 Copyright 2008-2013 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 dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 base/constants 39 base/coords 40 base/element 41 math/math 42 math/statistics 43 utils/type 44 */ 45 46 /** 47 * @fileoverview In this file the geometry element Image is defined. 48 */ 49 50 define([ 51 'jxg', 'base/constants', 'base/coords', 'base/element', 'math/math', 'math/statistics', 'utils/type' 52 ], function (JXG, Const, Coords, GeometryElement, Mat, Statistics, Type) { 53 54 "use strict"; 55 56 /** 57 * Construct and handle images 58 * @class Image: 59 * It inherits from @see GeometryElement. 60 * @constructor 61 */ 62 JXG.Image = function (board, url, coords, size, attributes) { 63 this.constructor(board, attributes, Const.OBJECT_TYPE_IMAGE, Const.OBJECT_CLASS_OTHER); 64 65 this.initialCoords = new Coords(Const.COORDS_BY_USER, coords, this.board); // Still needed? 66 67 if (!Type.isFunction(coords[0]) && !Type.isFunction(coords[1])) { 68 this.isDraggable = true; 69 } 70 this.X = Type.createFunction(coords[0], this.board, ''); 71 this.Y = Type.createFunction(coords[1], this.board, ''); 72 this.Z = Type.createFunction(1, this.board, ''); 73 this.W = Type.createFunction(size[0], this.board, ''); 74 this.H = Type.createFunction(size[1], this.board, ''); 75 this.coords = new Coords(Const.COORDS_BY_USER, [this.X(), this.Y()], this.board); 76 this.usrSize = [this.W(), this.H()]; 77 this.size = [Math.abs(this.usrSize[0] * board.unitX), Math.abs(this.usrSize[1] * board.unitY)]; 78 this.url = url; 79 80 this.elType = 'image'; 81 82 // span contains the anchor point and the two vectors 83 // spanning the image rectangle. 84 this.span = [ 85 [this.Z(), this.X(), this.Y()], 86 [this.Z(), this.W(), 0], 87 [this.Z(), 0, this.H()] 88 ]; 89 90 this.parent = board.select(attributes.anchor); 91 92 this.id = this.board.setId(this, 'Im'); 93 94 this.board.renderer.drawImage(this); 95 if (!this.visProp.visible) { 96 this.board.renderer.hide(this); 97 } 98 99 this.methodMap = JXG.deepCopy(this.methodMap, { 100 addTransformation: 'addTransform', 101 trans: 'addTransform' 102 }); 103 }; 104 105 JXG.Image.prototype = new GeometryElement(); 106 107 JXG.extend(JXG.Image.prototype, /** @lends JXG.Image.prototype */ { 108 109 /** 110 * Checks whether (x,y) is over or near the image; 111 * @param {Number} x Coordinate in x direction, screen coordinates. 112 * @param {Number} y Coordinate in y direction, screen coordinates. 113 * @return {Boolean} True if (x,y) is over the image, False otherwise. 114 */ 115 hasPoint: function (x, y) { 116 var dx, dy, r, 117 c, v, p, dot, 118 len = this.transformations.length; 119 120 // Easy case: no transformation 121 if (len === 0) { 122 dx = x - this.coords.scrCoords[1]; 123 dy = this.coords.scrCoords[2] - y; 124 r = this.board.options.precision.hasPoint; 125 126 return dx >= -r && dx - this.size[0] <= r && 127 dy >= -r && dy - this.size[1] <= r; 128 } 129 130 // Image is transformed 131 c = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board); 132 // v is the vector from anchor point to the drag point 133 c = c.usrCoords; 134 v = [c[0] - this.span[0][0], 135 c[1] - this.span[0][1], 136 c[2] - this.span[0][2]]; 137 dot = Mat.innerProduct; // shortcut 138 139 // Project the drag point to the sides. 140 p = dot(v, this.span[1]); 141 if (0 <= p && p <= dot(this.span[1], this.span[1])) { 142 p = dot(v, this.span[2]); 143 144 if (0 <= p && p <= dot(this.span[2], this.span[2])) { 145 return true; 146 } 147 } 148 return false; 149 }, 150 151 /** 152 * Recalculate the coordinates of lower left corner and the width amd the height. 153 * @private 154 */ 155 update: function () { 156 if (this.needsUpdate) { 157 if (!this.visProp.frozen) { 158 this.updateCoords(); 159 } 160 this.usrSize = [this.W(), this.H()]; 161 this.size = [Math.abs(this.usrSize[0] * this.board.unitX), Math.abs(this.usrSize[1] * this.board.unitY)]; 162 this.updateTransform(); 163 this.updateSpan(); 164 } 165 return this; 166 }, 167 168 /** 169 * Send an update request to the renderer. 170 */ 171 updateRenderer: function () { 172 if (this.needsUpdate) { 173 this.board.renderer.updateImage(this); 174 this.needsUpdate = false; 175 } 176 177 return this; 178 }, 179 180 updateTransform: function () { 181 var i, len = this.transformations.length; 182 183 if (len > 0) { 184 for (i = 0; i < len; i++) { 185 this.transformations[i].update(); 186 } 187 } 188 189 return this; 190 }, 191 192 /** 193 * Updates the coordinates of the top left corner of the image. 194 */ 195 updateCoords: function () { 196 this.coords.setCoordinates(Const.COORDS_BY_USER, [this.X(), this.Y()]); 197 }, 198 199 /** 200 * Updates the size of the image. 201 */ 202 updateSize: function () { 203 this.coords.setCoordinates(Const.COORDS_BY_USER, [this.W(), this.H()]); 204 }, 205 206 /** 207 * Update the anchor point of the image, i.e. the lower left corner 208 * and the two vectors which span the rectangle. 209 */ 210 updateSpan: function () { 211 var i, j, len = this.transformations.length, v = []; 212 213 if (len === 0) { 214 this.span = [[this.Z(), this.X(), this.Y()], 215 [this.Z(), this.W(), 0], 216 [this.Z(), 0, this.H()]]; 217 } else { 218 // v contains the three defining corners of the rectangle/image 219 v[0] = [this.Z(), this.X(), this.Y()]; 220 v[1] = [this.Z(), this.X() + this.W(), this.Y()]; 221 v[2] = [this.Z(), this.X(), this.Y() + this.H()]; 222 223 // Transform the three corners 224 for (i = 0; i < len; i++) { 225 for (j = 0; j < 3; j++) { 226 v[j] = Mat.matVecMult(this.transformations[i].matrix, v[j]); 227 } 228 } 229 // Normalize the vectors 230 for (j = 0; j < 3; j++) { 231 v[j][1] /= v[j][0]; 232 v[j][2] /= v[j][0]; 233 v[j][0] /= v[j][0]; 234 } 235 // Compute the two vectors spanning the rectangle 236 // by subtracting the anchor point. 237 for (j = 1; j < 3; j++) { 238 v[j][0] -= v[0][0]; 239 v[j][1] -= v[0][1]; 240 v[j][2] -= v[0][2]; 241 } 242 this.span = v; 243 } 244 245 return this; 246 }, 247 248 addTransform: function (transform) { 249 var i; 250 251 if (Type.isArray(transform)) { 252 for (i = 0; i < transform.length; i++) { 253 this.transformations.push(transform[i]); 254 } 255 } else { 256 this.transformations.push(transform); 257 } 258 }, 259 260 /** 261 * Sets x and y coordinate of the image. 262 * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 263 * @param {Array} coords coordinates in screen/user units of the mouse/touch position 264 * @param {Array} oldcoords coordinates in screen/user units of the previous mouse/touch position 265 * @returns {JXG.Image} this element 266 */ 267 setPositionDirectly: function (method, coords, oldcoords) { 268 var dc, 269 c = new Coords(method, coords, this.board), 270 oldc = new Coords(method, oldcoords, this.board), 271 v = [this.Z(), this.X(), this.Y()]; 272 273 dc = Statistics.subtract(c.usrCoords, oldc.usrCoords); 274 275 this.X = Type.createFunction(v[1] + dc[1], this.board, ''); 276 this.Y = Type.createFunction(v[2] + dc[2], this.board, ''); 277 278 return this; 279 } 280 281 }); 282 283 /** 284 * @class Displays an image. 285 * @pseudo 286 * @description Shows an image. The image can be supplied as an URL or an base64 encoded inline image 287 * like "data:image/png;base64, /9j/4AAQSkZJRgA..." or a function returning an URL: function(){ return 'xxx.png; }. 288 * @constructor 289 * @name Image 290 * @type JXG.Image 291 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 292 * @param {String_Array_Array} url,_topleft,_widthheight url defines the location of the image data. Optional topleft and 293 * widthheight define the user coordinates of the top left corner and the image's width and height. 294 * @example 295 * var im = board.create('image', ['http://geonext.uni-bayreuth.de/fileadmin/geonext/design/images/logo.gif', [-3,1],[5,5]]); 296 * 297 * </pre><div id="9850cda0-7ea0-4750-981c-68bacf9cca57" style="width: 400px; height: 400px;"></div> 298 * <script type="text/javascript"> 299 * var image_board = JXG.JSXGraph.initBoard('9850cda0-7ea0-4750-981c-68bacf9cca57', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false}); 300 * var image_im = image_board.create('image', ['http://jsxgraph.uni-bayreuth.de/distrib/images/uccellino.jpg', [-3,1],[5,5]]); 301 * </script><pre> 302 */ 303 JXG.createImage = function (board, parents, attributes) { 304 var attr, im; 305 306 attr = Type.copyAttributes(attributes, board.options, 'image'); 307 im = new JXG.Image(board, parents[0], parents[1], parents[2], attr); 308 309 if (Type.evaluate(attr.rotate) !== 0) { 310 im.addRotation(Type.evaluate(attr.rotate)); 311 } 312 313 return im; 314 }; 315 316 JXG.registerElement('image', JXG.createImage); 317 318 return { 319 Image: JXG.Image, 320 createImage: JXG.createImage 321 }; 322 });