1 /*
  2     Copyright 2008-2024
  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 <https://www.gnu.org/licenses/>
 29     and <https://opensource.org/licenses/MIT/>.
 30  */
 31 
 32 /*global JXG: true, define: true*/
 33 /*jslint nomen: true, plusplus: true*/
 34 
 35 /**
 36  * @fileoverview Example file for a triangle implemented as a extension to JSXGraph.
 37  */
 38 
 39 import JXG from "../jxg.js";
 40 import Type from "../utils/type.js";
 41 import Const from "../base/constants.js";
 42 import Polygon from "../base/polygon.js";
 43 
 44 var priv = {
 45     removeSlopeTriangle: function () {
 46         Polygon.prototype.remove.call(this);
 47 
 48         this.board.removeObject(this.toppoint);
 49         this.board.removeObject(this.glider);
 50 
 51         this.board.removeObject(this.baseline);
 52         this.board.removeObject(this.basepoint);
 53 
 54         this.board.removeObject(this.label);
 55 
 56         if (this._isPrivateTangent) {
 57             this.board.removeObject(this.tangent);
 58         }
 59     },
 60     Value: function () {
 61         return this.tangent.getSlope();
 62     },
 63     Direction: function() {
 64         return this.tangent.Direction();
 65     }
 66 };
 67 
 68 /**
 69  * @class Slope triangle for a point on a line.
 70  * @pseudo
 71  * @name Slopetriangle
 72  * @augments JXG.Line
 73  * @constructor
 74  * @type JXG.Polygon
 75  * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown.
 76  * Parameter options:
 77  * @param {JXG.Line} t A tangent based on a glider on some object, e.g. curve, circle, line or turtle.
 78  * @param {JXG.Line_JXG.Point} li, p A line and a point on that line.
 79  *  The user has to take care that the point is a member of the line.
 80  * @example
 81  * // Create a slopetriangle on a tangent
 82  * var f = board.create('plot', ['sin(x)']),
 83  *     g = board.create('glider', [1, 2, f]),
 84  *     t = board.create('tangent', [g]),
 85  *
 86  *     st = board.create('slopetriangle', [t]);
 87  *
 88  * </pre><div class="jxgbox" id="JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b" style="width: 300px; height: 300px;"></div>
 89  * <script type="text/javascript">
 90  * (function () {
 91  *   var board = JXG.JSXGraph.initBoard('JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}),
 92  *     f = board.create('plot', ['sin(x)']),
 93  *     g = board.create('glider', [1, 2, f]),
 94  *     t = board.create('tangent', [g]),
 95  *
 96  *     st = board.create('slopetriangle', [t]);
 97  * })();
 98  * </script><pre>
 99  *
100  * @example
101  * // Create a on a line and a point on that line
102  * var p1 = board.create('point', [-2, 3]),
103  *     p2 = board.create('point', [2, -3]),
104  *     li = board.create('line', [p1, p2]),
105  *     p = board.create('glider', [0, 0, li]),
106  *
107  *     st = board.create('slopetriangle', [li, p]);
108  *
109  * </pre><div class="jxgbox" id="JXGb52f451c-22cf-4677-852a-0bb9d764ee95" style="width: 300px; height: 300px;"></div>
110  * <script type="text/javascript">
111  * (function () {
112  *   var board = JXG.JSXGraph.initBoard('JXGb52f451c-22cf-4677-852a-0bb9d764ee95', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}),
113  *     p1 = board.create('point', [-2, 3]),
114  *     p2 = board.create('point', [2, -3]),
115  *     li = board.create('line', [p1, p2]),
116  *     p = board.create('glider', [0, 0, li]),
117  *
118  *     st = board.create('slopetriangle', [li, p]);
119  * })();
120  * </script><pre>
121  */
122 JXG.createSlopeTriangle = function (board, parents, attributes) {
123     var el, tangent, tglide, glider,
124         toppoint, baseline, basepoint,
125         label, attr,
126         isPrivateTangent = false;
127 
128     if (parents.length === 1 && parents[0].type === Const.OBJECT_TYPE_TANGENT) {
129         tangent = parents[0];
130         tglide = tangent.glider;
131     } else if (parents.length === 1 && parents[0].type === Const.OBJECT_TYPE_GLIDER) {
132         tglide = parents[0];
133         attr = Type.copyAttributes(attributes, board.options, "slopetriangle", "tangent");
134         tangent = board.create("tangent", [tglide], attr);
135         isPrivateTangent = true;
136     } else if (
137         parents.length === 2 &&
138         parents[0].elementClass === Const.OBJECT_CLASS_LINE &&
139         Type.isPoint(parents[1])
140     ) {
141         tangent = parents[0];
142         tglide = parents[1];
143     } else {
144         throw new Error(
145             "JSXGraph: Can't create slope triangle with parent types '" +
146                 typeof parents[0] +
147                 "'."
148         );
149     }
150 
151     attr = Type.copyAttributes(attributes, board.options, "slopetriangle", "basepoint");
152     basepoint = board.create(
153         "point",
154         [
155             function () {
156                 return [tglide.X() + 1, tglide.Y()];
157             }
158         ],
159         attr
160     );
161 
162     attr = Type.copyAttributes(attributes, board.options, "slopetriangle", "baseline");
163     baseline = board.create("line", [tglide, basepoint], attr);
164 
165     attr = Type.copyAttributes(attributes, board.options, "slopetriangle", "glider");
166     glider = board.create("glider", [tglide.X() + 1, tglide.Y(), baseline], attr);
167 
168     attr = Type.copyAttributes(attributes, board.options, "slopetriangle", "toppoint");
169     toppoint = board.create(
170         "point",
171         [
172             function () {
173                 return [
174                     glider.X(),
175                     glider.Y() + (glider.X() - tglide.X()) * tangent.getSlope()
176                 ];
177             }
178         ],
179         attr
180     );
181 
182     attr = Type.copyAttributes(attributes, board.options, "slopetriangle");
183     // attr.borders = Type.copyAttributes(attr.borders, board.options, "slopetriangle", "borders");
184     el = board.create("polygon", [tglide, glider, toppoint], attr);
185 
186     /**
187      * Returns the value of the slope triangle, that is the slope of the tangent.
188      * @name Value
189      * @memberOf Slopetriangle.prototype
190      * @function
191      * @returns {Number} slope of the tangent.
192      */
193     el.Value = priv.Value;
194 
195     /**
196      * Returns the direction of the slope triangle, that is the direction of the tangent.
197      * @name Direction
198      * @memberOf Slopetriangle.prototype
199      * @see Line#Direction
200      * @function
201      * @returns {Number} slope of the tangent.
202      */
203     el.Direction = priv.Direction;
204     el.tangent = tangent;
205     el._isPrivateTangent = isPrivateTangent;
206 
207     //el.borders[0].setArrow(false, {type: 2, size: 10});
208     //el.borders[1].setArrow(false, {type: 2, size: 10});
209     el.borders[2].setArrow(false, false);
210 
211     attr = Type.copyAttributes(attributes, board.options, "slopetriangle", "label");
212     //label = board.create("text", [
213     //         function () {
214     //             return glider.X() + 0.1;
215     //         },
216     //         function () {
217     //             return (glider.Y() + toppoint.Y()) * 0.5;
218     //         },
219     //         function () {
220     //             return "";
221     //         }
222     //     ],
223     //     attr
224     // );
225 
226     attr = Type.copyAttributes(attr, board.options, "label");
227     // Add label to vertical polygon edge
228     attr.isLabel = true;
229     attr.anchor = el.borders[1];
230     attr.priv = el.borders[1].visProp.priv;
231     attr.id = el.borders[1].id + "Label";
232 
233     label = board.create("text", [0, 0, function () { return ""; }], attr);
234     label.needsUpdate = true;
235     label.dump = false;
236     el.borders[1].label = label;
237     el.borders[1].hasLabel = true;
238     el.borders[1].visProp.withlabel = true;
239 
240     label._setText(function () {
241         var digits = Type.evaluate(label.visProp.digits);
242 
243         if (label.useLocale()) {
244             return label.formatNumberLocale(el.Value(), digits);
245         }
246         return Type.toFixed(el.Value(), digits);
247     });
248     label.fullUpdate();
249 
250     el.glider = glider;
251     el.basepoint = basepoint;
252     el.baseline = baseline;
253     el.toppoint = toppoint;
254     el.label = label;
255 
256     el.subs = {
257         glider: glider,
258         basePoint: basepoint,
259         baseLine: baseline,
260         topPoint: toppoint,
261         label: label
262     };
263     el.inherits.push(glider, basepoint, baseline, toppoint, label);
264 
265     el.methodMap = JXG.deepCopy(el.methodMap, {
266         tangent: "tangent",
267         glider: "glider",
268         basepoint: "basepoint",
269         baseline: "baseline",
270         toppoint: "toppoint",
271         label: "label",
272         Value: "Value",
273         V: "Value",
274         Direction: "Direction"
275     });
276 
277     el.remove = priv.removeSlopeTriangle;
278 
279     return el;
280 };
281 
282 JXG.registerElement("slopetriangle", JXG.createSlopeTriangle);
283 
284 // export default {
285 //     createSlopeTriangle: JXG.createSlopeTriangle
286 // };
287