[ Index ]

PHP Cross Reference of Moodle 310

title

Body

[close]

/question/type/ddmarker/amd/src/ -> shapes.js (source)

   1  // This file is part of Moodle - http://moodle.org/
   2  //
   3  // Moodle is free software: you can redistribute it and/or modify
   4  // it under the terms of the GNU General Public License as published by
   5  // the Free Software Foundation, either version 3 of the License, or
   6  // (at your option) any later version.
   7  //
   8  // Moodle is distributed in the hope that it will be useful,
   9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11  // GNU General Public License for more details.
  12  //
  13  // You should have received a copy of the GNU General Public License
  14  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  15  
  16  /* eslint max-depth: ["error", 8] */
  17  
  18  /**
  19   * Library of classes for handling simple shapes.
  20   *
  21   * These classes can represent shapes, let you alter them, can go to and from a string
  22   * representation, and can give you an SVG representation.
  23   *
  24   * @module qtype_ddmarker/shapes
  25   * @copyright  2018 The Open University
  26   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  27   */
  28  
  29  define(function() {
  30  
  31      "use strict";
  32  
  33      /**
  34       * A point, with x and y coordinates.
  35       *
  36       * @param {int} x centre X.
  37       * @param {int} y centre Y.
  38       * @constructor
  39       */
  40      function Point(x, y) {
  41          this.x = x;
  42          this.y = y;
  43      }
  44  
  45      /**
  46       * Standard toString method.
  47       * @returns {string} "x;y";
  48       */
  49      Point.prototype.toString = function() {
  50          return this.x + ',' + this.y;
  51      };
  52  
  53      /**
  54       * Move a point
  55       * @param {int} dx x offset
  56       * @param {int} dy y offset
  57       */
  58      Point.prototype.move = function(dx, dy) {
  59          this.x += dx;
  60          this.y += dy;
  61      };
  62  
  63      /**
  64       * Return a new point that is a certain position relative to this one.
  65       *
  66       * @param {(int|Point)} offsetX if a point, offset by this points coordinates, else and int x offset.
  67       * @param {int} [offsetY] used if offsetX is an int, the corresponding y offset.
  68       * @return {Point} the new point.
  69       */
  70      Point.prototype.offset = function(offsetX, offsetY) {
  71          if (offsetX instanceof Point) {
  72              offsetY = offsetX.y;
  73              offsetX = offsetX.x;
  74          }
  75          return new Point(this.x + offsetX, this.y + offsetY);
  76      };
  77  
  78      /**
  79       * Make a point from the string representation.
  80       *
  81       * @param {String} coordinates "x,y".
  82       * @return {Point} the point. Throws an exception if input is not valid.
  83       */
  84      Point.parse = function(coordinates) {
  85          var bits = coordinates.split(',');
  86          if (bits.length !== 2) {
  87              throw new Error(coordinates + ' is not a valid point');
  88          }
  89          return new Point(Math.round(bits[0]), Math.round(bits[1]));
  90      };
  91  
  92  
  93      /**
  94       * Shape constructor. Abstract class to represent the different types of drop zone shapes.
  95       *
  96       * @param {String} [label] name of this area.
  97       * @param {int} [x] centre X.
  98       * @param {int} [y] centre Y.
  99       * @constructor
 100       */
 101      function Shape(label, x, y) {
 102          this.label = label;
 103          this.centre = new Point(x || 0, y || 0);
 104      }
 105  
 106      /**
 107       * Get the type of shape.
 108       *
 109       * @return {String} 'circle', 'rectangle' or 'polygon';
 110       */
 111      Shape.prototype.getType = function() {
 112          throw new Error('Not implemented.');
 113      };
 114  
 115      /**
 116       * Get the string representation of this shape.
 117       *
 118       * @return {String} coordinates as they need to be typed into the form.
 119       */
 120      Shape.prototype.getCoordinates = function() {
 121          throw new Error('Not implemented.');
 122      };
 123  
 124      /**
 125       * Update the shape from the string representation.
 126       *
 127       * @param {String} coordinates in the form returned by getCoordinates.
 128       * @param {number} ratio Ratio to scale.
 129       * @return {boolean} true if the string could be parsed and the shape updated, else false.
 130       */
 131      Shape.prototype.parse = function(coordinates, ratio) {
 132          void (coordinates, ratio);
 133          throw new Error('Not implemented.');
 134      };
 135  
 136      /**
 137       * Move the entire shape by this offset.
 138       *
 139       * @param {int} dx x offset.
 140       * @param {int} dy y offset.
 141       * @param {int} maxX ensure that after editing, the shape lies between 0 and maxX on the x-axis.
 142       * @param {int} maxY ensure that after editing, the shape lies between 0 and maxX on the y-axis.
 143       */
 144      Shape.prototype.move = function(dx, dy, maxX, maxY) {
 145          void (maxY);
 146      };
 147  
 148      /**
 149       * Move one of the edit handles by this offset.
 150       *
 151       * @param {int} handleIndex which handle was moved.
 152       * @param {int} dx x offset.
 153       * @param {int} dy y offset.
 154       * @param {int} maxX ensure that after editing, the shape lies between 0 and maxX on the x-axis.
 155       * @param {int} maxY ensure that after editing, the shape lies between 0 and maxX on the y-axis.
 156       */
 157      Shape.prototype.edit = function(handleIndex, dx, dy, maxX, maxY) {
 158          void (maxY);
 159      };
 160  
 161      /**
 162       * Update the properties of this shape after a sequence of edits.
 163       *
 164       * For example make sure the circle radius is positive, of the polygon centre is centred.
 165       */
 166      Shape.prototype.normalizeShape = function() {
 167          void (1); // To make CiBoT happy.
 168      };
 169  
 170      /**
 171       * Get the string representation of this shape.
 172       *
 173       * @param {SVGElement} svg the SVG graphic to add this shape to.
 174       * @return {SVGElement} SVG representation of this shape.
 175       */
 176      Shape.prototype.makeSvg = function(svg) {
 177          void (svg);
 178          throw new Error('Not implemented.');
 179      };
 180  
 181      /**
 182       * Update the SVG representation of this shape.
 183       *
 184       * @param {SVGElement} svgEl the SVG representation of this shape.
 185       */
 186      Shape.prototype.updateSvg = function(svgEl) {
 187          void (svgEl);
 188      };
 189  
 190      /**
 191       * Make a circle similar to this shape.
 192       *
 193       * @return {Circle} a circle that is about the same size and position as this shape.
 194       */
 195      Shape.prototype.makeSimilarCircle = function() {
 196          throw new Error('Not implemented.');
 197      };
 198  
 199      /**
 200       * Make a rectangle similar to this shape.
 201       *
 202       * @return {Rectangle} a rectangle that is about the same size and position as this shape.
 203       */
 204      Shape.prototype.makeSimilarRectangle = function() {
 205          throw new Error('Not implemented.');
 206      };
 207  
 208      /**
 209       * Make a polygon similar to this shape.
 210       *
 211       * @return {Polygon} a polygon that is about the same size and position as this shape.
 212       */
 213      Shape.prototype.makeSimilarPolygon = function() {
 214          throw new Error('Not implemented.');
 215      };
 216  
 217      /**
 218       * Get the handles that should be offered to edit this shape, or null if not appropriate.
 219       *
 220       * @return {Object[]} with properties moveHandle {Point} and editHandles {Point[]}
 221       */
 222      Shape.prototype.getHandlePositions = function() {
 223          return null;
 224      };
 225  
 226  
 227      /**
 228       * A shape that is a circle.
 229       *
 230       * @param {String} label name of this area.
 231       * @param {int} [x] centre X.
 232       * @param {int} [y] centre Y.
 233       * @param {int} [radius] radius.
 234       * @constructor
 235       */
 236      function Circle(label, x, y, radius) {
 237          x = x || 15;
 238          y = y || 15;
 239          Shape.call(this, label, x, y);
 240          this.radius = radius || 15;
 241      }
 242      Circle.prototype = new Shape();
 243  
 244      Circle.prototype.getType = function() {
 245          return 'circle';
 246      };
 247  
 248      Circle.prototype.getCoordinates = function() {
 249          return this.centre + ';' + Math.abs(this.radius);
 250      };
 251  
 252      Circle.prototype.makeSvg = function(svg) {
 253          var svgEl = createSvgShapeGroup(svg, 'circle');
 254          this.updateSvg(svgEl);
 255          return svgEl;
 256      };
 257  
 258      Circle.prototype.updateSvg = function(svgEl) {
 259          svgEl.childNodes[0].setAttribute('cx', this.centre.x);
 260          svgEl.childNodes[0].setAttribute('cy', this.centre.y);
 261          svgEl.childNodes[0].setAttribute('r', Math.abs(this.radius));
 262          svgEl.childNodes[1].setAttribute('x', this.centre.x);
 263          svgEl.childNodes[1].setAttribute('y', this.centre.y + 15);
 264          svgEl.childNodes[1].textContent = this.label;
 265      };
 266  
 267      Circle.prototype.parse = function(coordinates, ratio) {
 268          if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?;\d+(\.\d+)?$/)) {
 269              return false;
 270          }
 271  
 272          var bits = coordinates.split(';');
 273          this.centre = Point.parse(bits[0]);
 274          this.centre.x = this.centre.x * parseFloat(ratio);
 275          this.centre.y = this.centre.y * parseFloat(ratio);
 276          this.radius = Math.round(bits[1]) * parseFloat(ratio);
 277          return true;
 278      };
 279  
 280      Circle.prototype.move = function(dx, dy, maxX, maxY) {
 281          this.centre.move(dx, dy);
 282          if (this.centre.x < this.radius) {
 283              this.centre.x = this.radius;
 284          }
 285          if (this.centre.x > maxX - this.radius) {
 286              this.centre.x = maxX - this.radius;
 287          }
 288          if (this.centre.y < this.radius) {
 289              this.centre.y = this.radius;
 290          }
 291          if (this.centre.y > maxY - this.radius) {
 292              this.centre.y = maxY - this.radius;
 293          }
 294      };
 295  
 296      Circle.prototype.edit = function(handleIndex, dx, dy, maxX, maxY) {
 297          this.radius += dx;
 298          var limit = Math.min(this.centre.x, this.centre.y, maxX - this.centre.x, maxY - this.centre.y);
 299          if (this.radius > limit) {
 300              this.radius = limit;
 301          }
 302          if (this.radius < -limit) {
 303              this.radius = -limit;
 304          }
 305      };
 306  
 307      /**
 308       * Update the properties of this shape after a sequence of edits.
 309       *
 310       * For example make sure the circle radius is positive, of the polygon centre is centred.
 311       */
 312      Circle.prototype.normalizeShape = function() {
 313          this.radius = Math.abs(this.radius);
 314      };
 315  
 316      Circle.prototype.makeSimilarRectangle = function() {
 317          return new Rectangle(this.label,
 318                  this.centre.x - this.radius, this.centre.y - this.radius,
 319                  this.radius * 2, this.radius * 2);
 320      };
 321  
 322      Circle.prototype.makeSimilarPolygon = function() {
 323          // We make a similar square, so if you go to and from Rectangle afterwards, it is loss-less.
 324          return new Polygon(this.label, [
 325                  this.centre.offset(-this.radius, -this.radius), this.centre.offset(-this.radius, this.radius),
 326                  this.centre.offset(this.radius, this.radius), this.centre.offset(this.radius, -this.radius)]);
 327      };
 328  
 329      Circle.prototype.getHandlePositions = function() {
 330          return {
 331              moveHandle: this.centre,
 332              editHandles: [this.centre.offset(this.radius, 0)]
 333          };
 334      };
 335  
 336  
 337      /**
 338       * A shape that is a rectangle.
 339       *
 340       * @param {String} label name of this area.
 341       * @param {int} [x] top left X.
 342       * @param {int} [y] top left Y.
 343       * @param {int} [width] width.
 344       * @param {int} [height] height.
 345       * @constructor
 346       */
 347      function Rectangle(label, x, y, width, height) {
 348          Shape.call(this, label, x, y);
 349          this.width = width || 30;
 350          this.height = height || 30;
 351      }
 352      Rectangle.prototype = new Shape();
 353  
 354      Rectangle.prototype.getType = function() {
 355          return 'rectangle';
 356      };
 357  
 358      Rectangle.prototype.getCoordinates = function() {
 359          return this.centre + ';' + this.width + ',' + this.height;
 360      };
 361  
 362      Rectangle.prototype.makeSvg = function(svg) {
 363          var svgEl = createSvgShapeGroup(svg, 'rect');
 364          this.updateSvg(svgEl);
 365          return svgEl;
 366      };
 367  
 368      Rectangle.prototype.updateSvg = function(svgEl) {
 369          if (this.width >= 0) {
 370              svgEl.childNodes[0].setAttribute('x', this.centre.x);
 371              svgEl.childNodes[0].setAttribute('width', this.width);
 372          } else {
 373              svgEl.childNodes[0].setAttribute('x', this.centre.x + this.width);
 374              svgEl.childNodes[0].setAttribute('width', -this.width);
 375          }
 376          if (this.height >= 0) {
 377              svgEl.childNodes[0].setAttribute('y', this.centre.y);
 378              svgEl.childNodes[0].setAttribute('height', this.height);
 379          } else {
 380              svgEl.childNodes[0].setAttribute('y', this.centre.y + this.height);
 381              svgEl.childNodes[0].setAttribute('height', -this.height);
 382          }
 383  
 384          svgEl.childNodes[1].setAttribute('x', this.centre.x + this.width / 2);
 385          svgEl.childNodes[1].setAttribute('y', this.centre.y + this.height / 2 + 15);
 386          svgEl.childNodes[1].textContent = this.label;
 387      };
 388  
 389      Rectangle.prototype.parse = function(coordinates, ratio) {
 390          if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?;\d+(\.\d+)?,\d+(\.\d+)?$/)) {
 391              return false;
 392          }
 393  
 394          var bits = coordinates.split(';');
 395          this.centre = Point.parse(bits[0]);
 396          this.centre.x = this.centre.x * parseFloat(ratio);
 397          this.centre.y = this.centre.y * parseFloat(ratio);
 398          var size = Point.parse(bits[1]);
 399          this.width = size.x * parseFloat(ratio);
 400          this.height = size.y * parseFloat(ratio);
 401          return true;
 402      };
 403  
 404      Rectangle.prototype.move = function(dx, dy, maxX, maxY) {
 405          this.centre.move(dx, dy);
 406          if (this.centre.x < 0) {
 407              this.centre.x = 0;
 408          }
 409          if (this.centre.x > maxX - this.width) {
 410              this.centre.x = maxX - this.width;
 411          }
 412          if (this.centre.y < 0) {
 413              this.centre.y = 0;
 414          }
 415          if (this.centre.y > maxY - this.height) {
 416              this.centre.y = maxY - this.height;
 417          }
 418      };
 419  
 420      Rectangle.prototype.edit = function(handleIndex, dx, dy, maxX, maxY) {
 421          this.width += dx;
 422          this.height += dy;
 423          if (this.width < -this.centre.x) {
 424              this.width = -this.centre.x;
 425          }
 426          if (this.width > maxX - this.centre.x) {
 427              this.width = maxX - this.centre.x;
 428          }
 429          if (this.height < -this.centre.y) {
 430              this.height = -this.centre.y;
 431          }
 432          if (this.height > maxY - this.centre.y) {
 433              this.height = maxY - this.centre.y;
 434          }
 435      };
 436  
 437      /**
 438       * Update the properties of this shape after a sequence of edits.
 439       *
 440       * For example make sure the circle radius is positive, of the polygon centre is centred.
 441       */
 442      Rectangle.prototype.normalizeShape = function() {
 443          if (this.width < 0) {
 444              this.centre.x += this.width;
 445              this.width = -this.width;
 446          }
 447          if (this.height < 0) {
 448              this.centre.y += this.height;
 449              this.height = -this.height;
 450          }
 451      };
 452  
 453      Rectangle.prototype.makeSimilarCircle = function() {
 454          return new Circle(this.label,
 455                  Math.round(this.centre.x + this.width / 2),
 456                  Math.round(this.centre.y + this.height / 2),
 457                  Math.round((this.width + this.height) / 4));
 458      };
 459  
 460      Rectangle.prototype.makeSimilarPolygon = function() {
 461          return new Polygon(this.label, [
 462              this.centre, this.centre.offset(0, this.height),
 463              this.centre.offset(this.width, this.height), this.centre.offset(this.width, 0)]);
 464      };
 465  
 466      Rectangle.prototype.getHandlePositions = function() {
 467          return {
 468              moveHandle: this.centre.offset(this.width / 2, this.height / 2),
 469              editHandles: [this.centre.offset(this.width, this.height)]
 470          };
 471      };
 472  
 473  
 474      /**
 475       * A shape that is a polygon.
 476       *
 477       * @param {String} label name of this area.
 478       * @param {Point[]} [points] position of the vertices relative to (centreX, centreY).
 479       *      each object in the array should have two
 480       * @constructor
 481       */
 482      function Polygon(label, points) {
 483          Shape.call(this, label, 0, 0);
 484          this.points = points ? points.slice() : [new Point(10, 10), new Point(40, 10), new Point(10, 40)];
 485          this.normalizeShape();
 486          this.ratio = 1;
 487      }
 488      Polygon.prototype = new Shape();
 489  
 490      Polygon.prototype.getType = function() {
 491          return 'polygon';
 492      };
 493  
 494      Polygon.prototype.getCoordinates = function() {
 495          var coordinates = '';
 496          for (var i = 0; i < this.points.length; i++) {
 497              coordinates += this.centre.offset(this.points[i]) + ';';
 498          }
 499          return coordinates.slice(0, coordinates.length - 1); // Strip off the last ';'.
 500      };
 501  
 502      Polygon.prototype.makeSvg = function(svg) {
 503          var svgEl = createSvgShapeGroup(svg, 'polygon');
 504          this.updateSvg(svgEl);
 505          return svgEl;
 506      };
 507  
 508      Polygon.prototype.updateSvg = function(svgEl) {
 509          svgEl.childNodes[0].setAttribute('points', this.getCoordinates().replace(/[,;]/g, ' '));
 510          svgEl.childNodes[0].setAttribute('transform', 'scale(' + parseFloat(this.ratio) + ')');
 511          svgEl.childNodes[1].setAttribute('x', this.centre.x);
 512          svgEl.childNodes[1].setAttribute('y', this.centre.y + 15);
 513          svgEl.childNodes[1].textContent = this.label;
 514      };
 515  
 516      Polygon.prototype.parse = function(coordinates, ratio) {
 517          if (!coordinates.match(/^\d+(\.\d+)?,\d+(\.\d+)?(?:;\d+(\.\d+)?,\d+(\.\d+)?)*$/)) {
 518              return false;
 519          }
 520  
 521          var bits = coordinates.split(';');
 522          var points = [];
 523          for (var i = 0; i < bits.length; i++) {
 524              points.push(Point.parse(bits[i]));
 525          }
 526  
 527          this.points = points;
 528          this.centre.x = 0;
 529          this.centre.y = 0;
 530          this.ratio = ratio;
 531          this.normalizeShape();
 532  
 533          return true;
 534      };
 535  
 536      Polygon.prototype.move = function(dx, dy, maxX, maxY) {
 537          this.centre.move(dx, dy);
 538          var bbXMin = maxX,
 539              bbXMax = 0,
 540              bbYMin = maxY,
 541              bbYMax = 0;
 542          // Computer centre.
 543          for (var i = 0; i < this.points.length; i++) {
 544              bbXMin = Math.min(bbXMin, this.points[i].x);
 545              bbXMax = Math.max(bbXMax, this.points[i].x);
 546              bbYMin = Math.min(bbYMin, this.points[i].y);
 547              bbYMax = Math.max(bbYMax, this.points[i].y);
 548          }
 549          if (this.centre.x < -bbXMin) {
 550              this.centre.x = -bbXMin;
 551          }
 552          if (this.centre.x > maxX - bbXMax) {
 553              this.centre.x = maxX - bbXMax;
 554          }
 555          if (this.centre.y < -bbYMin) {
 556              this.centre.y = -bbYMin;
 557          }
 558          if (this.centre.y > maxY - bbYMax) {
 559              this.centre.y = maxY - bbYMax;
 560          }
 561      };
 562  
 563      Polygon.prototype.edit = function(handleIndex, dx, dy, maxX, maxY) {
 564          this.points[handleIndex].move(dx, dy);
 565          if (this.points[handleIndex].x < -this.centre.x) {
 566              this.points[handleIndex].x = -this.centre.x;
 567          }
 568          if (this.points[handleIndex].x > maxX - this.centre.x) {
 569              this.points[handleIndex].x = maxX - this.centre.x;
 570          }
 571          if (this.points[handleIndex].y < -this.centre.y) {
 572              this.points[handleIndex].y = -this.centre.y;
 573          }
 574          if (this.points[handleIndex].y > maxY - this.centre.y) {
 575              this.points[handleIndex].y = maxY - this.centre.y;
 576          }
 577      };
 578  
 579      /**
 580       * Add a new point after the given point, with the same co-ordinates.
 581       *
 582       * This does not automatically normalise.
 583       *
 584       * @param {int} pointIndex the index of the vertex after which to insert this new one.
 585       */
 586      Polygon.prototype.addNewPointAfter = function(pointIndex) {
 587          this.points.splice(pointIndex, 0,
 588                  new Point(this.points[pointIndex].x, this.points[pointIndex].y));
 589      };
 590  
 591      Polygon.prototype.normalizeShape = function() {
 592          var i,
 593              x = 0,
 594              y = 0;
 595  
 596          if (this.points.length === 0) {
 597              return;
 598          }
 599  
 600          // Computer centre.
 601          for (i = 0; i < this.points.length; i++) {
 602              x += this.points[i].x;
 603              y += this.points[i].y;
 604          }
 605          x = Math.round(x / this.points.length);
 606          y = Math.round(y / this.points.length);
 607  
 608          if (x === 0 && y === 0) {
 609              return;
 610          }
 611  
 612          for (i = 0; i < this.points.length; i++) {
 613              this.points[i].move(-x, -y);
 614          }
 615          this.centre.move(x, y);
 616      };
 617  
 618      Polygon.prototype.makeSimilarCircle = function() {
 619          return this.makeSimilarRectangle().makeSimilarCircle();
 620      };
 621  
 622      Polygon.prototype.makeSimilarRectangle = function() {
 623          var p,
 624              minX = 0,
 625              maxX = 0,
 626              minY = 0,
 627              maxY = 0;
 628          for (var i = 0; i < this.points.length; i++) {
 629              p = this.points[i];
 630              minX = Math.min(minX, p.x);
 631              maxX = Math.max(maxX, p.x);
 632              minY = Math.min(minY, p.y);
 633              maxY = Math.max(maxY, p.y);
 634          }
 635          return new Rectangle(this.label,
 636                  this.centre.x + minX, this.centre.y + minY,
 637                  Math.max(maxX - minX, 10), Math.max(maxY - minY, 10));
 638      };
 639  
 640      Polygon.prototype.getHandlePositions = function() {
 641          var editHandles = [];
 642          for (var i = 0; i < this.points.length; i++) {
 643              editHandles.push(this.points[i].offset(this.centre.x, this.centre.y));
 644          }
 645  
 646          this.centre.x = this.centre.x * parseFloat(this.ratio);
 647          this.centre.y = this.centre.y * parseFloat(this.ratio);
 648  
 649          return {
 650              moveHandle: this.centre,
 651              editHandles: editHandles
 652          };
 653      };
 654  
 655  
 656      /**
 657       * Not a shape (null object pattern).
 658       *
 659       * @param {String} label name of this area.
 660       * @constructor
 661       */
 662      function NullShape(label) {
 663          Shape.call(this, label);
 664      }
 665      NullShape.prototype = new Shape();
 666  
 667      NullShape.prototype.getType = function() {
 668          return 'null';
 669      };
 670  
 671      NullShape.prototype.getCoordinates = function() {
 672          return '';
 673      };
 674  
 675      NullShape.prototype.makeSvg = function(svg) {
 676          void (svg);
 677          return null;
 678      };
 679  
 680      NullShape.prototype.updateSvg = function(svgEl) {
 681          void (svgEl);
 682      };
 683  
 684      NullShape.prototype.parse = function(coordinates) {
 685          void (coordinates);
 686          return false;
 687      };
 688  
 689      NullShape.prototype.makeSimilarCircle = function() {
 690          return new Circle(this.label);
 691      };
 692  
 693      NullShape.prototype.makeSimilarRectangle = function() {
 694          return new Rectangle(this.label);
 695      };
 696  
 697      NullShape.prototype.makeSimilarPolygon = function() {
 698          return new Polygon(this.label);
 699      };
 700  
 701  
 702      /**
 703       * Make a new SVG DOM element as a child of svg.
 704       *
 705       * @param {SVGElement} svg the parent node.
 706       * @param {String} tagName the tag name.
 707       * @return {SVGElement} the newly created node.
 708       */
 709      function createSvgElement(svg, tagName) {
 710          var svgEl = svg.ownerDocument.createElementNS('http://www.w3.org/2000/svg', tagName);
 711          svg.appendChild(svgEl);
 712          return svgEl;
 713      }
 714  
 715      /**
 716       * Make a group SVG DOM elements containing a shape of the given type as first child,
 717       * and a text label as the second child.
 718       *
 719       * @param {SVGElement} svg the parent node.
 720       * @param {String} tagName the tag name.
 721       * @return {SVGElement} the newly created g element.
 722       */
 723      function createSvgShapeGroup(svg, tagName) {
 724          var svgEl = createSvgElement(svg, 'g');
 725          createSvgElement(svgEl, tagName).setAttribute('class', 'shape');
 726          createSvgElement(svgEl, 'text').setAttribute('class', 'shapeLabel');
 727          return svgEl;
 728      }
 729  
 730      /**
 731       * @alias module:qtype_ddmarker/shapes
 732       */
 733      return {
 734          /**
 735           * A point, with x and y coordinates.
 736           *
 737           * @param {int} x centre X.
 738           * @param {int} y centre Y.
 739           * @constructor
 740           */
 741          Point: Point,
 742  
 743          /**
 744           * A point, with x and y coordinates.
 745           *
 746           * @param {int} x centre X.
 747           * @param {int} y centre Y.
 748           * @constructor
 749           */
 750          Shape: Shape,
 751  
 752          /**
 753           * A shape that is a circle.
 754           *
 755           * @param {String} label name of this area.
 756           * @param {int} [x] centre X.
 757           * @param {int} [y] centre Y.
 758           * @param {int} [radius] radius.
 759           * @constructor
 760           */
 761          Circle: Circle,
 762  
 763          /**
 764           * A shape that is a rectangle.
 765           *
 766           * @param {String} label name of this area.
 767           * @param {int} [x] top left X.
 768           * @param {int} [y] top left Y.
 769           * @param {int} [width] width.
 770           * @param {int} [height] height.
 771           * @constructor
 772           */
 773          Rectangle: Rectangle,
 774  
 775          /**
 776           * A shape that is a polygon.
 777           *
 778           * @param {String} label name of this area.
 779           * @param {Point[]} [points] position of the vertices relative to (centreX, centreY).
 780           *      each object in the array should have two
 781           * @constructor
 782           */
 783          Polygon: Polygon,
 784  
 785          /**
 786           * Not a shape (null object pattern).
 787           *
 788           * @param {String} label name of this area.
 789           * @constructor
 790           */
 791          NullShape: NullShape,
 792  
 793          /**
 794           * Make a new SVG DOM element as a child of svg.
 795           *
 796           * @param {SVGElement} svg the parent node.
 797           * @param {String} tagName the tag name.
 798           * @return {SVGElement} the newly created node.
 799           */
 800          createSvgElement: createSvgElement,
 801  
 802          /**
 803           * Make a shape of the given type.
 804           *
 805           * @param {String} shapeType
 806           * @param {String} label
 807           * @return {Shape} the requested shape.
 808           */
 809          make: function(shapeType, label) {
 810              switch (shapeType) {
 811                  case 'circle':
 812                      return new Circle(label);
 813                  case 'rectangle':
 814                      return new Rectangle(label);
 815                  case 'polygon':
 816                      return new Polygon(label);
 817                  default:
 818                      return new NullShape(label);
 819              }
 820          },
 821  
 822          /**
 823           * Make a shape of the given type that is similar to the shape of the original type.
 824           *
 825           * @param {String} shapeType the new type of shape to make
 826           * @param {Shape} shape the shape to copy
 827           * @return {Shape} the similar shape of a different type.
 828           */
 829          getSimilar: function(shapeType, shape) {
 830              if (shapeType === shape.getType()) {
 831                  return shape;
 832              }
 833              switch (shapeType) {
 834                  case 'circle':
 835                      return shape.makeSimilarCircle();
 836                  case 'rectangle':
 837                      return shape.makeSimilarRectangle();
 838                  case 'polygon':
 839                      return shape.makeSimilarPolygon();
 840                  default:
 841                      return new NullShape(shape.label);
 842              }
 843          }
 844      };
 845  });


Generated: Wed Jan 22 11:59:49 2025 Cross-referenced by PHPXref 0.7.1