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