1 /* 2 Copyright 2008-2012 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 /** 28 * @fileoverview In this file the geometry element Image is defined. 29 */ 30 31 /** 32 * Construct and handle images 33 * @class Image: 34 * It inherits from @see GeometryElement. 35 * @constructor 36 */ 37 JXG.Image = function (board, url, coords, size, attributes) { 38 this.constructor(board, attributes, JXG.OBJECT_TYPE_IMAGE, JXG.OBJECT_CLASS_OTHER); 39 40 this.initialCoords = new JXG.Coords(JXG.COORDS_BY_USER, coords, this.board); // Still needed? 41 42 if (!JXG.isFunction(coords[0]) && !JXG.isFunction(coords[1])) { 43 this.isDraggable = true; 44 } 45 this.X = JXG.createFunction(coords[0],this.board,''); 46 this.Y = JXG.createFunction(coords[1],this.board,''); 47 this.Z = JXG.createFunction(1.0,this.board,''); 48 this.W = JXG.createFunction(size[0],this.board,''); 49 this.H = JXG.createFunction(size[1],this.board,''); 50 this.coords = new JXG.Coords(JXG.COORDS_BY_USER, [this.X(),this.Y()], this.board); 51 this.usrSize = [this.W(), this.H()]; 52 this.size = [Math.abs(this.usrSize[0]*board.unitX),Math.abs(this.usrSize[1]*board.unitY)]; 53 this.url = url; 54 55 this.elType = 'image'; 56 57 // span contains the anchor point and the two vectors 58 // spanning the image rectangle. 59 this.span = [[this.Z(), this.X(), this.Y()], 60 [this.Z(), this.W(), 0], 61 [this.Z(), 0, this.H()]]; 62 63 this.parent = JXG.getRef(attributes.anchor); 64 65 this.id = this.board.setId(this, 'Im'); 66 67 this.board.renderer.drawImage(this); 68 if(!this.visProp.visible) { 69 this.board.renderer.hide(this); 70 } 71 72 }; 73 74 JXG.Image.prototype = new JXG.GeometryElement; 75 76 JXG.extend(JXG.Image.prototype, /** @lends JXG.Image.prototype */ { 77 78 /** 79 * Checks whether (x,y) is over or near the image; 80 * @param {int} x Coordinate in x direction, screen coordinates. 81 * @param {int} y Coordinate in y direction, screen coordinates. 82 * @return {Boolean} True if (x,y) is over the image, False otherwise. 83 */ 84 hasPoint: function (x,y) { 85 var len = this.transformations.length, 86 dx, dy, r, // No transformations 87 c, v, p, dot; // Transformed image 88 89 // Easy case: no transformation 90 if (len==0) { 91 dx = x-this.coords.scrCoords[1]; 92 dy = this.coords.scrCoords[2]-y; 93 r = this.board.options.precision.hasPoint; 94 if (dx>=-r && dx-this.size[0]<=r && 95 dy>=-r && dy-this.size[1]<=r) { 96 97 return true; 98 } else { 99 return false; 100 } 101 } else { 102 // Image is transformed 103 c = new JXG.Coords(JXG.COORDS_BY_SCREEN, [x,y], this.board); 104 // v is the vector from anchor poit to the drag point 105 c = c.usrCoords; 106 v = [c[0]-this.span[0][0], 107 c[1]-this.span[0][1], 108 c[2]-this.span[0][2]]; 109 dot = JXG.Math.innerProduct; // shortcut 110 111 // Project the drag point to the sides. 112 p = dot(v, this.span[1]); 113 if ( 0.0<=p && p <= dot(this.span[1], this.span[1])) { 114 p = dot(v, this.span[2]); 115 if ( 0.0<=p && p <= dot(this.span[2], this.span[2])) { 116 return true; 117 } 118 } 119 return false; 120 } 121 }, 122 123 /** 124 * Recalculate the coordinates of lower left corner and the width amd the height. 125 * @private 126 */ 127 update: function () { 128 if (this.needsUpdate) { 129 this.updateCoords(); 130 this.usrSize = [this.W(), this.H()]; 131 this.size = [Math.abs(this.usrSize[0]*this.board.unitX),Math.abs(this.usrSize[1]*this.board.unitY)]; 132 this.updateTransform(); 133 this.updateSpan(); 134 } 135 return this; 136 }, 137 138 /** 139 * Send an update request to the renderer. 140 */ 141 updateRenderer: function () { 142 if (this.needsUpdate) { 143 this.board.renderer.updateImage(this); 144 this.needsUpdate = false; 145 } 146 return this; 147 }, 148 149 updateTransform: function () { 150 var i, len = this.transformations.length; 151 if (len>0) { 152 for (i=0; i<len; i++) { 153 this.transformations[i].update(); 154 } 155 } 156 return this; 157 }, 158 159 /** 160 * Updates the coordinates of the top left corner of the image. 161 */ 162 updateCoords: function () { 163 this.coords.setCoordinates(JXG.COORDS_BY_USER, [this.X(), this.Y()]); 164 }, 165 166 /** 167 * Updates the size of the image. 168 */ 169 updateSize: function () { 170 this.coords.setCoordinates(JXG.COORDS_BY_USER, [this.W(), this.H()]); 171 }, 172 173 /** 174 * Update the anchor point of the image, i.e. the lower left corner 175 * and the two vectors which span the rectangle. 176 */ 177 updateSpan: function () { 178 var i, j, len = this.transformations.length, v = []; 179 180 if (len==0) { 181 this.span = [[this.Z(), this.X(), this.Y()], 182 [this.Z(), this.W(), 0], 183 [this.Z(), 0, this.H()]]; 184 } else { 185 // v contains the three defining corners of the rectangle/image 186 v[0] = [this.Z(), this.X(), this.Y()]; 187 v[1] = [this.Z(), this.X()+this.W(), this.Y()]; 188 v[2] = [this.Z(), this.X(), this.Y()+this.H()]; 189 // Transform the three corners 190 for (i=0; i<len; i++) { 191 for (j=0; j<3; j++) { 192 v[j] = JXG.Math.matVecMult(this.transformations[i].matrix, v[j]); 193 } 194 } 195 // Normalize the vectors 196 for (j=0; j<3; j++) { 197 v[j][1] /= v[j][0]; 198 v[j][2] /= v[j][0]; 199 v[j][0] /= v[j][0]; 200 } 201 // Compute the two vectors spanning the rectangle 202 // by subtracting the anchor point. 203 for (j=1; j<3; j++) { 204 v[j][0] -= v[0][0]; 205 v[j][1] -= v[0][1]; 206 v[j][2] -= v[0][2]; 207 } 208 this.span = v; 209 } 210 return this; 211 }, 212 213 addTransform: function (transform) { 214 if (JXG.isArray(transform)) { 215 for (var i=0;i<transform.length;i++) { 216 this.transformations.push(transform[i]); 217 } 218 } else { 219 this.transformations.push(transform); 220 } 221 }, 222 223 /** 224 * Sets x and y coordinate of the image. 225 * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 226 * @param {Array} coords coordinates in screen/user units of the mouse/touch position 227 * @param {Array} oldcoords coordinates in screen/user units of the previous mouse/touch position 228 * @returns {JXG.Image} 229 */ 230 setPositionDirectly: function (method, coords, oldcoords) { 231 var c = new JXG.Coords(method, coords, this.board), 232 oldc = new JXG.Coords(method, oldcoords, this.board), 233 dc, v = [this.Z(), this.X(), this.Y()]; 234 235 dc = JXG.Math.Statistics.subtract(c.usrCoords, oldc.usrCoords); 236 237 this.X = JXG.createFunction(v[1]+dc[1], this.board, ''); 238 this.Y = JXG.createFunction(v[2]+dc[2], this.board, ''); 239 240 return this; 241 } 242 243 }); 244 245 /** 246 * @class Displays an image. 247 * @pseudo 248 * @description Shows an image. The image can be supplied as an URL or an base64 encoded inline image 249 * like "data:image/png;base64, /9j/4AAQSkZJRgA..." or a function returning an URL: function(){ return 'xxx.png; }. 250 * @constructor 251 * @name Image 252 * @type JXG.Image 253 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 254 * @param {String_Array_Array} url, [position of the top left vertice], [width,height] 255 * @example 256 * var im = board.create('image', ['http://geonext.uni-bayreuth.de/fileadmin/geonext/design/images/logo.gif', [-3,1],[5,5]]); 257 * 258 * </pre><div id="9850cda0-7ea0-4750-981c-68bacf9cca57" style="width: 400px; height: 400px;"></div> 259 * <script type="text/javascript"> 260 * var image_board = JXG.JSXGraph.initBoard('9850cda0-7ea0-4750-981c-68bacf9cca57', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false}); 261 * var image_im = image_board.create('image', ['http://jsxgraph.uni-bayreuth.de/distrib/images/uccellino.jpg', [-3,1],[5,5]]); 262 * </script><pre> 263 */ 264 JXG.createImage = function(board, parents, attributes) { 265 var url, attr, im; 266 attr = JXG.copyAttributes(attributes, board.options, 'image'); 267 im = new JXG.Image(board, parents[0], parents[1], parents[2], attr); 268 269 if (JXG.evaluate(attr.rotate) != 0) { 270 im.addRotation(JXG.evaluate(attr.rotate)); 271 } 272 273 return im; 274 }; 275 276 JXG.JSXGraph.registerElement('image', JXG.createImage); 277