1 /*
  2     Copyright 2008,2009
  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: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 
 25 */
 26 
 27 /* Compatibility for <= 0.75.4 */
 28 /*
 29 JXG.getReference = function(board, object) {
 30     return JXG.GetReferenceFromParameter(board, object);
 31 };
 32 */
 33 
 34 JXG.IntergeoReader = new function() {
 35     this.board = null;
 36     /**
 37      * this.objects holds all objects from the XML file.
 38      * Every object hets an attribute "exists"
 39      */
 40     this.objects = {}; 
 41 
 42     this.readIntergeo = function(tree,board) {
 43         this.board = board;
 44         this.board.origin = {};
 45         this.board.origin.usrCoords = [1, 0, 0];
 46         this.board.origin.scrCoords = [1, 400, 300];
 47         this.board.unitX = 30;
 48         this.board.unitY = 30;
 49         this.board.updateStretch();
 50 
 51         this.readElements(tree.getElementsByTagName("elements"));
 52         this.readConstraints(tree.getElementsByTagName("constraints"));
 53         this.cleanUp();
 54         this.board.fullUpdate();
 55         this.readDisplay(tree.getElementsByTagName("display"));
 56         this.board.fullUpdate();
 57     };
 58 
 59     /**
 60      * Element part
 61      */
 62     this.readElements = function(tree) {
 63         var s;
 64         for (var s=0;s<tree[0].childNodes.length;s++) (function(s) {
 65             var node;
 66             node = tree[0].childNodes[s];
 67             if (node.nodeType>1) { return; } // not an element node
 68             if (node.nodeName=='point') {
 69                 JXG.IntergeoReader.storePoint(node); //addPoint(node);
 70             } 
 71             else if (node.nodeName=='line' 
 72                      || node.nodeName=='line_segment'
 73                      || node.nodeName=='ray'
 74                      || node.nodeName=='vector'
 75                     ) {
 76                 JXG.IntergeoReader.storeLine(node);
 77             } 
 78             else if (node.nodeName=='circle') {
 79                 JXG.IntergeoReader.storeConic(node);
 80             } 
 81             else if (node.nodeName=='conic') {
 82                 JXG.IntergeoReader.storeConic(node);
 83             } 
 84             else if (node.nodeName=='polygon') {
 85                 // ignore, see this.addPolygonByVertices
 86             } 
 87             else {
 88                 JXG.debug('Not implemented: '+node.nodeName + ' ' + node.getAttribute('id'));
 89             }
 90         })(s);
 91     };
 92 
 93     /**
 94      * Points are created instantly via create
 95      */
 96     this.addPointOld = function(node) {
 97         var i = 0, 
 98             j = 0,
 99             l = 0,
100             el,
101             p = node.childNodes[i],
102             c = [],
103             attributes = {strokeColor:'red', fillColor:'red', withLabel:true},
104             parents = [];
105             
106         while (p.nodeType>1) {  // skip non element nodes
107             i++;
108             p = node.childNodes[i];
109         }
110 
111         attributes['name'] = node.getAttribute('id');
112         
113         if (p.nodeName == 'homogeneous_coordinates') {
114             for (j=0;j<p.childNodes.length;j++) {
115                 if (p.childNodes[j].nodeType==1) {
116                     if (p.childNodes[j].nodeName=='double') {
117                         c.push(p.childNodes[j].firstChild.data);  // content of <double>...</double>
118                     } else if (p.childNodes[j].nodeName=='complex') {
119                             for (l=0;l<p.childNodes[j].childNodes.length;l++) {
120                                 if (p.childNodes[j].childNodes[l].nodeName=='double') {
121                                     c.push(p.childNodes[j].childNodes[l].firstChild.data);
122                                 }
123                             }
124                     } else {
125                         JXG.debug('Not implemented: '+ p.childNodes[j].nodeName);  // <complex>
126                         return;
127                     }
128                 }
129             }
130             for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
131             if (c.length==3) { // Real
132                 parents = [c[2],c[0],c[1]];
133             } else if (c.length==6 && Math.abs(c[1])<1e-10 && Math.abs(c[3])<1e-10 && Math.abs(c[5])<1e-10) {  // complex, but real
134                 parents = [c[4],c[0],c[2]];
135             } else {
136                 JXG.debug('type not supported, yet');  // <complex>
137                 return;
138             }
139         } else if (p.nodeName == 'euclidean_coordinates') {
140             for (j=0;j<p.childNodes.length;j++) {
141                 if (p.childNodes[j].nodeType==1) {
142                     c.push(p.childNodes[j].firstChild.data);  // content of <double>...</double>
143                 }
144             }
145             for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
146             parents = [c[0],c[1]];
147         } else if (p.nodeName == 'polar_coordinates') {
148             for (j=0;j<p.childNodes.length;j++) {
149                 if (p.childNodes[j].nodeType==1) {
150                     c.push(p.childNodes[j].firstChild.data);  // content of <double>...</double>
151                 }
152             }
153             for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
154             parents = [c[0]*Math.cos(c[1]),c[0]*Math.sin(c[1])];
155         } else {
156             JXG.debug('This coordinate type is not yet implemented: ' +p.nodeName);
157             return; 
158         }
159 
160         el = this.board.create('point', parents, attributes);
161         this.objects[node.getAttribute('id')] = el;
162     };
163 
164     /**
165      * Points are created instantly via create
166      */
167     this.storePoint = function(node) {
168         var i = 0, 
169             j = 0,
170             l = 0,
171             p = node.childNodes[i],
172             c = [],
173             parents = [];
174             
175         while (p.nodeType>1) {  // skip non element nodes
176             i++;
177             p = node.childNodes[i];
178         }
179 
180         if (p.nodeName == 'homogeneous_coordinates') {
181             for (j=0;j<p.childNodes.length;j++) {
182                 if (p.childNodes[j].nodeType==1) {
183                     if (p.childNodes[j].nodeName=='double') {
184                         c.push(p.childNodes[j].firstChild.data);  // content of <double>...</double>
185                     } else if (p.childNodes[j].nodeName=='complex') {
186                             for (l=0;l<p.childNodes[j].childNodes.length;l++) {
187                                 if (p.childNodes[j].childNodes[l].nodeName=='double') {
188                                     c.push(p.childNodes[j].childNodes[l].firstChild.data);
189                                 }
190                             }
191                     } else {
192                         JXG.debug('Not implemented: '+ p.childNodes[j].nodeName);  // <complex>
193                         return;
194                     }
195                 }
196             }
197             for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
198             if (c.length==3) { // Real
199                 parents = [c[2],c[0],c[1]];
200             } else if (c.length==6 && Math.abs(c[1])<1e-10 && Math.abs(c[3])<1e-10 && Math.abs(c[5])<1e-10) {  // complex, but real
201                 parents = [c[4],c[0],c[2]];
202             } else {
203                 JXG.debug('type not supported, yet');  // <complex>
204                 return;
205             }
206         } else if (p.nodeName == 'euclidean_coordinates' || p.nodeName == 'euclidian_coordinates') { // the latter one is a workaround for faulty i2g construction exported by DynaGeo
207             for (j=0;j<p.childNodes.length;j++) {
208                 if (p.childNodes[j].nodeType==1) {
209                     c.push(p.childNodes[j].firstChild.data);  // content of <double>...</double>
210                 }
211             }
212             for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
213             parents = [c[0],c[1]];
214         } else if (p.nodeName == 'polar_coordinates') {
215             for (j=0;j<p.childNodes.length;j++) {
216                 if (p.childNodes[j].nodeType==1) {
217                     c.push(p.childNodes[j].firstChild.data);  // content of <double>...</double>
218                 }
219             }
220             for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
221             parents = [c[0]*Math.cos(c[1]),c[0]*Math.sin(c[1])];
222         } else {
223             JXG.debug('This coordinate type is not yet implemented: ' +p.nodeName);
224             return; 
225         }
226 
227         this.objects[node.getAttribute('id')] = {'id':node.getAttribute('id'), 'coords':null};
228         this.objects[node.getAttribute('id')].coords = parents;
229         this.objects[node.getAttribute('id')].id = node.getAttribute('id');
230         this.objects[node.getAttribute('id')].exists = false;
231         //el = this.board.create('point', parents, attributes);
232         //this.objects[node.getAttribute('id')] = el;
233         this.objects[node.getAttribute('id')].i2geoType = 'point';
234     };
235 
236     /**
237      * Line data is stored in an array
238      * for further access during the reading of constraints.
239      * There, id and name are needed.
240      **/
241     this.storeLine = function(node) {
242         var i, p, c, j;
243         
244         this.objects[node.getAttribute('id')] = {'id':node.getAttribute('id'), 'coords':null};
245         i = 0;
246         p = node.childNodes[i];
247         while (p.nodeType>1) {  // skip non element nodes
248             i++;
249             p = node.childNodes[i];
250         }
251         if (p.nodeName == 'homogeneous_coordinates') {
252             c = [];
253             for (j=0;j<p.childNodes.length;j++) {
254                 if (p.childNodes[j].nodeType==1) {
255                     if (p.childNodes[j].nodeName=='double') {
256                         c.push(parseFloat(p.childNodes[j].firstChild.data));  // content of <double>...</double>
257                     } else {
258                         //$('debug').innerHTML += 'Not: '+ p.childNodes[j].nodeName + '<br>';  // <complex>
259                     }
260                 }
261             }
262             this.objects[node.getAttribute('id')].coords = c;
263             this.objects[node.getAttribute('id')].id = node.getAttribute('id');
264             this.objects[node.getAttribute('id')].exists = false;
265             this.objects[node.getAttribute('id')].i2geoType = 'line';
266        }
267         //this.addLine(node.getAttribute('id'));
268     };
269     
270     /**
271      * Circle / conic data is stored in an array
272      * for further access during the reading of constraints.
273      * There, id and name are needed.
274      * Concretely, the circle   (x-1)^2 + (y-3)^2 = 4   has matrix
275      * (  1  0 -1 )
276      * (  0  1 -3 )
277      * ( -1 -3  6 )
278      *
279      * In general
280      * Ax^2+Bxy+Cy^2+Dx+Ey+F = 0
281      * is stored as
282      * (  A   B/2  D/2 )
283      * (  B/2  C   E/2 )
284      * (  D/2 E/2  F )
285      *
286      *  Mx = D/A
287      *  My = E/C
288      *  r = A*Mx^2+B*My^2-F
289      **/
290     this.storeConic = function(node) {
291         var i, j, p, c;
292         
293         this.objects[node.getAttribute('id')] = {'id':node.getAttribute('id'), 'coords':null};
294         i = 0;
295         p = node.childNodes[i];
296         while (p.nodeType>1) {  // skip non element nodes
297             i++;
298             p = node.childNodes[i];
299         }
300         if (p.nodeName == 'matrix') {
301             c = [];
302             for (j=0;j<p.childNodes.length;j++) {
303                 if (p.childNodes[j].nodeType==1) {
304                     if (p.childNodes[j].nodeName=='double') {
305                         c.push(parseFloat(p.childNodes[j].firstChild.data));  // content of <double>...</double>
306                     } else {
307                         //$('debug').innerHTML += 'Not: '+ p.childNodes[j].nodeName + '<br>';  // <complex>
308                     }
309                 }
310             }
311             this.objects[node.getAttribute('id')].coords = c;
312             this.objects[node.getAttribute('id')].id = node.getAttribute('id');
313             this.objects[node.getAttribute('id')].exists = false;
314             this.objects[node.getAttribute('id')].i2geoType = 'conic';
315         }
316     };
317 
318     /**
319      * Constraint part
320      */
321     this.readConstraints = function(tree) {
322         var s, param;
323         
324         // Set the default colors of dependent elements
325         this.board.options.point.strokeColor = 'blue';
326         this.board.options.point.fillColor = 'blue';
327         
328         for (s=0;s<tree[0].childNodes.length;s++) (function(s) {
329             var node;
330             node = tree[0].childNodes[s];
331             if (node.nodeType>1) { return; } // not an element node
332             if (node.nodeName=='line_through_two_points') {
333                 JXG.IntergeoReader.addLineThroughTwoPoints(node, false);
334             } 
335             else if (node.nodeName=='ray_from_point_through_point') {
336                 JXG.IntergeoReader.addLineThroughTwoPoints(node, true);
337             }
338             else if (node.nodeName=='line_through_point') {
339                 JXG.IntergeoReader.addLineThroughPoint(node);
340             } 
341             else if (node.nodeName=='line_parallel_to_line_through_point') {
342                 JXG.IntergeoReader.addLineParallelToLineThroughPoint(node, false);
343             } 
344             else if (node.nodeName=='ray_from_point_and_vector') {
345                 JXG.IntergeoReader.addLineParallelToLineThroughPoint(node, true);
346             } 
347             else if (node.nodeName=='line_perpendicular_to_line_through_point') {
348                 JXG.IntergeoReader.addLinePerpendicularToLineThroughPoint(node);
349             } 
350             else if (node.nodeName=='line_segment_by_points') {
351                 JXG.IntergeoReader.addLineSegmentByTwoPoints(node);
352             } 
353             else if (node.nodeName=='vector_from_point_to_point') {
354                 JXG.IntergeoReader.addVectorFromPointToPoint(node);
355             } 
356             else if (node.nodeName=='endpoints_of_line_segment') {
357                 JXG.IntergeoReader.addEndpointsOfLineSegment(node);
358             } 
359             else if (node.nodeName=='free_point') {
360                 // do nothing
361             } 
362             else if (node.nodeName=='free_line') {
363                 JXG.IntergeoReader.addFreeLine(node);
364             } 
365             else if (node.nodeName=='point_on_line') {
366                 JXG.IntergeoReader.addPointOnLine(node);
367             } 
368             else if (node.nodeName=='point_on_line_segment') {
369                 JXG.IntergeoReader.addPointOnLine(node);
370             }
371             else if (node.nodeName=='point_on_circle') {
372                 JXG.IntergeoReader.addPointOnCircle(node);
373             } 
374             else if (node.nodeName=='angular_bisector_of_three_points') {
375                 JXG.IntergeoReader.addAngularBisectorOfThreePoints(node, false);
376             } 
377             else if (node.nodeName=='angular_bisectors_of_two_lines') {
378                 JXG.IntergeoReader.addAngularBisectorsOfTwoLines(node, false);
379             } 
380             else if (node.nodeName=='line_angular_bisector_of_three_points') {
381                 JXG.IntergeoReader.addAngularBisectorOfThreePoints(node, true);
382             } 
383             else if (node.nodeName=='line_angular_bisectors_of_two_lines') {
384                 JXG.IntergeoReader.addAngularBisectorsOfTwoLines(node, true);
385             } 
386             else if (node.nodeName=='midpoint_of_two_points') {
387                 JXG.IntergeoReader.addMidpointOfTwoPoints(node);
388             } 
389             else if (node.nodeName=='midpoint') {
390                 JXG.IntergeoReader.addMidpointOfTwoPoints(node);
391             } 
392             else if (node.nodeName=='midpoint_of_line_segment' || node.nodeName=='midpoint_line_segment') {
393                 JXG.IntergeoReader.addMidpointOfLineSegment(node);
394             } 
395             else if (node.nodeName=='point_intersection_of_two_lines') {
396                 JXG.IntergeoReader.addPointIntersectionOfTwoLines(node);
397             } 
398             else if (node.nodeName=='locus_defined_by_point') {
399                 JXG.IntergeoReader.addLocusDefinedByPoint(node);
400             } 
401             else if (node.nodeName=='locus_defined_by_point_on_line') {
402                 JXG.IntergeoReader.addLocusDefinedByPointOnLine(node);
403             } 
404             else if (node.nodeName=='locus_defined_by_point_on_line_segment') {
405                 JXG.IntergeoReader.addLocusDefinedByPointOnLine(node);
406             }
407             else if (node.nodeName=='locus_defined_by_line_through_point') {
408                 JXG.IntergeoReader.addLocusDefinedByLineThroughPoint(node);
409             }
410             else if (node.nodeName=='locus_defined_by_point_on_circle') {
411                 JXG.IntergeoReader.addLocusDefinedByPointOnCircle(node);
412             } 
413             else if (node.nodeName=='circle_by_three_points') {
414                 JXG.IntergeoReader.addCircleByThreePoints(node);
415             } 
416             else if (node.nodeName=='circle_by_center_and_point') {
417                 JXG.IntergeoReader.addCircleByCenterAndPoint(node);
418             } 
419             else if (node.nodeName=='center_of_circle') {
420                 JXG.IntergeoReader.addCenterOfCircle(node);
421             } 
422             else if (node.nodeName=='intersection_points_of_two_circles') {
423                 JXG.IntergeoReader.addIntersectionPointsOfTwoCircles(node);
424             } 
425             else if (node.nodeName=='intersection_points_of_circle_and_line') {
426                 JXG.IntergeoReader.addIntersectionPointsOfCircleAndLine(node);
427             } 
428             else if (node.nodeName=='other_intersection_point_of_two_circles') {
429                 JXG.IntergeoReader.addOtherIntersectionPointOfTwoCircles(node);
430             } 
431             else if (node.nodeName=='other_intersection_point_of_circle_and_line') {
432                 JXG.IntergeoReader.addOtherIntersectionPointOfCircleAndLine(node);
433             } 
434             else if (node.nodeName=='circle_tangent_lines_by_point') {
435                 JXG.IntergeoReader.addCircleTangentLinesByPoint(node);
436             } 
437             else if (node.nodeName=='polygon_by_vertices') {
438                 JXG.IntergeoReader.addPolygonByVertices(node);
439             } 
440             else {
441                 param = JXG.IntergeoReader.readParams(node);
442                 JXG.debug('readConstraints: not implemented: ' + node.nodeName + ': ' + param[0]);
443             }
444         })(s);
445     };
446 
447     this.setAttributes = function(o) {
448         o.setProperty({strokecolor:this.board.options.point.strokeColor,fillColor:this.board.options.point.fillColor});
449     }
450     
451     this.readParams = function(node) {
452         var param = [], j;
453         for (j=0;j<node.childNodes.length;j++) {
454             if (node.childNodes[j].nodeType==1) {
455                 param.push(node.childNodes[j].firstChild.data);
456             }
457         }
458         return param;
459     };
460 
461     this.addPoint = function(p) {
462         if (!p.exists) {
463             p.exists = true;
464             p = this.board.create('point',p.coords,{name:p.id});
465             p.setProperty({strokecolor:'red',fillColor:'red'});
466 
467             //this.setAttributes(p);
468         }
469         return p;
470     };
471 
472     /** 
473      * Direct construction of a line 
474      * in read elements
475      **/
476     this.addLine = function(id) {    
477         var j,
478             c = this.objects[id].coords,
479             el;
480             
481         for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
482         el = this.board.create('line',[c[2],c[0],c[1]],{name:id, strokeColor:'black', withLabel:true});
483         this.objects[id] = el;
484     };
485 
486     this.addConic = function(p) {
487         var c;
488         if (!p.exists) {
489             c = p.coords;
490             // (a_00,a_11,a_22,a_01,a_12,a_22)
491             p = this.board.create('conic',[c[0],c[4],c[8],c[1],c[5],c[2]],{name:p.id});
492             //p.setProperty({strokecolor:'blue',fillColor:'none'});
493             //this.setAttributes(p);
494             p.exists = true;
495         }
496         return p;
497     };
498 
499     this.cleanUp = function() {
500         var p;
501         for (p in this.objects) {
502             if (this.objects[p].exists==false) {
503                 if (this.objects[p].i2geoType=='point') {
504                     this.addPoint(this.objects[p]); 
505                 } else if (this.objects[p].i2geoType=='line') {
506                     this.addLine(this.objects[p]); 
507                 } else if (this.objects[p].i2geoType=='conic') {
508                     this.addConic(this.objects[p]); 
509                 } else {
510                     JXG.debug('forgotten: '+ this.objects[p].id +' of type ' + this.objects[p].i2geoType);
511                 }
512             }
513         }
514     };
515     
516     this.addLineThroughTwoPoints = function(node, isRay) {
517         var param = JXG.IntergeoReader.readParams(node),
518             el1, el2, el;
519             
520         el1 = this.addPoint(this.objects[param[1]]);
521         el2 = this.addPoint(this.objects[param[2]]);
522         el = this.board.create('line', [el1.id,el2.id], {name:param[0],withLabel:true, straightFirst:!isRay, straightLast:true});
523         this.objects[param[0]] = el;
524         this.objects[param[0]].exists = true;
525     };
526 
527     this.addLineThroughPoint = function(node) {        
528         var param = JXG.IntergeoReader.readParams(node),
529             j,
530             c = this.objects[param[0]].coords,
531             p = this.addPoint(this.objects[param[1]]),
532             el;
533         
534         for (j=0;j<c.length;j++) { c[j] = parseFloat(c[j]); }
535         el = this.board.create('line',[
536                     function() { return c[2]-c[0]*p.X()-c[1]*p.Y()-c[2]*p.Z(); }, c[0], c[1]
537                 ],{name:param[0], strokeColor:'black', withLabel:true});
538         this.objects[param[0]] = el;
539         this.objects[param[0]].exists = true;
540     };
541 
542     this.addLineParallelToLineThroughPoint = function(node, isRay) {
543         var param = JXG.IntergeoReader.readParams(node), 
544             el1, el2, el;
545 
546         el1 = this.addPoint(this.objects[param[1]]);
547         el2 = this.addPoint(this.objects[param[2]]);
548         el = this.board.create('parallel',[el1.id,el2.id], {name:param[0],withLabel:true, straightFirst:!isRay, straightLast:true});
549         this.objects[param[0]] = el;
550         this.objects[param[0]].exists = true;
551     };
552 
553     this.addLinePerpendicularToLineThroughPoint = function(node) {
554         var param = JXG.IntergeoReader.readParams(node),
555             el1, el2, el;
556         
557         el1 = this.addPoint(this.objects[param[1]]);
558         el2 = this.addPoint(this.objects[param[2]]);
559         el = this.board.create('perpendicular',[el1.id,el2.id],
560                                 {name:[param[0],param[0]+'foot'],id:[param[0],param[0]+'foot'],withLabel:true});
561         el[0].setProperty("straightFirst:true","straightLast:true"); // line
562         el[1].setProperty("visible:false");                          // point
563         this.objects[param[0]] = el[0];
564         this.objects[param[0]].exists = true;
565     };
566 
567     this.addLineSegmentByTwoPoints = function(node) {
568         var param = JXG.IntergeoReader.readParams(node),
569             el1, el2, el;
570 
571         el1 = this.addPoint(this.objects[param[1]]);
572         el2 = this.addPoint(this.objects[param[2]]);
573         el = this.board.create('line',[el1.id,el2.id], 
574                         {name:param[0],
575                             straightFirst:false, straightLast:false,
576                             strokeColor:'black',
577                             withLabel:true});
578         this.objects[param[0]] = el;
579         this.objects[param[0]].exists = true;
580     };
581 
582     this.addPointIntersectionOfTwoLines = function(node) {
583         var param = JXG.IntergeoReader.readParams(node),
584             l1 = this.objects[param[1]],
585             l2 = this.objects[param[2]];
586         
587         this.objects[param[0]] = this.board.create('intersection',[l1,l2,0], {name:param[0],id:param[0], withLabel:true});
588         this.setAttributes(this.objects[param[0]]);
589         this.objects[param[0]].exists = true;
590     };
591 
592     this.addFreeLine = function(node) {
593         var param = JXG.IntergeoReader.readParams(node),
594             a = this.objects[param[0]].coords[0],
595             b = this.objects[param[0]].coords[1],
596             c = this.objects[param[0]].coords[2],
597             el = this.board.create('line',[c,a,b],{name:param[0],id:param[0],withLabel:true});
598         this.objects[param[0]] = el;
599         this.objects[param[0]].exists = true;
600     };
601 
602     this.addPointOnLine = function(node) {
603         var param = JXG.IntergeoReader.readParams(node),
604             l = JXG.getReference(this.board,param[1]),
605             el;
606         el = this.board.create('glider',[0,0,l],{name:param[0],id:param[0],withLabel:true});
607         //this.setAttributes(p);
608         this.objects[param[0]].exists = true;
609     };
610 
611     this.addPointOnCircle = function(node) {
612         var param = JXG.IntergeoReader.readParams(node),
613             c = JXG.getReference(this.board,param[1]),
614             el;
615         c.update();
616         el = this.board.create('glider',[this.objects[param[0]].coords[1],this.objects[param[0]].coords[2],c],
617                     {name:param[0],id:param[0],withLabel:true});
618         //this.setAttributes(p);
619         this.objects[param[0]].exists = true;
620     };
621 
622     this.addEndpointsOfLineSegment = function(node) {
623         var param = JXG.IntergeoReader.readParams(node),
624             line = this.objects[param[2]],
625             p = this.addPoint(this.objects[param[0]]),
626             q = this.addPoint(this.objects[param[1]]);
627 
628         p.addConstraint([
629                     function(){return line.point1.Z();},
630                     function(){return line.point1.X();},
631                     function(){return line.point1.Y();}
632                     ]);
633         q.addConstraint([
634                     function(){return line.point2.Z();},
635                     function(){return line.point2.X();},
636                     function(){return line.point2.Y();}
637                     ]);
638         this.setAttributes(p);
639         this.setAttributes(q);
640     };
641     
642     this.addAngularBisectorOfThreePoints = function(node, isLine) {
643         var param = JXG.IntergeoReader.readParams(node),
644             el1, el2, el3, el;
645             
646         el1 = this.addPoint(this.objects[param[1]]);
647         el2 = this.addPoint(this.objects[param[2]]);
648         el3 = this.addPoint(this.objects[param[3]]);
649         el = this.board.create('bisector',[el1.id,el2.id,el3.id],
650                                 {name:param[0], id:param[0], withLabel:true});
651                                 //{name:[param[0]+'_1',param[0]+'_2'], id:[param[0]+'_1',param[0]+'_2'], withLabel:false});
652         el.setProperty({straightFirst:isLine,straightLast:true,strokeColor:'#000000'});
653         this.objects[param[0]] = el;
654         this.objects[param[0]].exists = true;
655     };
656     
657     this.addMidpointOfTwoPoints = function(node) {
658         var param = JXG.IntergeoReader.readParams(node),
659             el1, el2, el3;
660 
661         el1 = this.addPoint(this.objects[param[1]]);
662         el2 = this.addPoint(this.objects[param[2]]);
663         el = this.board.create('midpoint',[el1.id,el2.id],{name:param[0]});
664         this.setAttributes(el);
665         this.objects[param[0]].exists = true;
666     };
667 
668     this.addMidpointOfLineSegment = function(node) {
669         var param = JXG.IntergeoReader.readParams(node),
670             l = JXG.getReference(this.board,param[1]);
671         el = this.board.create('midpoint',[l.point1,l.point2],{name:param[0]});
672         this.setAttributes(el);
673         this.objects[param[0]].exists = true;
674     };
675 
676     this.addCircleByThreePoints = function(node) {
677         var param = JXG.IntergeoReader.readParams(node),
678             p = [], i, ar;
679         for (i=0;i<3;i++) {
680           p[i] = this.addPoint(this.objects[param[i+1]]); //JXG.getReference(this.board,param[i+1]);
681         }
682         ar = this.board.create('circumcircle',p, {name:[param[0]+'c',param[0]], id:[param[0]+'c',param[0]],withLabel:true});
683         ar[0].setProperty({visible:false}); // center should be invisible
684         ar[1].setProperty({withLabel:true}); // label of circle does not work yet
685         this.objects[param[0]].exists = true;
686     };
687 
688     this.addCenterOfCircle = function(node) {
689         var param = JXG.IntergeoReader.readParams(node),
690             c = JXG.getReference(this.board,param[1]),
691             el = this.board.create('point',[function(){return c.midpoint.X();},function(){return c.midpoint.Y();}],
692                     {name:param[0], id:param[0],withLabel:true});
693         this.setAttributes(el);
694         this.objects[param[0]].exists = true;
695     };
696 
697     this.addCircleTangentLinesByPoint = function(node) {
698         var param = JXG.IntergeoReader.readParams(node),
699             c = JXG.getReference(this.board,param[2]),
700             p = this.addPoint(this.objects[param[3]]),
701             //t1 = this.objects[param[0]],
702             //t2 = this.objects[param[1]];
703             m, polar, i1, i2, t1, t2;
704         
705         polar = this.board.create('line', [
706                     function(){ return JXG.Math.matVecMult(c.quadraticform,p.coords.usrCoords)[0]; },
707                     function(){ return JXG.Math.matVecMult(c.quadraticform,p.coords.usrCoords)[1]; },
708                     function(){ return JXG.Math.matVecMult(c.quadraticform,p.coords.usrCoords)[2]; }
709                 ] , {visible:false});     
710 
711         i1 = this.board.create('intersection', [c,polar,0],{visible:false});
712         i2 = this.board.create('intersection', [c,polar,1],{visible:false});
713         //t1 = this.board.create('line', [p,i1]);
714         //t2 = this.board.create('line', [p,i2]);
715         t1 = this.board.create('tangent', [i1,c]);
716         t2 = this.board.create('tangent', [i2,c]);
717         this.objects[param[0]] = t1;
718         this.objects[param[1]] = t2;
719         this.objects[param[0]].exists = true;
720         this.objects[param[1]].exists = true;
721     };
722 
723     this.addIntersectionPointsOfTwoCircles = function(node) {
724         var param = JXG.IntergeoReader.readParams(node),
725             c1 = JXG.getReference(this.board,param[2]),
726             c2 = JXG.getReference(this.board,param[3]),
727             p1, p2;
728             //p1 = this.objects[param[0]],
729             //p2 = this.objects[param[1]];
730         //p1.addConstraint([this.board.intersection(c1,c2,0)]);
731         //p2.addConstraint([this.board.intersection(c1,c2,1)]);
732         p1 = this.board.create('intersection',[c1,c2,0], {name:param[0], id:param[0],withLabel:true});
733         p2 = this.board.create('intersection',[c1,c2,1], {name:param[1], id:param[1],withLabel:true});
734         this.setAttributes(p1);
735         this.setAttributes(p2);
736         this.objects[param[0]].exists = true;
737         this.objects[param[1]].exists = true;
738     };
739     
740     this.addIntersectionPointsOfCircleAndLine = function(node) {
741         var param = JXG.IntergeoReader.readParams(node),
742             c1 = JXG.getReference(this.board,param[2]),
743             c2 = JXG.getReference(this.board,param[3]),
744             p1, p2;
745             //p1 = this.objects[param[0]],
746             //p2 = this.objects[param[1]];
747         
748         p1 = this.board.create('intersection',[c1,c2,0], {name:param[0], id:param[0],withLabel:true});
749         p2 = this.board.create('intersection',[c1,c2,1], {name:param[1], id:param[1],withLabel:true});
750         this.setAttributes(p1);
751         this.setAttributes(p2);
752         this.objects[param[0]].exists = true;
753         this.objects[param[1]].exists = true;
754     };
755 
756     this.addCircleByCenterAndPoint = function(node) {
757         var param = JXG.IntergeoReader.readParams(node),
758             el1 = this.addPoint(this.objects[param[1]]),
759             el2 = this.addPoint(this.objects[param[2]]);
760             
761         el = this.board.create('circle',
762             [el1.id,el2.id],
763             {name:param[0],id:param[0],withLabel:true});
764         this.objects[param[0]].exists = true;
765     };
766 
767     this.addOtherIntersectionPointOfTwoCircles = function(node) {
768         var param = JXG.IntergeoReader.readParams(node),
769             c1 = JXG.getReference(this.board,param[2]),
770             c2 = JXG.getReference(this.board,param[3]),
771             p1 = JXG.getReference(this.board,param[1]), // Should exist by now
772             p2;
773             //p1  = this.objects[param[1]],
774             //p2  = this.objects[param[0]]; // output
775 
776         //p2.addConstraint([this.board.otherIntersection(c1,c2,p1)]);
777         p2 = this.board.create('otherintersection',[c1,c2,p1], {name:param[0], id:param[0],withLabel:true});
778         this.setAttributes(p2);
779         this.objects[param[0]].exists = true;
780     };
781     
782     this.addOtherIntersectionPointOfCircleAndLine = function(node) {
783         this.addOtherIntersectionPointOfTwoCircles(node);
784     };
785 
786     /**
787      * The angular bisectors of two line [c1,a1,b1] and [c2,a2,b2] are determined by the equation:
788      * (a1*x+b1*y+c1*z)/sqrt(a1^2+b1^2) = +/- (a2*x+b2*y+c2*z)/sqrt(a2^2+b2^2)
789      */
790     this.addAngularBisectorsOfTwoLines = function(node, isLine) {
791         var param = JXG.IntergeoReader.readParams(node),
792             l1 = this.objects[param[2]],
793             l2 = this.objects[param[3]],
794             ar;
795 
796         ar = this.board.create('bisectorlines',
797             [l1,l2],
798             {name:[param[0],param[1]], id:[param[0],param[1]],
799             straightFirst:true, straightLast:true, strokeColor:'#ff0000', withLabel:true});
800         this.objects[param[0]].exists = true;
801         this.objects[param[1]].exists = true;
802     };
803     
804     this.addPolygonByVertices = function(node) {
805         var j, n, param2 = [], p = [], el,
806             param = JXG.IntergeoReader.readParams(node);
807 
808         for (j=0;j<node.childNodes.length;j++) {
809             if (node.childNodes[j].nodeType==1) {
810                 if (node.childNodes[j].nodeName=='list_of_vertices') {
811                     n = node.childNodes[j];
812                     param2 = JXG.IntergeoReader.readParams(n);
813                     break;
814                 }
815             }
816         }
817         for (j=0;j<param2.length;j++) {
818             p.push(this.addPoint(this.objects[param2[j]]));
819         }
820             
821         el = this.board.create('polygon', p, {name:param[0],id:param[0],withLabel:true});
822         this.objects[param[0]].exists = true;
823     };
824 
825     this.addVectorFromPointToPoint = function(node) {
826         var param = JXG.IntergeoReader.readParams(node),
827             el1, el2;
828 
829         el1 = this.addPoint(this.objects[param[1]]);
830         el2 = this.addPoint(this.objects[param[2]]);
831         el = this.board.create('arrow',[el1.id,el2.id],{name:param[0]});
832         this.setAttributes(el);
833         this.objects[param[0]].exists = true;
834     };
835 
836 // ----------------------------------------------------------------------------------------------------
837     
838     this.addLocusDefinedByPoint = function(node) {
839         var param = JXG.IntergeoReader.readParams(node), 
840             el = JXG.getReference(this.board,param[1]);
841         el.setProperty({trace:true});
842         this.objects[param[1]] = el;
843         this.setAttributes(el);
844     };
845     
846     this.addLocusDefinedByPointOnLine = function(node) {
847         var param = JXG.IntergeoReader.readParams(node),
848             el = JXG.getReference(this.board,param[1]);
849         el.setProperty({trace:true});
850         this.objects[param[1]] = el;
851         this.setAttributes(el);
852     };
853 
854     this.addLocusDefinedByLineThroughPoint = function(node) {
855         var param = JXG.IntergeoReader.readParams(node),
856             el = JXG.getReference(this.board,param[1]);
857         el.setProperty({trace:true});
858         this.objects[param[1]] = el;
859         this.setAttributes(el);
860     };
861     
862     this.addLocusDefinedByPointOnCircle = function(node) {
863         var param = JXG.IntergeoReader.readParams(node), 
864             el = JXG.getReference(this.board,param[1]);
865         el.setProperty({trace:true});
866         this.objects[param[1]] = el;
867         this.setAttributes(el);
868     };
869         
870     /**
871      * Extract the xml-code as String from the zipped Intergeo archive.
872      * @return {string} xml code
873      */
874     this.prepareString = function(fileStr){
875         var bA = [], i;
876         
877         if (fileStr.indexOf('<')!=0) {
878             //binary = false;
879             for (i=0;i<fileStr.length;i++)
880                 bA[i]=JXG.Util.asciiCharCodeAt(fileStr,i);
881                    
882             fileStr = (new JXG.Util.Unzip(bA)).unzipFile("construction/intergeo.xml");  // Unzip 
883                                                                                         // Extract "construction/intergeo.xml" from
884                                                                                         // the zip-archive in bA.
885         }
886 
887         return fileStr;
888     };
889 
890     /**
891      * Displpay part
892      */
893     this.readDisplay = function(tree) {
894         var s, j;
895         
896         for (s=0;s<tree[0].childNodes.length;s++) (function(s) {
897             var node, el, prop = {}, key, val;
898             node = tree[0].childNodes[s];
899             if (node.nodeType>1) { return; } // not an element node
900             if (node.nodeName=='background-color') {
901                 this.board.containerObj.style.backgroundColor = node.firstChild.data;
902             } 
903             else if (node.nodeName=='style') {
904                 el = JXG.getReference(this.board,node.getAttribute('ref'));  // get the element
905                 var param = [], j;
906                 for (j=0;j<node.childNodes.length;j++) {
907                     if (node.childNodes[j].nodeType==1) {
908                         key = node.childNodes[j].nodeName;
909                         val = node.childNodes[j].firstChild.data;
910                         if (key=='stroke') {
911                             key = 'strokeColor';
912                         } else if (key=='stroke-width' || key=='border-width') {
913                             key = 'strokeWidth';
914                         } else if (key=='fill') {
915                             key = 'fillColor';
916                         } else if (key=='fill-opacity') {
917                             key = 'fillOpacity';
918                         } else if (key=='border-opacity') {
919                             key = 'strokeOpacity';
920                         } else if (key=='point-size') {
921                             key = 'size';
922                         } else if (key=='label') {
923                             key = 'name';
924                         } else if (key=='point-style') {
925                             key = 'face';
926                             if (val=='circle') {
927                                 val == 'o';
928                             } else if (val=='cross') {
929                                 val = '+';
930                             } else if (val=='x-mark') {
931                                 val = 'x';
932                             } else if (val=='square') {
933                                 val = '[]';
934                             } else if (val=='triangle') {
935                                 val = 'triangleup';
936                             } else if (val=='point') {  // Setting size to 1 is missing
937                                 val = 'o';            
938                             }
939                             else {
940                                 JXG.debug('Display: not implemented' + node.nodeName);
941                                 // Missing:
942                                 // circumference, image
943                             }
944                         }
945                         prop[key] = val;
946                     }
947                 }
948                 el.setProperty(prop);
949             }
950             else {
951                 JXG.debug('Display: not implemented' + node.nodeName);
952             }
953         })(s);
954     };
955 
956 };
957 
958 
959