1 /*
  2     Copyright 2008-2022
  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  utils/type
 39  base/element
 40   elements:
 41    point
 42    segment
 43    ticks
 44  */
 45 
 46 /**
 47  * @fileoverview Geometry objects for measurements are defined in this file. This file stores all
 48  * style and functional properties that are required to use a tape measure on
 49  * a board.
 50  */
 51 
 52 define([
 53     'jxg', 'utils/type', 'base/element'
 54 ], function (JXG, Type, GeometryElement) {
 55 
 56     "use strict";
 57 
 58     /**
 59      * @class A tape measure can be used to measure distances between points.
 60      * @pseudo
 61      * @description
 62      * @name Tapemeasure
 63      * @augments Segment
 64      * @constructor
 65      * @type JXG.Segment
 66      * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
 67      * @param {Array_Array} start,end, The two arrays give the initial position where the tape measure
 68      * is drawn on the board.
 69      * @example
 70      * // Create a tape measure
 71      * var p1 = board.create('point', [0,0]);
 72      * var p2 = board.create('point', [1,1]);
 73      * var p3 = board.create('point', [3,1]);
 74      * var tape = board.create('tapemeasure', [[1, 2], [4, 2]], {name:'dist'});
 75      * </pre><div class="jxgbox" id="JXG6d9a2cda-22fe-4cd1-9d94-34283b1bdc01" style="width: 200px; height: 200px;"></div>
 76      * <script type="text/javascript">
 77      *   (function () {
 78      *     var board = JXG.JSXGraph.initBoard('JXG6d9a2cda-22fe-4cd1-9d94-34283b1bdc01', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false});
 79      *     var p1 = board.create('point', [0,0]);
 80      *     var p2 = board.create('point', [1,1]);
 81      *     var p3 = board.create('point', [3,1]);
 82      *     var tape = board.create('tapemeasure', [[1, 2], [4, 2]], {name:'dist'} );
 83      *   })();
 84      * </script><pre>
 85      */
 86     JXG.createTapemeasure = function (board, parents, attributes) {
 87         var pos0, pos1,
 88             attr, withTicks, withText, digits,
 89             li, p1, p2, n, ti;
 90 
 91         pos0 = parents[0];
 92         pos1 = parents[1];
 93 
 94         // start point
 95         attr = Type.copyAttributes(attributes, board.options, 'tapemeasure', 'point1');
 96         p1 = board.create('point', pos0,  attr);
 97 
 98         // end point
 99         attr = Type.copyAttributes(attributes, board.options, 'tapemeasure', 'point2');
100         p2 = board.create('point', pos1,  attr);
101 
102         p1.setAttribute({ignoredSnapToPoints: [p2]});
103         p2.setAttribute({ignoredSnapToPoints: [p1]});
104 
105         // tape measure line
106         attr = Type.copyAttributes(attributes, board.options, 'tapemeasure');
107         withTicks = attr.withticks;
108         withText = attr.withlabel;
109         digits = attr.digits;
110 
111         if (digits === 2 && attr.precision !== 2) {
112             // Backward compatibility
113             digits = attr.precision;
114         }
115 
116         // Below, we will replace the label by the measurement function.
117         if (withText) {
118             attr.withlabel = true;
119         }
120         li = board.create('segment', [p1, p2], attr);
121         // p1, p2 are already added to li.inherits
122 
123         if (withText) {
124             if (attributes.name && attributes.name !== '') {
125                 n = attributes.name + ' = ';
126             } else {
127                 n = '';
128             }
129             li.label.setText(function () {
130                 return n + Type.toFixed(p1.Dist(p2), digits);
131             });
132         }
133 
134         if (withTicks) {
135             attr = Type.copyAttributes(attributes, board.options, 'tapemeasure', 'ticks');
136             //ticks  = 2;
137             ti = board.create('ticks', [li, 0.1], attr);
138             li.inherits.push(ti);
139         }
140 
141         // override the segments's remove method to ensure the removal of all elements
142         /** @ignore */
143         li.remove = function () {
144             if (withTicks) {
145                 li.removeTicks(ti);
146             }
147 
148             board.removeObject(p2);
149             board.removeObject(p1);
150 
151             GeometryElement.prototype.remove.call(this);
152         };
153 
154         /**
155          * Returns the length of the tape measure.
156          * @name Value
157          * @memberOf Tapemeasure.prototype
158          * @function
159          * @returns {Number} length of tape measure.
160          */
161         li.Value = function () {
162             return p1.Dist(p2);
163         };
164 
165         p1.dump = false;
166         p2.dump = false;
167 
168         li.elType = 'tapemeasure';
169         li.getParents = function() {
170             return [[p1.X(), p1.Y()], [p2.X(), p2.Y()]];
171         };
172 
173         li.subs = {
174             point1: p1,
175             point2: p2
176         };
177 
178         if (withTicks) {
179             ti.dump = false;
180         }
181 
182         li.methodMap = JXG.deepCopy(li.methodMap, {
183             Value: 'Value'
184         });
185 
186         li.prepareUpdate().update();
187         if (!board.isSuspendedUpdate) {
188             li.updateVisibility().updateRenderer();
189             // The point updates are necessary in case of snapToGrid==true
190             li.point1.updateVisibility().updateRenderer();
191             li.point2.updateVisibility().updateRenderer();
192         }
193 
194         return li;
195     };
196 
197     JXG.registerElement('tapemeasure', JXG.createTapemeasure);
198 
199     return {
200         createTapemeasure: JXG.createTapemeasure
201     };
202 });
203