1 /* 2 Copyright 2011 3 Emmanuel Ostenne 4 Alfred Wassermann 5 6 This file is part of JSXGraph. 7 8 JSXGraph is free software: you can redistribute it and/or modify 9 it under the terms of the GNU Lesser General Public License as published by 10 the Free Software Foundation, either version 3 of the License, or 11 (at your option) any later version. 12 13 JSXGraph is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU Lesser General Public License for more details. 17 18 You should have received a copy of the GNU Lesser General Public License 19 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 JXG.TracenpocheReader = new function() { 22 23 this.tokenize = function(inputStr, prefix, suffix) { 24 if (typeof prefix !== 'string') { prefix = '<>+-&'; } 25 if (typeof suffix !== 'string') { suffix = '=>&:'; } 26 var c; // The current character. 27 var from; // The index of the start of the token. 28 var i = 0; // The index of the current character. 29 var length = inputStr.length; 30 var n; // The number value. 31 var q; // The quote character. 32 var str; // The string value. 33 var isSmallName; 34 35 var result = []; // An array to hold the results. 36 37 // Make a token object. 38 var make = function (type, value) { 39 return { 40 type: type, 41 value: value, 42 from: from, 43 to: i 44 }; 45 }; 46 47 var error = function(type, value, msg) { 48 console.log('Tokenizer: problem with ' + type + ' ' + value + ': ' + msg); 49 }; 50 51 if (!inputStr || inputStr=='') return; 52 53 // Loop through this text, one character at a time. 54 55 c = inputStr.charAt(i); 56 while (c) { 57 from = i; 58 if (c <= ' ') { // Ignore whitespace 59 i++; 60 c = inputStr.charAt(i); 61 } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { // name 62 str = c; 63 i += 1; 64 // if the name starts with a small capital, anything may follow 65 // otherwise, if the next char is a capital letter like 66 // AB, the meaning is Dist(A,B) 67 isSmallName = (c >= 'a' && c <= 'z') ? true : false; 68 69 for (;;) { 70 c = inputStr.charAt(i); 71 if (isSmallName) { 72 if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || 73 (c >= '0' && c <= '9') || (c === "'") /*|| c === '_' */) { 74 str += c; 75 i++; 76 } else { 77 break; 78 } 79 } else { 80 if ((c >= '0' && c <= '9') || (c === "'") ) { 81 str += c; 82 i++; 83 } else { 84 break; 85 } 86 } 87 } 88 if (result.length>0 && result[result.length-1].type=='name' 89 && result[result.length-1].value!='var' 90 && result[result.length-1].value!='for' 91 ) { 92 // Here we have the situation AB -> A#B 93 result.push(make('operator', '#')); 94 } 95 result.push(make('name', str)); 96 } else if (c >= '0' && c <= '9') { // number 97 // A number cannot start with a decimal point. It must start with a digit, 98 // possibly '0'. 99 str = c; 100 i++; 101 for (;;) { // Look for more digits 102 c = inputStr.charAt(i); 103 if (c < '0' || c > '9') { break; } 104 i++; 105 str += c; 106 } 107 if (c === '.') { // Look for a decimal fraction part 108 i++; 109 str += c; 110 for (;;) { 111 c = inputStr.charAt(i); 112 if (c < '0' || c > '9') { break; } 113 i++; 114 str += c; 115 } 116 } 117 if (c === 'e' || c === 'E') { // Look for an exponent part. 118 i++; 119 str += c; 120 c = inputStr.charAt(i); 121 if (c === '-' || c === '+') { 122 i++; 123 str += c; 124 c = inputStr.charAt(i); 125 } 126 if (c < '0' || c > '9') { 127 error('number', str, "Bad exponent"); 128 } 129 do { 130 i++; 131 str += c; 132 c = inputStr.charAt(i); 133 } while (c >= '0' && c <= '9'); 134 } 135 if (c >= 'a' && c <= 'z') { // Make sure the next character is not a letter 136 i++; 137 str += c; 138 error('number', str, "Bad number"); 139 } 140 n = +str; // Convert the string value to a number. If it is finite, then it is a good token. 141 if (isFinite(n)) { 142 result.push(make('number', n)); 143 } else { 144 error('number', str, "Bad number"); 145 } 146 } else if (c === '\'' || c === '"') { // string 147 str = ''; 148 q = c; 149 i++; 150 for (;;) { 151 c = inputStr.charAt(i); 152 if (c < ' ') { 153 error('string', str, c === '\n' || c === '\r' || c === '' ? 154 "Unterminated string." : 155 "Control character in string.", make('', str)); 156 break; 157 } 158 if (i >= length) { 159 error('string', str, "Unterminated string"); break; 160 } 161 if (c === q) { // Look for the closing quote. 162 break; 163 } 164 if (c === '\\') { // Look for escapement 165 i++; 166 if (i >= length) { 167 error('string', str, "Unterminated string"); break; 168 } 169 c = inputStr.charAt(i); 170 switch (c) { 171 case 'b': 172 c = '\b'; break; 173 case 'f': 174 c = '\f'; break; 175 case 'n': 176 c = '\n'; break; 177 case 'r': 178 c = '\r'; break; 179 case 't': 180 c = '\t'; break; 181 case 'u': 182 if (i >= length) { 183 error('string', str, "Unterminated string"); 184 } 185 c = parseInt(inputStr.substr(i + 1, 4), 16); 186 if (!isFinite(c) || c < 0) { 187 error('string', str, "Unterminated string"); 188 } 189 c = String.fromCharCode(c); 190 i += 4; 191 break; 192 } 193 } 194 str += c; 195 i++; 196 } 197 i++; 198 result.push(make('string', str)); 199 c = inputStr.charAt(i); 200 } else if (c === '/' && inputStr.charAt(i + 1) === '/') { // comment 201 i++; 202 for (;;) { 203 c = inputStr.charAt(i); 204 if (c === '\n' || c === '\r' || c === '') { break; } 205 i++; 206 } 207 } else if (prefix.indexOf(c) >= 0) { // combining (multi-character operator) 208 str = c; 209 i++; 210 while (true) { 211 c = inputStr.charAt(i); 212 if (i >= length || suffix.indexOf(c) < 0) { 213 break; 214 } 215 i++; 216 str += c; 217 } 218 result.push(make('operator', str)); 219 } else { // single-character operator 220 i++; 221 result.push(make('operator', c)); 222 c = inputStr.charAt(i); 223 } 224 } 225 226 return result; 227 }; 228 229 this.parseOptions = function(board) { 230 //var code, i, len = script.length; 231 232 // Analyze this.data for "@options;" 233 // Just for testing. 234 board.setBoundingBox([-10,10,10,-10], true); 235 board.create('axis', [[0, 0], [1, 0]]); 236 board.create('axis', [[0, 0], [0, 1]]); 237 238 /* 239 for (i=start+1; i<len; i++) { 240 code = script[i]; 241 if (code=='') continue; 242 243 if (code.match(/@/)) { // Reached the end of the options section 244 return i-1; 245 } 246 console.log("OPT>", code); 247 // Read options: 248 } 249 */ 250 }; 251 252 this.parse = function(tokens, scopeObjName) { 253 var scope; 254 var symbol_table = {}; 255 var token; 256 var token_nr; 257 var i, arr; 258 259 var error = function(tok, msg) { 260 throw new Error("TraceEnPocheReader: syntax error at char " + tok.from + ': ' + tok.value+ ' - ' + msg); 261 }; 262 263 var createObject = function (o) { 264 function F() {}; 265 F.prototype = o; 266 return new F(); 267 }; 268 269 var original_scope = { 270 define: function (n) { 271 //console.log("Add scope var " + n.value); 272 this.def[n.value] = n.value; 273 }, 274 find: function (n) { 275 var e = this, o; 276 while (true) { 277 o = e.def[n]; 278 if (o) { 279 return "$"+'["' + e.def[n].value + '"]'; 280 } 281 e = e.parent; 282 if (!e) { 283 o = symbol_table[n]; 284 return o; 285 } 286 } 287 }, 288 pop: function () { 289 scope = this.parent; 290 } 291 }; 292 293 var new_scope = function () { 294 var s = scope; 295 scope = Object.create(original_scope); 296 scope.def = {}; 297 scope.parent = s; 298 return scope; 299 }; 300 301 var advance = function (id) { 302 var a, o, t, v; 303 if (id && token.id !== id) { 304 error(token, "Expected '" + id + "'."); 305 } 306 if (token_nr >= tokens.length) { 307 token = symbol_table["(end)"]; 308 return; 309 } 310 t = tokens[token_nr]; 311 token_nr++; 312 v = t.value; 313 a = t.type; 314 if (a === "name") { 315 o = symbol_table[v]; 316 if (!o) { 317 o = variable(v); 318 } 319 } else if (a === "operator") { 320 o = symbol_table[v]; 321 if (!o) { 322 error(t, "Unknown operator."); 323 } 324 } else if (a === "string" || a === "number") { 325 o = symbol_table["(literal)"]; 326 // a = "literal"; 327 } else { 328 error(t, "Unexpected token."); 329 } 330 token = createObject(o); 331 token.from = t.from; 332 token.to = t.to; 333 token.value = v; 334 return token; 335 }; 336 337 var expression = function (rbp) { 338 var left, t = token; 339 advance(); 340 left = t.nud(); 341 while (rbp < token.lbp) { 342 t = token; 343 advance(); 344 left = t.led(left); 345 } 346 return left; 347 }; 348 349 var statement = function () { 350 var n = token, v; 351 352 if (n.std) { 353 advance(); 354 //scope.reserve(n); 355 356 return n.std(); 357 } 358 v = expression(0) + ';'; 359 /* 360 if (!v.assignment && v.id !== "(") { 361 error(v, "Bad expression statement."); 362 } 363 */ 364 advance(";"); 365 return v; 366 }; 367 368 var statements = function () { 369 var a = [], s; 370 while (true) { 371 if (token.id === "end" || token.id === "(end)") { 372 break; 373 } 374 s = statement(); 375 if (s) { 376 a.push(s); 377 } 378 } 379 return a.length === 0 ? null : a; 380 }; 381 382 var original_symbol = { 383 nud: function () { 384 error(this, "Undefined."); 385 }, 386 led: function (left) { 387 error(this, "Missing operator."); 388 } 389 }; 390 391 /* 392 * Shortcuts 393 */ 394 var symbol = function (id, bp) { 395 var s = symbol_table[id]; 396 bp = bp || 0; 397 if (s) { 398 if (bp >= s.lbp) { 399 s.lbp = bp; 400 } 401 } else { 402 s = createObject(original_symbol); 403 s.id = s.value = id; 404 s.lbp = bp; 405 symbol_table[id] = s; 406 } 407 return s; 408 }; 409 410 var constant = function (s, v) { 411 var x = symbol(s); 412 x.nud = function () { 413 this.value = symbol_table[this.id].value; 414 return this.value; 415 }; 416 x.value = v; 417 x.arity = 'name'; 418 return x; 419 }; 420 421 var predefined = function (s, v) { 422 var x = symbol(s); 423 x.nud = function () { 424 this.value = symbol_table[this.id].value; 425 return this.value; 426 }; 427 x.arity = "function"; 428 x.value = v; 429 return x; 430 }; 431 432 var variable = function (s) { 433 var x = symbol(s), second; 434 //console.log("Define " + s); 435 scope.define(x); 436 437 x.nud = function () { 438 this.value = symbol_table[this.id].value; 439 if (token.id === '[') { 440 //console.log("Proceed " + this.value); 441 second = expression(11); 442 return scopeObjName + '["' + this.value + '"+' + second + ']'; 443 } 444 return scopeObjName + '["' + this.value + '"]'; 445 }; 446 return x; 447 }; 448 449 var infix = function (id, bp, led) { 450 var s = symbol(id, bp); 451 s.led = led || function (left) { 452 this.first = left; 453 this.second = expression(bp); 454 return '('+this.first + this.value + this.second+')'; 455 }; 456 return s; 457 }; 458 459 var infixr = function (id, bp, led) { 460 var s = symbol(id, bp); 461 s.led = led || function (left) { 462 this.first = left; 463 this.second = expression(bp - 1); 464 return '('+this.first + this.value + this.second+')'; 465 }; 466 return s; 467 }; 468 469 var assignment = function (id) { 470 return infixr(id, 10, function (left) { 471 this.first = left; 472 if (token.id === '[') { 473 this.first += expression(0); 474 this.first = '$[' + this.first + ']'; 475 } 476 this.second = expression(9); 477 this.assignment = true; 478 return this.first + this.value + this.second; 479 }); 480 }; 481 482 var prefix = function (id, nud) { 483 var s = symbol(id); 484 s.nud = nud || function () { 485 this.first = expression(70); 486 return this.value + this.first; 487 }; 488 return s; 489 }; 490 491 var stmt = function (s, f) { 492 var x = symbol(s); 493 x.std = f; 494 return x; 495 }; 496 497 /* 498 * Define the language 499 * 500 */ 501 symbol("(literal)").nud = function() { return (typeof this.value === "string")? "'" + this.value + "'" :this.value; }; 502 symbol("(end)"); 503 symbol("(name)"); 504 symbol(":"); 505 symbol(";"); 506 symbol(")"); 507 symbol("]"); 508 symbol("}"); 509 symbol(","); 510 symbol("do"); 511 symbol("to"); 512 symbol("end"); 513 514 constant("true", true); 515 constant("false", false); 516 517 /* 518 * Predefined functions 519 */ 520 for (i=0; i<this.tepElements.length; i++) { 521 predefined(this.tepElements[i], "that." + this.tepElements[i]); 522 } 523 524 constant("x", "x"); 525 predefined("pi", "Math.PI"); 526 predefined("sin", "Math.sin"); 527 predefined("cos", "Math.cos"); 528 predefined("tan", "Math.tan"); 529 predefined("abs", "Math.abs"); 530 predefined("racine", "Math.sqrt"); 531 predefined("carre", "JXG.Math.carre"); 532 533 assignment("="); 534 535 infixr("&&", 30); 536 infixr("||", 30); 537 538 arr = ["==", "!=", "<", "<=", ">", ">="]; 539 for (i=0; i<arr.length; i++) { 540 infixr(arr[i], 40); 541 } 542 543 infix("#", 50, function (left) { 544 this.first = left; 545 this.second = expression(0); 546 return 'function(){return '+ this.first + '.Dist(' + this.second+');}'; 547 }); 548 549 infix("+", 50); 550 infix("-", 50); 551 infix("*", 60); 552 infix("/", 60); 553 infix("%", 50); 554 555 infixr("^", 65, function (left) { 556 this.first = left; 557 this.second = expression(64); 558 return 'Math.pow('+this.first + ',' + this.second+')'; 559 }); 560 561 infix("(", 80, function (left) { 562 var a = []; 563 this.first = left; 564 this.second = a; 565 566 // Parameters 567 if (token.id !== ")") { 568 while (true) { 569 a.push(expression(0)); 570 if (token.id !== ",") { 571 break; 572 } 573 advance(","); 574 } 575 } 576 advance(")"); 577 578 // Optional attributes 579 if (token.id === '{') { 580 this.third = expression(0).first; 581 } else { 582 this.third = []; 583 } 584 return this.first + '([' + this.second.join(',') + '],[' + this.third.join(',') + '])'; 585 }); 586 587 prefix("-"); 588 prefix("(", function () { 589 var e = expression(0); 590 advance(")"); 591 return e; 592 }); 593 594 prefix("fonction", function () { 595 advance("("); 596 var e = expression(0); 597 advance(")"); 598 e = e.replace(/,\[\]/g,"").replace(/[\[\]]/g,""); 599 return "that.fonction([" + "'" + e + "'" + "],{})"; 600 }); 601 602 // Attributes 603 prefix("{", function () { 604 var a = [], n, v; 605 if (token.id !== "}") { 606 while (true) { 607 // Ignore 608 n = token; 609 610 //if (n.arity !== "name"/* && n.arity !== "literal"*/) { 611 // error(token, "Bad property name."); 612 //} 613 advance(); 614 a.push( "'" + n.value + "'"); 615 if (token.id !== ",") { 616 break; 617 } 618 advance(","); 619 } 620 } 621 advance("}"); 622 this.first = a; 623 this.arity = "unary"; 624 return this; 625 }); 626 627 prefix("for", function () { 628 var n = token, vname; // FIXME error message 629 630 this.first = expression(0); 631 advance("to"); 632 this.second = expression(0); 633 advance("do"); 634 if (token.id === ';') advance(";"); 635 this.third = statements().join("\n"); 636 advance("end"); 637 varname = scopeObjName + '["' + n.value + '"]'; 638 return 'for (' + this.first + ';' + 639 varname + '<=' + this.second + ';' + 640 varname + '++){' + this.third + '}'; 641 }); 642 643 stmt("var", function () { 644 var a, n, t; 645 //n = token; 646 // scope.define(n); 647 a = statement(); 648 return /*"VAR " + */ a; 649 }); 650 651 prefix("[", function () { 652 var a = []; 653 if (token.id !== "]") { 654 while (true) { 655 a.push(expression(0)); 656 if (token.id !== ",") { 657 break; 658 } 659 advance(","); 660 } 661 } 662 advance("]"); 663 this.first = a; 664 this.arity = "unary"; 665 return a.length==0 ? null : a.length==1 ? a[0] : a[0]+'?'+a[1]+':'+a[2]; 666 }); 667 668 /* 669 * Here starts the parsing part 670 * 671 */ 672 token_nr = 0; 673 new_scope(); 674 advance(); 675 var s = statements().join('\n'); 676 //console.log(s); 677 return s; 678 }; 679 680 this.parseData = function(board) { 681 this.parseOptions(board); 682 this.parseFigure(board); 683 }; 684 685 this.parseFigure = function(board) { 686 var i = this.data.indexOf('@figure;'); 687 if (i<0) { 688 return; // no figure found 689 } 690 691 i += 8; // skip string "@figure;" 692 var i2 = this.data.indexOf('@',i+1); 693 if (i2<0) { i2 = this.data.length; } 694 695 var tokens = this.tokenize(this.data.slice(i, i2), '=<>!+-*&|/%^#', '=<>&|'); 696 this.board = board; 697 var s = this.parse(tokens, 'tep'); 698 var tep = {}; 699 700 701 // Set the default options 702 board.options.point.face = 'x'; 703 board.options.point.strokeColor = '#0000ff'; 704 board.options.point.strokeWidth = 1; 705 board.options.line.strokeWidth = 1; 706 707 //console.log(s); 708 var fun = new Function("that", "tep", s); 709 //console.log(fun.toString()); 710 fun(this, tep); 711 //console.log(tep); 712 713 // Set the correct labels and names 714 var el; 715 for (el in tep) { 716 if (JXG.exists(tep[el].setProperty)) { 717 tep[el].setProperty({name:el}); 718 if (JXG.exists(tep[el].label) && JXG.exists(tep[el].label.content)) { 719 tep[el].label.content.setText(el); 720 } 721 } 722 } 723 }; 724 725 // 726 //--------------------------------------------------------------------- 727 // 728 this.prepareString = function(fileStr) { 729 //fileStr = JXG.Util.utf8Decode(fileStr); 730 //fileStr = JXG.GeogebraReader.utf8replace(fileStr); 731 return fileStr; 732 }; 733 734 this.readTracenpoche = function(fileStr, board){ 735 this.data = this.prepareString(fileStr); 736 board.suspendUpdate(); 737 this.parseData(board); 738 board.unsuspendUpdate(); 739 return this.data; 740 }; 741 742 // 743 //--------------------------------------------------------------------- 744 // 745 this.handleAtts = function(attsArr) { 746 var obj = {}, i, le = attsArr.length; 747 748 obj["withLabel"] = true; 749 for (i=0; i<le; i++) { 750 switch (attsArr[i]) { 751 case 'sansnom': obj["withLabel"] = false; break; 752 } 753 } 754 return obj; 755 }; 756 757 758 JXG.Math.carre = function(x) { 759 return x*x; 760 }; 761 762 /* 763 * Now, the constructions of TeP elements follow 764 */ 765 this.tepElements = [ 766 // points 767 "point", "pointsur", "intersection", "projete", "barycentre", "image", "milieu", 768 // lines 769 "segment", "droite", "droiteEQR", "droiteEQ", "mediatrice", "parallele", "bissectrice", "perpendiculaire", "tangente", 770 "vecteur", 771 // circles 772 "cercle", "cerclerayon", 773 // polygons 774 "polygone", 775 // other 776 "texte", "reel", "entier", "fonction", 777 // transformations 778 "homothetie", "reflexion", "rotation", "symetrie", "translation" 779 ]; 780 781 /* 782 * Points 783 */ 784 this.point = function(parents, attributes) { 785 if (parents.length==0) { 786 return this.board.create('point', [Math.random(),Math.random()], this.handleAtts(attributes)); 787 } else { 788 return this.board.create('point', parents, this.handleAtts(attributes)); 789 } 790 }; 791 792 this.pointsur = function(parents, attributes) { 793 var p1, p2, c, lambda, par3; 794 795 par3 = parents[parents.length-1]; 796 if (JXG.isNumber(par3)) { 797 lambda = function(){ return par3; }; 798 } else { 799 lambda = function(){ return par3.Value(); }; 800 } 801 802 if (parents.length==3) { // point between two points 803 p1 = parents[0]; 804 p2 = parents[1]; 805 return this.board.create('point', [ 806 function(){ return p1.X()+(p2.X()-p1.X())*lambda(); }, 807 function(){ return p1.Y()+(p2.Y()-p1.Y())*lambda(); } 808 ], 809 this.handleAtts(attributes) 810 ); 811 } else if (parents.length==2) { // point on segment 812 if (parents[0].elementClass==JXG.OBJECT_CLASS_LINE) { 813 p1 = parents[0].point1; 814 p2 = parents[0].point2; 815 return this.board.create('point', [ 816 function(){ return p1.X()+(p2.X()-p1.X())*lambda(); }, 817 function(){ return p1.Y()+(p2.Y()-p1.Y())*lambda(); } 818 ], 819 this.handleAtts(attributes) 820 ); 821 } else { // point on circle 822 c = parents[0]; 823 return this.board.create('point', [ 824 function(){ return c.center.X()+c.Radius()*Math.cos(lambda()); }, 825 function(){ return c.center.Y()+c.Radius()*Math.sin(lambda()); } 826 ], 827 this.handleAtts(attributes) 828 ); 829 } 830 831 } 832 }; 833 834 this.intersection = function(parents, attributes) { 835 if (parents.length==2) { // line line 836 return this.board.create('intersection', [parents[0],parents[1],0], this.handleAtts(attributes)); 837 } else if (parents.length==3) { 838 if (JXG.isNumber(parents[2])) { // line circle 839 parents[2] -= 1; 840 return this.board.create('intersection', parents, this.handleAtts(attributes)); 841 } else { 842 return this.board.create('otherintersection', parents, this.handleAtts(attributes)); 843 } 844 } 845 } 846 847 this.projete = function(parents, attributes) { 848 var lpar; 849 if (parents.length == 2) { // orthogonal projection 850 return this.board.create('orthogonalprojection', parents, this.handleAtts(attributes)); 851 } else { // parallel projection along parents[2] 852 lpar = this.board.create('parallel', [parents[2], parents[0]], {visible:false, withLabel:false}); 853 return this.board.create('intersection', [parents[1], lpar, 0], this.handleAtts(attributes)); 854 } 855 } 856 857 this.barycentre = function(parents, attributes) { 858 return this.board.create('point', [ 859 function() { 860 var i, s = 0, le = parents.length, x = 0.0; 861 for (i=0; i<le; i+=2) { 862 x += parents[i].X()*parents[i+1]; 863 s += parents[i+1]; 864 } 865 return x/s; 866 }, 867 function() { 868 var i, s = 0, le = parents.length, y = 0.0; 869 for (i=0; i<le; i+=2) { 870 y += parents[i].Y()*parents[i+1]; 871 s += parents[i+1]; 872 } 873 return y/s; 874 } 875 ], this.handleAtts(attributes)); 876 } 877 878 this.image = function(parents, attributes) { 879 return this.board.create('point', [parents[1], parents[0]], this.handleAtts(attributes)); 880 } 881 882 this.milieu = function(parents, attributes) { 883 return this.board.create('midpoint', parents, this.handleAtts(attributes)); 884 } 885 886 /* 887 * Lines 888 */ 889 this.segment = function(parents, attributes) { 890 return this.board.create('segment', parents, this.handleAtts(attributes)); 891 }; 892 893 this.droite = function(parents, attributes) { 894 return this.board.create('line', parents, this.handleAtts(attributes)); 895 }; 896 897 this.droiteEQR = function(parents, attributes) { 898 return this.board.create('line', [parents[2], parents[0], parents[1]], this.handleAtts(attributes)); 899 }; 900 901 this.droiteEQ = function(parents, attributes) { 902 return this.board.create('line', [1.0, parents[0], parents[1]], this.handleAtts(attributes)); 903 }; 904 905 this.parallele = function(parents, attributes) { 906 return this.board.create('parallel', [parents[1], parents[0]], this.handleAtts(attributes)); 907 }; 908 909 this.mediatrice = function(parents, attributes) { 910 var m, li, el; 911 if (parents.length==1) { 912 m = this.board.create('midpoint', [parents[0]], {visible:false, withLabel:false}); 913 el = this.board.create('perpendicular', [parents[0], m], this.handleAtts(attributes)); 914 } else { 915 li = this.board.create('line', parents, {visible:false, withLabel:false}); 916 m = this.board.create('midpoint', parents, {visible:false, withLabel:false}); 917 el = this.board.create('perpendicular', [li, m], this.handleAtts(attributes)); 918 } 919 return el; 920 }; 921 922 this.perpendiculaire = function(parents, attributes) { 923 return this.board.create('perpendicular', [parents[1], parents[0]], this.handleAtts(attributes)); 924 }; 925 926 this.bissectrice = function(parents, attributes) { 927 return this.board.create('bisector', parents, this.handleAtts(attributes)); 928 }; 929 930 this.tangente = function(parents, attributes) { 931 var gli, 932 f = parents[0], 933 x = parents[1]; 934 935 if (JXG.isNumber(parents[1])) { 936 x = parents[1]; 937 gli = this.board.create('glider', [x, f.Y(x), f], {fixed:true, visible:false, withLabel:false}); 938 return this.board.create('tangent', [f, gli], this.handleAtts(attributes)); 939 } else if (JXG.exists(parents[1].Value)) { 940 // Fake glider: it needs the properties "position" and "slideObject". 941 gli = this.board.create('point', 942 [function(){ this.position = x.Value(); return x.Value(); }, function(){ return f.Y(x.Value()); }], 943 {visible:false, withLabel:false}); 944 gli.slideObject = f; 945 return this.board.create('tangent', [f, gli], this.handleAtts(attributes)); 946 } else { 947 // Fake glider: it needs the properties "position" and "slideObject". 948 gli = this.board.create('point', 949 [function(){ this.position = x.X(); return x.X(); }, function(){ return f.Y(x.X()); }], 950 {visible:false, withLabel:false}); 951 gli.slideObject = f; 952 return this.board.create('tangent', [f, gli], this.handleAtts(attributes)); 953 } 954 }; 955 956 this.vecteur = function(parents, attributes) { 957 return this.board.create('arrow', parents, this.handleAtts(attributes)); 958 }; 959 960 961 /* 962 * Circles 963 */ 964 this.cercle = function(parents, attributes) { 965 return this.board.create('circle', parents, this.handleAtts(attributes)); 966 }; 967 968 this.cerclerayon = function(parents, attributes) { 969 return this.board.create('circle', parents, this.handleAtts(attributes)); 970 }; 971 972 /* 973 * Polygons 974 */ 975 this.polygone = function(parents, attributes) { 976 return this.board.create('polygon', parents, this.handleAtts(attributes)); 977 }; 978 979 /* 980 * Other 981 */ 982 this.texte = function(parents, attributes) { 983 return this.board.create('text', parents, this.handleAtts(attributes)); 984 }; 985 986 this.reel = function(parents, attributes) { 987 var atts = this.handleAtts(attributes); 988 atts["snapWidth"] = parents[3]; 989 return this.board.create('slider', [[0,-2],[3,-2], [parents[1], parents[0], parents[2]]], atts); 990 }; 991 992 this.entier = function(parents, attributes) { 993 return this.reel(parents, attributes); 994 }; 995 996 this.fonction = function(parents, attributes) { 997 var f = new Function("x", "return " + parents[0]); 998 return this.board.create('functiongraph', [f], this.handleAtts(attributes)); 999 }; 1000 1001 /* 1002 * Transformations 1003 */ 1004 1005 this.homothetie = function(parents, attributes) { 1006 var c = parents[0], a = parents[1]; 1007 if (JXG.isNumber(a)) { 1008 return this.board.create('transform', 1009 [1, 0, 0, 1010 function(){ return (-a+1)*c.X(); }, a, 0, 1011 function(){ return (-a+1)*c.Y(); }, 0, a], 1012 {type:'generic'}); 1013 } else { // Slider 1014 return this.board.create('transform', 1015 [1, 0, 0, 1016 function(){ return (-a.Value()+1)*c.X(); }, function(){ return a.Value();}, 0, 1017 function(){ return (-a.Value()+1)*c.Y(); }, 0, function(){ return a.Value();}], 1018 {type:'generic'}); 1019 } 1020 }; 1021 1022 this.symetrie = function(parents, attributes) { 1023 if (parents.length==1 && JXG.isPoint(parents[0])) { 1024 return this.board.create('transform', [Math.PI, parents[0]], {type:'rotate'}); 1025 } 1026 }; 1027 1028 this.reflexion = function(parents, attributes) { 1029 return this.board.create('transform', [parents[0]], {type:'reflect'}); 1030 }; 1031 1032 this.rotation = function(parents, attributes) { 1033 var a = parents[1]; 1034 if (JXG.isNumber(a)) { 1035 a = (Math.PI*a)/180.0; 1036 return this.board.create('transform', [a, parents[0]], {type:'rotate'}); 1037 } else { // slider 1038 return this.board.create('transform', [function(){ return (Math.PI*a.Value())/180.0;}, parents[0]], {type:'rotate'}); 1039 } 1040 }; 1041 1042 this.translation = function(parents, attributes) { 1043 if (parents.length==1) { 1044 return this.board.create('transform', [ 1045 function(){ return parents[0].point2.X()-parents[0].point1.X(); }, 1046 function(){ return parents[0].point2.Y()-parents[0].point1.Y(); } 1047 ], {type:'translate'}); 1048 } else { 1049 return this.board.create('transform', [ 1050 function(){ return parents[1].X()-parents[0].X(); }, 1051 function(){ return parents[1].Y()-parents[0].Y(); } 1052 ], {type:'translate'}); 1053 } 1054 }; 1055 1056 1057 }; 1058