1 /* 2 Copyright 2008-2026 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 Slope: function () { 61 return this.tangent.getSlope(); 62 }, 63 getAngle: function (unit) { 64 return this.tangent.getAngle(unit); 65 }, 66 DeltaX: function () { 67 return this.borderHorizontal.Direction()[0]; 68 }, 69 DeltaY: function () { 70 return this.borderVertical.Direction()[1]; 71 }, 72 Direction: function () { 73 return this.tangent.Direction(); 74 } 75 }; 76 77 /** 78 * @class Slope triangle to visualize the slope of a tangent to a curve, circle or line. 79 * @pseudo 80 * @name Slopetriangle 81 * @augments JXG.Line 82 * @constructor 83 * @type JXG.Polygon 84 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 85 * Parameter options: 86 * @param {JXG.Line} t A tangent based on a glider on some object, e.g. curve, circle, line or turtle. 87 * @param {JXG.Line_JXG.Point} li, p A line and a point on that line. 88 * The user has to take care that the point is a member of the line. 89 * @example 90 * // Create a slopetriangle on a tangent 91 * var f = board.create('plot', ['sin(x)']), 92 * g = board.create('glider', [1, 2, f]), 93 * t = board.create('tangent', [g]), 94 * 95 * st = board.create('slopetriangle', [t]); 96 * 97 * </pre><div class="jxgbox" id="JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b" style="width: 300px; height: 300px;"></div> 98 * <script type="text/javascript"> 99 * (function () { 100 * var board = JXG.JSXGraph.initBoard('JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}), 101 * f = board.create('plot', ['sin(x)']), 102 * g = board.create('glider', [1, 2, f]), 103 * t = board.create('tangent', [g]), 104 * 105 * st = board.create('slopetriangle', [t]); 106 * })(); 107 * </script><pre> 108 * 109 * @example 110 * // Create a on a line and a point on that line 111 * var p1 = board.create('point', [-2, 3]), 112 * p2 = board.create('point', [2, -3]), 113 * li = board.create('line', [p1, p2]), 114 * p = board.create('glider', [0, 0, li]), 115 * 116 * st = board.create('slopetriangle', [li, p]); 117 * 118 * </pre><div class="jxgbox" id="JXGb52f451c-22cf-4677-852a-0bb9d764ee95" style="width: 300px; height: 300px;"></div> 119 * <script type="text/javascript"> 120 * (function () { 121 * var board = JXG.JSXGraph.initBoard('JXGb52f451c-22cf-4677-852a-0bb9d764ee95', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}), 122 * p1 = board.create('point', [-2, 3]), 123 * p2 = board.create('point', [2, -3]), 124 * li = board.create('line', [p1, p2]), 125 * p = board.create('glider', [0, 0, li]), 126 * 127 * st = board.create('slopetriangle', [li, p]); 128 * })(); 129 * </script><pre> 130 */ 131 JXG.createSlopeTriangle = function (board, parents, attributes) { 132 var el, tangent, tglide, glider, 133 toppoint, baseline, basepoint, 134 label, attr, 135 isPrivateTangent = false; 136 137 if (parents.length === 1 && parents[0].type === Const.OBJECT_TYPE_TANGENT) { 138 tangent = parents[0]; 139 tglide = tangent.glider; 140 } else if (parents.length === 1 && parents[0].type === Const.OBJECT_TYPE_GLIDER) { 141 tglide = parents[0]; 142 attr = Type.copyAttributes(attributes, board.options, "slopetriangle", 'tangent'); 143 tangent = board.create("tangent", [tglide], attr); 144 isPrivateTangent = true; 145 } else if ( 146 parents.length === 2 && 147 parents[0].elementClass === Const.OBJECT_CLASS_LINE && 148 Type.isPoint(parents[1]) 149 ) { 150 tangent = parents[0]; 151 tglide = parents[1]; 152 } else { 153 throw new Error( 154 "JSXGraph: Can't create slope triangle with parent types '" + 155 typeof parents[0] + 156 "'." 157 ); 158 } 159 160 attr = Type.copyAttributes(attributes, board.options, "slopetriangle", 'basepoint'); 161 basepoint = board.create( 162 "point", 163 [ 164 function () { 165 return [tglide.X() + 1, tglide.Y()]; 166 } 167 ], 168 attr 169 ); 170 171 attr = Type.copyAttributes(attributes, board.options, "slopetriangle", 'baseline'); 172 baseline = board.create("line", [tglide, basepoint], attr); 173 174 attr = Type.copyAttributes(attributes, board.options, "slopetriangle", 'glider'); 175 glider = board.create("glider", [tglide.X() + 1, tglide.Y(), baseline], attr); 176 177 attr = Type.copyAttributes(attributes, board.options, "slopetriangle", 'toppoint'); 178 toppoint = board.create( 179 "point", 180 [ 181 function () { 182 return [ 183 glider.X(), 184 glider.Y() + (glider.X() - tglide.X()) * tangent.getSlope() 185 ]; 186 } 187 ], 188 attr 189 ); 190 191 attr = Type.copyAttributes(attributes, board.options, 'slopetriangle'); 192 // attr.borders = Type.copyAttributes(attr.borders, board.options, "slopetriangle", 'borders'); 193 el = board.create("polygon", [tglide, glider, toppoint], attr); 194 el.elType = 'slopetriangle'; 195 196 /** 197 * Returns the slope of the tangent. 198 * @name Slope 199 * @memberOf Slopetriangle.prototype 200 * @function 201 * @returns {Number} slope of the tangent. 202 */ 203 el.Slope = priv.Slope; 204 205 /** 206 * Returns the angle between the tangent and the x-axis. 207 * @name getAngle 208 * @memberOf Slopetriangle.prototype 209 * @function 210 * @param {String} [unit='radians'] Unit of the returned values. Possible units are 211 * <ul> 212 * <li> 'radians' (default): angle value in radians 213 * <li> 'degrees': angle value in degrees 214 * <li> 'semicircle': angle value in radians as a multiple of π, e.g. if the angle is 1.5π, 1.5 will be returned. 215 * <li> 'circle': angle value in radians as a multiple of 2π 216 * </ul> 217 * @returns {Number} 218 */ 219 el.getAngle = priv.getAngle; 220 221 /** 222 * Returns δx of the slope triangle, that is the slope of the tangent. 223 * This value is less than 0 if the line points to the left. 224 * @name DeltaX 225 * @memberOf Slopetriangle.prototype 226 * @function 227 * @returns {Number} 228 */ 229 el.DeltaX = priv.DeltaX; 230 231 /** 232 * Returns δy of the slope triangle, that is the slope of the tangent. 233 * This value is less than 0 if the line points to the bottom. 234 * @name DeltaY 235 * @memberOf Slopetriangle.prototype 236 * @function 237 * @returns {Number} 238 */ 239 el.DeltaY = priv.DeltaY; 240 241 /** 242 * Returns the direction of the slope triangle, that is the direction of the tangent. 243 * @name Direction 244 * @memberOf Slopetriangle.prototype 245 * @see Line#Direction 246 * @function 247 * @returns {Number} slope of the tangent. 248 */ 249 el.Direction = priv.Direction; 250 el.tangent = tangent; 251 el._isPrivateTangent = isPrivateTangent; 252 253 el.borderHorizontal = el.borders[0]; 254 el.borderVertical = el.borders[1]; 255 el.borderParallel = el.borders[2]; 256 257 //el.borderHorizontal.setArrow(false, {type: 2, size: 10}); 258 //el.borderVertical.setArrow(false, {type: 2, size: 10}); 259 el.borderParallel.setArrow(false, false); 260 261 attr = Type.copyAttributes(attributes, board.options, "slopetriangle", 'label'); 262 //label = board.create("text", [ 263 // function () { 264 // return glider.X() + 0.1; 265 // }, 266 // function () { 267 // return (glider.Y() + toppoint.Y()) * 0.5; 268 // }, 269 // function () { 270 // return ""; 271 // } 272 // ], 273 // attr 274 // ); 275 276 attr = Type.copyAttributes(attr, board.options, 'label'); 277 // Add label to vertical polygon edge 278 attr.isLabel = true; 279 attr.anchor = el.borderVertical; 280 attr.priv = el.borderVertical.visProp.priv; 281 attr.id = el.borderVertical.id + 'Label'; 282 283 label = board.create("text", [0, 0, function () { return ""; }], attr); 284 label.elType = 'label'; 285 label.needsUpdate = true; 286 label.dump = false; 287 el.borderVertical.label = label; 288 el.borderVertical.hasLabel = true; 289 el.borderVertical.visProp.withlabel = true; 290 291 el.borderVertical.slopetriangle = el; 292 el.borderHorizontal.slopetriangle = el; 293 glider.slopetriangle = el; 294 basepoint.slopetriangle = el; 295 baseline.slopetriangle = el; 296 toppoint.slopetriangle = el; 297 label.slopetriangle = el; 298 299 label.setText(function () { 300 var prefix = '', 301 suffix = '', 302 digits = label.evalVisProp('digits'), 303 val = el.Slope(); 304 305 if (label.evalVisProp('showprefix')) { 306 prefix = label.evalVisProp('prefix'); 307 } 308 if (label.evalVisProp('showsuffix')) { 309 suffix = label.evalVisProp('suffix'); 310 } 311 312 if (digits === 'none') { 313 // do nothing 314 } else if (digits === 'auto') { 315 if (label.useLocale()) { 316 val = label.formatNumberLocale(val); 317 } else { 318 val = Type.autoDigits(val); 319 } 320 } else { 321 if (label.useLocale()) { 322 val = label.formatNumberLocale(val, digits); 323 } else { 324 val = Type.toFixed(val, digits); 325 } 326 } 327 328 if (Type.isFunction(el.visProp.formatvalue)) { 329 val = el.visProp.formatvalue(el, val); 330 } 331 332 return prefix + val + suffix; 333 }); 334 label.fullUpdate(); 335 336 el.glider = glider; 337 el.basepoint = basepoint; 338 el.baseline = baseline; 339 el.toppoint = toppoint; 340 el.label = label; 341 342 el.subs = { 343 glider: glider, 344 basePoint: basepoint, 345 baseLine: baseline, 346 topPoint: toppoint, 347 label: label 348 }; 349 el.inherits.push(glider, basepoint, baseline, toppoint, label); 350 351 el.methodMap = JXG.deepCopy(el.methodMap, { 352 tangent: "tangent", 353 glider: "glider", 354 basepoint: "basepoint", 355 baseline: "baseline", 356 toppoint: "toppoint", 357 borderHorizontal: "borderHorizontal", 358 borderVertical: "borderVertical", 359 borderParallel: "borderParallel", 360 label: "label", 361 Value: "Slope", 362 V: "Slope", 363 Slope: "Slope", 364 Angle: "getAngle", 365 getAngle: "getAngle", 366 DeltaX: "DeltaX", 367 DeltaY: "DeltaY", 368 Direction: "Direction" 369 }); 370 371 el.remove = priv.removeSlopeTriangle; 372 373 return el; 374 }; 375 376 JXG.registerElement("slopetriangle", JXG.createSlopeTriangle); 377 378 // export default { 379 // createSlopeTriangle: JXG.createSlopeTriangle 380 // }; 381