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 /**
 28  * @fileoverview The object Intersection is defined in this file. Intersection
 29  * manages all properties and actiones required to cut circles and lines with
 30  * each other and draws the intersection points.
 31  * @author graphjs
 32  * @version 0.1
 33  */
 34 
 35 /**
 36  * Constructs a new Intersection object.
 37  * @class This is the Intersection class. 
 38  * It manages all properties and actiones required to cut circles and lines with
 39  * each other and draws the intersection points.
 40  * @constructor
 41  * @param {String,Board} board The board the new point is drawn on.
 42  * @param {Array} coordinates An array with the affine user coordinates of the point.
 43  * @param {String} id Unique identifier for the point. If null or an empty string is given,
 44  *  an unique id will be generated by Board
 45  * @see JXG.Board#addPoint
 46  * @param {String} name Not necessarily unique name for the point. If null or an
 47  *  empty string is given, an unique name will be generated
 48  * @see JXG.Board#generateName
 49  * @param {bool} show False if the point is invisible, True otherwise
 50  */
 51 JXG.Intersection = function(Board, Id, Intersect1, Intersect2, InterId1, InterId2, InterName1, InterName2) {
 52     this.constructor();
 53     /**
 54      * Reference to board where the intersected elements are drawn.
 55      * @type JXG.Board
 56      * @see JXG.Board
 57      */
 58     this.board = Board;
 59     
 60     /**
 61      * Unique identifier for the element. Equivalent to id-attribute of renderer element.
 62      * @type String
 63      */
 64     this.id = Id;
 65     this.name = this.id;
 66 
 67     /**
 68      * True when this object is visible, false otherwise.
 69      * @type bool
 70      */
 71     this.visProp = {};
 72     this.visProp['visible'] = true;
 73     this.show = true; // noch noetig? BV
 74     
 75     /**
 76      * True when the intersection points have real coordinates, false otherwise.
 77      * @type bool
 78      */    
 79     this.real = true;
 80     
 81     /** 
 82      * Stores all Intersection Objects which in this moment are not real and
 83      * hide this element.
 84      */
 85     this.notExistingParents = {};
 86 
 87     /**
 88      * Geometry element that is intersected with intersect2.
 89      * @type JXG.GeometryElement
 90      * @see #intersect2
 91      */
 92     this.intersect1 = JXG.getReference(this.board, Intersect1);
 93 
 94     /**
 95      * Geometry element that is intersected with intersect1.
 96      * @type JXG.GeometryElement
 97      * @see #intersect1
 98      */
 99     this.intersect2 = JXG.getReference(this.board, Intersect2);
100 
101     /**
102      * Type of this object. For internal use only.
103      * @private
104      */
105     this.type = JXG.OBJECT_TYPE_INTERSECTION;     
106 
107     /*
108      * Only intersect existing geometry elements.
109      */
110     if( ((this.intersect1 == '') || (!JXG.exists(this.intersect1))) && ((this.intersect2 == '') || (!JXG.exists(this.intersect2)))) {
111         return;
112     }
113 
114     /*
115      * Do not intersect elements which aren't of type line, arrow, circle or arc.
116      */
117     if( ((this.intersect1.type == this.intersect2.type) && (this.intersect1.type == JXG.OBJECT_TYPE_LINE || this.intersect1.type == JXG.OBJECT_TYPE_ARROW)) 
118          || ((this.intersect1.type == JXG.OBJECT_TYPE_LINE) && (this.intersect2.type == JXG.OBJECT_TYPE_ARROW))
119          || ((this.intersect2.type == JXG.OBJECT_TYPE_LINE) && (this.intersect1.type == JXG.OBJECT_TYPE_ARROW)) ) {
120         /* Intersect two elements of type line or arrow */
121         
122         var coords = JXG.Math.Geometry.intersectLineLine(this.intersect1, this.intersect2, this.board).usrCoords.slice(1);
123 
124         /* Create intersection point */
125         this.p = new JXG.Point(this.board, coords, InterId1, InterName1, true);
126         /* A point constructed by an intersection can't be moved, so it is fixed */
127         this.p.fixed = true;
128         this.addChild(this.p);
129         this.real = true;
130 
131         /* 
132          * Because the update function depends on the types of the intersected elements
133          * the update method has to be defined dynamically in dependence of the intersected
134          * elements.
135          */
136         this.update = function () {
137             /* Calculate the coordinates of the intersection point in dependance of the intersected elements */
138             if (this.needsUpdate) {
139                 this.p.coords = JXG.Math.Geometry.intersectLineLine(this.intersect1, this.intersect2, this.board);
140                 /* Update the point */
141                 //this.p.update();
142                 this.needsUpdate = false;
143             }
144         };
145         
146         /*
147          * Hides the element, generated dynamically.
148          */
149         this.hideElement = function() {
150             this.visProp['visible'] = false;
151             this.p.hideElement();
152         };
153         
154         /*
155          * Shows the element, generated dynamically.
156          */
157         this.showElement = function() {
158             this.visProp['visible'] = true;
159             this.p.showElement();
160         };
161         
162         /*
163          * Hides the element and his children. This is called from parents which became invisible or unreal
164          * and so this element isn't real anymore. The not existing parent is stored in the notExistingParents
165          * array.
166          */
167         this.hideChild = function(id) {    
168             this.notExistingParents[id] = this.board.objects[id];
169 
170             for(var el in this.descendants) {    
171                 if(this.descendants[el].visProp['visible'] && this.descendants[el].type != JXG.OBJECT_TYPE_INTERSECTION) {
172                     if(this.descendants[el].type != JXG.OBJECT_TYPE_TEXT) {
173                         this.descendants[el].hideElement();
174                         this.descendants[el].visProp['visible'] = true;
175                     }
176                     else {
177                         if(!this.descendants[el].isLabel) {
178                             this.descendants[el].hideElement();
179                             this.descendants[el].visProp['visible'] = true;
180                         }
181                     }
182                 }      
183                 this.descendants[el].notExistingParents[id] = this.board.objects[id];
184             }            
185         };
186 
187         /*
188          * Shows the element and his children. This is called from parents which became visible or real
189          * and so this element is now real. The formerly not existing parent is deleted from the
190          * notExistingParents array.
191          */
192         this.showChild = function(id) {        
193             for(var el in this.board.objects) {        
194                 delete(this.board.objects[el].notExistingParents[id]);
195                 if(this.board.objects[el].visProp['visible'] && JXG.keys(this.board.objects[el].notExistingParents).length == 0) {
196                     if(this.board.objects[el].type != JXG.OBJECT_TYPE_INTERSECTION) {
197                         this.board.objects[el].showElement();
198                     }
199                 }
200             }
201         };
202     }
203     else if( ((Intersect1.type == Intersect2.type) && (Intersect1.type == JXG.OBJECT_TYPE_CIRCLE || Intersect1.type == JXG.OBJECT_TYPE_ARC)) ||
204               (Intersect1.type == JXG.OBJECT_TYPE_CIRCLE && Intersect2.type == JXG.OBJECT_TYPE_ARC) ||
205               (Intersect2.type == JXG.OBJECT_TYPE_CIRCLE && Intersect1.type == JXG.OBJECT_TYPE_ARC) ) { // Circle <-> Circle, Arc <-> Arc, Arc <-> Circle,
206         this.p1 = new JXG.Point(this.board, [0, 0], InterId1, InterName1, false);
207         this.p1.fixed = true;
208         this.p1.label.content.visProp['visible'] = true;
209         this.p2 = new JXG.Point(this.board, [0, 0], InterId2, InterName2, false);
210         this.p2.fixed = true;
211         this.p2.label.content.visProp['visible'] = true;
212         this.addChild(this.p1);
213         this.addChild(this.p2);
214 
215         var coordinates = JXG.Math.Geometry.intersectCircleCircle(this.intersect1, this.intersect2, this.board);
216         if(coordinates[0] == 1) {
217             this.p1.coords = coordinates[1];
218             this.p1.showElement();
219             this.p1.updateRenderer();
220 
221             this.p2.coords = coordinates[2];
222             this.p2.showElement();
223             this.p2.updateRenderer();
224             
225             this.real = true;
226         }
227         else {
228             this.real = false;
229         }
230 
231         this.update = function () {    
232             if (!this.needsUpdate) { return; }
233             var coordinates = JXG.Math.Geometry.intersectCircleCircle(this.intersect1, this.intersect2, this.board);
234             var p1show = this.p1.visProp['visible'];
235             var p2show = this.p2.visProp['visible'];         
236             if(coordinates[0] == 0) {  
237                 if(this.real) {
238                     this.hideChild(this.id);
239                     this.p1.visProp['visible'] = p1show;
240                     this.p2.visProp['visible'] = p2show;
241                     this.real = false;
242                 }
243             } else {
244                 this.p1.coords = coordinates[1];     
245                 this.p2.coords = coordinates[2];
246                 if(!this.real) {
247                     this.showChild(this.id); 
248                     this.real = true;
249                 }
250             }
251             this.needsUpdate = false;
252         };
253         
254         this.hideElement = function() {
255             this.visProp['visible'] = false;
256             this.p1.hideElement();
257             this.p2.hideElement();
258         };
259         
260         this.showElement = function() {
261             this.visProp['visible'] = true;
262             this.p1.showElement();
263             this.p2.showElement();
264         };
265 
266         this.hideChild = function(id) {
267             this.notExistingParents[id] = this.board.objects[id];
268 
269             for(var el in this.descendants) {    
270                 if(this.descendants[el].visProp['visible'] && this.descendants[el].type != JXG.OBJECT_TYPE_INTERSECTION) {
271                     if(this.descendants[el].type != JXG.OBJECT_TYPE_TEXT) {
272                         this.descendants[el].hideElement();
273                         this.descendants[el].visProp['visible'] = true;
274                     }
275                     else {
276                         if(!this.descendants[el].isLabel) {
277                             this.descendants[el].hideElement();
278                             this.descendants[el].visProp['visible'] = true;
279                         }
280                     }
281                 }      
282                 this.descendants[el].notExistingParents[id] = this.board.objects[id];
283             }                
284         };
285 
286         this.showChild = function(id) {            
287             var el;
288             for(el in this.board.objects) {        
289                 delete(this.board.objects[el].notExistingParents[id]);
290                 if(this.board.objects[el].visProp['visible'] && JXG.keys(this.board.objects[el].notExistingParents).length == 0) {
291                     if(this.board.objects[el].type != JXG.OBJECT_TYPE_INTERSECTION) {
292                         this.board.objects[el].showElement();
293                     }
294                 }        
295             }            
296         };
297     }
298     else { // Circle <-> Line, Arc <-> Line, Circle <-> Arrow, Arc <-> Arrow
299         this.p1 = new JXG.Point(this.board, [0, 0], InterId1, InterName1, false);
300         this.p1.fixed = true;
301         this.p1.label.content.visProp['visible'] = true;        
302         this.p2 = new JXG.Point(this.board, [0, 0], InterId2, InterName2, false);
303         this.p2.fixed = true;
304         this.p2.label.content.visProp['visible'] = true;
305         this.addChild(this.p1);
306         this.addChild(this.p2);        
307         
308         if(this.intersect1.type == JXG.OBJECT_TYPE_LINE || this.intersect1.type == JXG.OBJECT_TYPE_ARROW) {
309             var swap = this.intersect1;
310             this.intersect1 = this.intersect2;
311             this.intersect2 = swap;
312         }
313         
314         var coordinates = JXG.Math.Geometry.intersectCircleLine(this.intersect1, this.intersect2, this.board);
315         if(coordinates[0] == 1) { // not really implemented
316             this.p1.coords = coordinates[1];
317             this.p1.showElement();
318             this.p1.update();    
319         } 
320         else if(coordinates[0] == 2) {
321             this.p1.coords = coordinates[1];
322             this.p1.showElement();        
323 
324             this.p2.coords = coordinates[2];
325             this.p2.showElement();            
326 
327             //this.p1.update();
328             this.p1.updateRenderer();
329             //this.p2.update(); 
330             this.p2.updateRenderer();    
331             
332             this.real = true;
333         }
334         else {
335             this.real = false;
336         }
337 
338         this.update = function () {
339             if (!this.needsUpdate) { return; }
340             var coordinates = JXG.Math.Geometry.intersectCircleLine(this.intersect1, this.intersect2, this.board);
341             var show1 = this.p1.visProp['visible'];
342             var show2 = this.p2.visProp['visible'];
343             
344             if(coordinates[0] == 0) {
345                 if(this.real) {
346                     this.hideChild(this.id);
347                     this.p1.visProp['visible'] = show1; 
348                     this.p2.visProp['visible'] = show2;
349                     this.real = false;
350                 }
351             } else if(coordinates[0] == 2) {
352                 this.p1.coords = coordinates[1];   
353                 this.p2.coords = coordinates[2];
354                 if(!this.real) {
355                     this.showChild(this.id);  
356                     this.real = true;
357                 }
358             }
359             this.needsUpdate = false;
360         };
361         
362         this.hideElement = function() {
363             this.visProp['visible'] = false;
364             this.p1.hideElement();
365             this.p2.hideElement();
366         };
367         
368         this.showElement = function() {
369             this.visProp['visible'] = true;
370             this.p1.showElement();
371             this.p2.showElement();
372         };
373 
374         this.hideChild = function(id) {
375             this.notExistingParents[id] = this.board.objects[id];
376 
377             for(var el in this.descendants) {    
378                 if(this.descendants[el].visProp['visible'] && this.descendants[el].type != JXG.OBJECT_TYPE_INTERSECTION) {
379                     if(this.descendants[el].type != JXG.OBJECT_TYPE_TEXT) {
380                         this.descendants[el].hideElement();
381                         this.descendants[el].visProp['visible'] = true;
382                     }
383                     else {
384                         if(!this.descendants[el].isLabel) {
385                             this.descendants[el].hideElement();
386                             this.descendants[el].visProp['visible'] = true;
387                         }
388                     }
389                 }      
390                 this.descendants[el].notExistingParents[id] = this.board.objects[id];
391             }                
392         };
393 
394         this.showChild = function(id) {
395             var el;
396             for(el in this.board.objects) {            
397                 delete(this.board.objects[el].notExistingParents[id]);
398                 if(this.board.objects[el].visProp['visible'] && JXG.keys(this.board.objects[el].notExistingParents).length == 0) {
399                     if(this.board.objects[el].type != JXG.OBJECT_TYPE_INTERSECTION) {
400                         this.board.objects[el].showElement();
401                     }
402                 }            
403             }            
404         };
405     }
406 
407     this.id = this.board.setId(this, 'I');
408 
409     this.intersect1.addChild(this);
410     this.intersect2.addChild(this);
411 };
412 JXG.Intersection.prototype = new JXG.GeometryElement();
413    
414 /**
415  * Calls the renderer to update the drawing. This method is defined dynamically
416  * as it highly depends on the types of the intersected elements.
417  */
418 JXG.Intersection.prototype.update = function() {
419 };
420 
421 /**
422  * Checks whether (x,y) is near the point.
423  * @param {int} x Coordinate in x direction, screen coordinates.
424  * @param {int} y Coordinate in y direction, screen coordinates.
425  * @return {bool} Always returns false
426  */
427 JXG.Intersection.prototype.hasPoint = function(x, y) {
428 };
429 
430 /**
431  * Hides the element and his children. This is called from parents which became invisible or unreal
432  * and so this element isn't real anymore. The not existing parent is stored in the notExistingParents
433  * array.
434  * @param {String} id The identifier of the element causing this element to be hidden.
435  */
436 JXG.Intersection.prototype.hideChild = function(id) {
437 };
438 
439 /**
440  * Shows the element and his children. This is called from parents which became visible or real
441  * and so this element is now real. The formerly not existing parent is deleted from the
442  * notExistingParents array.
443  * @param {String} id The identifier of the element causing this element to be shown.
444  */
445 JXG.Intersection.prototype.showChild = function(id) {
446 };
447 
448 /**
449  * Remove intersection points from drawing.
450  */
451 JXG.Intersection.prototype.remove = function() {
452     if(JXG.exists(this.p))
453         this.board.removeObject(this.p);
454     if(JXG.exists(this.p1))
455         this.board.removeObject(this.p1);
456     if(JXG.exists(this.p2))
457         this.board.removeObject(this.p2);
458 };
459 
460 /**
461  * Dummy method 
462  */
463 JXG.Intersection.prototype.updateRenderer = function() {
464 };
465