]> git.saurik.com Git - wxWidgets.git/blob - contrib/src/ogl/ogldiag.cpp
Assert correction.
[wxWidgets.git] / contrib / src / ogl / ogldiag.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: ogldiag.cpp
3 // Purpose: wxDiagram
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 12/07/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "ogldiag.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include <wx/wx.h>
25 #endif
26
27 #if wxUSE_PROLOGIO
28 #include <wx/deprecated/wxexpr.h>
29 #endif
30
31 #ifdef new
32 #undef new
33 #endif
34
35 #include <ctype.h>
36 #include <math.h>
37 #include <stdlib.h>
38
39 #include "wx/ogl/ogl.h"
40
41
42 IMPLEMENT_DYNAMIC_CLASS(wxDiagram, wxObject)
43
44 // Object canvas
45 wxDiagram::wxDiagram()
46 {
47 m_diagramCanvas = NULL;
48 m_quickEditMode = false;
49 m_snapToGrid = true;
50 m_gridSpacing = 5.0;
51 m_shapeList = new wxList;
52 m_mouseTolerance = DEFAULT_MOUSE_TOLERANCE;
53 }
54
55 wxDiagram::~wxDiagram()
56 {
57 if (m_shapeList)
58 delete m_shapeList;
59 }
60
61 void wxDiagram::SetSnapToGrid(bool snap)
62 {
63 m_snapToGrid = snap;
64 }
65
66 void wxDiagram::SetGridSpacing(double spacing)
67 {
68 m_gridSpacing = spacing;
69 }
70
71 void wxDiagram::Snap(double *x, double *y)
72 {
73 if (m_snapToGrid)
74 {
75 *x = m_gridSpacing * ((int)(*x/m_gridSpacing + 0.5));
76 *y = m_gridSpacing * ((int)(*y/m_gridSpacing + 0.5));
77 }
78 }
79
80
81 void wxDiagram::Redraw(wxDC& dc)
82 {
83 if (m_shapeList)
84 {
85 if (GetCanvas())
86 GetCanvas()->SetCursor(* wxHOURGLASS_CURSOR);
87 wxNode *current = m_shapeList->GetFirst();
88
89 while (current)
90 {
91 wxShape *object = (wxShape *)current->GetData();
92 if (!object->GetParent())
93 object->Draw(dc);
94
95 current = current->GetNext();
96 }
97 if (GetCanvas())
98 GetCanvas()->SetCursor(* wxSTANDARD_CURSOR);
99 }
100 }
101
102 void wxDiagram::Clear(wxDC& dc)
103 {
104 dc.Clear();
105 }
106
107 // Insert object after addAfter, or at end of list.
108 void wxDiagram::AddShape(wxShape *object, wxShape *addAfter)
109 {
110 wxNode *nodeAfter = NULL;
111 if (addAfter)
112 nodeAfter = m_shapeList->Member(addAfter);
113
114 if (!m_shapeList->Member(object))
115 {
116 if (nodeAfter)
117 {
118 if (nodeAfter->GetNext())
119 m_shapeList->Insert(nodeAfter->GetNext(), object);
120 else
121 m_shapeList->Append(object);
122 }
123 else
124 m_shapeList->Append(object);
125 object->SetCanvas(GetCanvas());
126 }
127 }
128
129 void wxDiagram::InsertShape(wxShape *object)
130 {
131 m_shapeList->Insert(object);
132 object->SetCanvas(GetCanvas());
133 }
134
135 void wxDiagram::RemoveShape(wxShape *object)
136 {
137 m_shapeList->DeleteObject(object);
138 }
139
140 // Should this delete the actual objects too? I think not.
141 void wxDiagram::RemoveAllShapes()
142 {
143 m_shapeList->Clear();
144 }
145
146 void wxDiagram::DeleteAllShapes()
147 {
148 wxNode *node = m_shapeList->GetFirst();
149 while (node)
150 {
151 wxShape *shape = (wxShape *)node->GetData();
152 if (!shape->GetParent())
153 {
154 RemoveShape(shape);
155 delete shape;
156 node = m_shapeList->GetFirst();
157 }
158 else
159 node = node->GetNext();
160 }
161 }
162
163 void wxDiagram::ShowAll(bool show)
164 {
165 wxNode *current = m_shapeList->GetFirst();
166
167 while (current)
168 {
169 wxShape *object = (wxShape *)current->GetData();
170 object->Show(show);
171
172 current = current->GetNext();
173 }
174 }
175
176 void wxDiagram::DrawOutline(wxDC& dc, double x1, double y1, double x2, double y2)
177 {
178 wxPen dottedPen(*wxBLACK, 1, wxDOT);
179 dc.SetPen(dottedPen);
180 dc.SetBrush((* wxTRANSPARENT_BRUSH));
181
182 wxPoint points[5];
183
184 points[0].x = (int) x1;
185 points[0].y = (int) y1;
186
187 points[1].x = (int) x2;
188 points[1].y = (int) y1;
189
190 points[2].x = (int) x2;
191 points[2].y = (int) y2;
192
193 points[3].x = (int) x1;
194 points[3].y = (int) y2;
195
196 points[4].x = (int) x1;
197 points[4].y = (int) y1;
198 dc.DrawLines(5, points);
199 }
200
201 // Make sure all text that should be centred, is centred.
202 void wxDiagram::RecentreAll(wxDC& dc)
203 {
204 wxNode *object_node = m_shapeList->GetFirst();
205 while (object_node)
206 {
207 wxShape *obj = (wxShape *)object_node->GetData();
208 obj->Recentre(dc);
209 object_node = object_node->GetNext();
210 }
211 }
212
213 // Input/output
214 #if wxUSE_PROLOGIO
215 bool wxDiagram::SaveFile(const wxString& filename)
216 {
217 wxBeginBusyCursor();
218
219 wxExprDatabase *database = new wxExprDatabase;
220
221 // First write the diagram type
222 wxExpr *header = new wxExpr(_T("diagram"));
223 OnHeaderSave(*database, *header);
224
225 database->Append(header);
226
227 wxNode *node = m_shapeList->GetFirst();
228 while (node)
229 {
230 wxShape *shape = (wxShape *)node->GetData();
231
232 if (!shape->IsKindOf(CLASSINFO(wxControlPoint)))
233 {
234 wxExpr *expr;
235 if (shape->IsKindOf(CLASSINFO(wxLineShape)))
236 expr = new wxExpr(_T("line"));
237 else
238 expr = new wxExpr(_T("shape"));
239
240 OnShapeSave(*database, *shape, *expr);
241 }
242 node = node->GetNext();
243 }
244 OnDatabaseSave(*database);
245
246 wxString tempFile;
247 wxGetTempFileName(wxT("diag"), tempFile);
248 FILE* file = fopen(tempFile.mb_str(wxConvFile), "w");
249 if (! file)
250 {
251 wxEndBusyCursor();
252 delete database;
253 return false;
254 }
255
256 database->Write(file);
257 fclose(file);
258 delete database;
259
260 /*
261 // Save backup
262 if (FileExists(filename))
263 {
264 char buf[400];
265 #ifdef __X__
266 sprintf(buf, "%s.bak", filename);
267 #endif
268 #ifdef __WXMSW__
269 sprintf(buf, "_diagram.bak");
270 #endif
271 if (FileExists(buf)) wxRemoveFile(buf);
272 if (!wxRenameFile(filename, buf))
273 {
274 wxCopyFile(filename, buf);
275 wxRemoveFile(filename);
276 }
277 }
278 */
279
280 // Copy the temporary file to the correct filename
281 if (!wxRenameFile(tempFile, filename))
282 {
283 wxCopyFile(tempFile, filename);
284 wxRemoveFile(tempFile);
285 }
286
287 wxEndBusyCursor();
288 return true;
289 }
290
291 bool wxDiagram::LoadFile(const wxString& filename)
292 {
293 wxBeginBusyCursor();
294
295 wxExprDatabase database(wxExprInteger, _T("id"));
296 if (!database.Read(filename))
297 {
298 wxEndBusyCursor();
299 return false;
300 }
301
302 DeleteAllShapes();
303
304 database.BeginFind();
305 wxExpr *header = database.FindClauseByFunctor(_T("diagram"));
306
307 if (header)
308 OnHeaderLoad(database, *header);
309
310 // Scan through all clauses and register the ids
311 wxNode *node = database.GetFirst();
312 while (node)
313 {
314 wxExpr *clause = (wxExpr *)node->GetData();
315 long id = -1;
316 clause->GetAttributeValue(_T("id"), id);
317 wxRegisterId(id);
318 node = node->GetNext();
319 }
320
321 ReadNodes(database);
322 ReadContainerGeometry(database);
323 ReadLines(database);
324
325 OnDatabaseLoad(database);
326
327 wxEndBusyCursor();
328
329 return true;
330 }
331
332 void wxDiagram::ReadNodes(wxExprDatabase& database)
333 {
334 // Find and create the node images
335 database.BeginFind();
336 wxExpr *clause = database.FindClauseByFunctor(_T("shape"));
337 while (clause)
338 {
339 wxChar *type = NULL;
340 long parentId = -1;
341
342 clause->AssignAttributeValue(wxT("type"), &type);
343 clause->AssignAttributeValue(wxT("parent"), &parentId);
344 wxClassInfo *classInfo = wxClassInfo::FindClass(type);
345 if (classInfo)
346 {
347 wxShape *shape = (wxShape *)classInfo->CreateObject();
348 OnShapeLoad(database, *shape, *clause);
349
350 shape->SetCanvas(GetCanvas());
351 shape->Show(true);
352
353 m_shapeList->Append(shape);
354
355 // If child of composite, link up
356 if (parentId > -1)
357 {
358 wxExpr *parentExpr = database.HashFind(_T("shape"), parentId);
359 if (parentExpr && parentExpr->GetClientData())
360 {
361 wxShape *parent = (wxShape *)parentExpr->GetClientData();
362 shape->SetParent(parent);
363 parent->GetChildren().Append(shape);
364 }
365 }
366
367 clause->SetClientData(shape);
368 }
369 if (type)
370 delete[] type;
371
372 clause = database.FindClauseByFunctor(_T("shape"));
373 }
374 return;
375 }
376
377 void wxDiagram::ReadLines(wxExprDatabase& database)
378 {
379 database.BeginFind();
380 wxExpr *clause = database.FindClauseByFunctor(_T("line"));
381 while (clause)
382 {
383 wxString type;
384 long parentId = -1;
385
386 clause->GetAttributeValue(_T("type"), type);
387 clause->GetAttributeValue(_T("parent"), parentId);
388 wxClassInfo *classInfo = wxClassInfo::FindClass(type);
389 if (classInfo)
390 {
391 wxLineShape *shape = (wxLineShape *)classInfo->CreateObject();
392 shape->Show(true);
393
394 OnShapeLoad(database, *shape, *clause);
395 shape->SetCanvas(GetCanvas());
396
397 long image_to = -1; long image_from = -1;
398 clause->GetAttributeValue(_T("to"), image_to);
399 clause->GetAttributeValue(_T("from"), image_from);
400
401 wxExpr *image_to_expr = database.HashFind(_T("shape"), image_to);
402
403 if (!image_to_expr)
404 {
405 // Error
406 }
407 wxExpr *image_from_expr = database.HashFind(_T("shape"), image_from);
408
409 if (!image_from_expr)
410 {
411 // Error
412 }
413
414 if (image_to_expr && image_from_expr)
415 {
416 wxShape *image_to_object = (wxShape *)image_to_expr->GetClientData();
417 wxShape *image_from_object = (wxShape *)image_from_expr->GetClientData();
418
419 if (image_to_object && image_from_object)
420 {
421 image_from_object->AddLine(shape, image_to_object, shape->GetAttachmentFrom(), shape->GetAttachmentTo());
422 }
423 }
424 clause->SetClientData(shape);
425
426 m_shapeList->Append(shape);
427 }
428
429 clause = database.FindClauseByFunctor(_T("line"));
430 }
431 }
432
433 // Containers have divisions that reference adjoining divisions,
434 // so we need a separate pass to link everything up.
435 // Also used by Symbol Library.
436 void wxDiagram::ReadContainerGeometry(wxExprDatabase& database)
437 {
438 database.BeginFind();
439 wxExpr *clause = database.FindClauseByFunctor(_T("shape"));
440 while (clause)
441 {
442 wxShape *image = (wxShape *)clause->GetClientData();
443 if (image && image->IsKindOf(CLASSINFO(wxCompositeShape)))
444 {
445 wxCompositeShape *composite = (wxCompositeShape *)image;
446 wxExpr *divisionExpr = NULL;
447
448 // Find the list of divisions in the composite
449 clause->GetAttributeValue(_T("divisions"), &divisionExpr);
450 if (divisionExpr)
451 {
452 int i = 0;
453 wxExpr *idExpr = divisionExpr->Nth(i);
454 while (idExpr)
455 {
456 long divisionId = idExpr->IntegerValue();
457 wxExpr *childExpr = database.HashFind(_T("shape"), divisionId);
458 if (childExpr && childExpr->GetClientData())
459 {
460 wxDivisionShape *child = (wxDivisionShape *)childExpr->GetClientData();
461 composite->GetDivisions().Append(child);
462
463 // Find the adjoining shapes
464 long leftSideId = -1;
465 long topSideId = -1;
466 long rightSideId = -1;
467 long bottomSideId = -1;
468 childExpr->GetAttributeValue(_T("left_side"), leftSideId);
469 childExpr->GetAttributeValue(_T("top_side"), topSideId);
470 childExpr->GetAttributeValue(_T("right_side"), rightSideId);
471 childExpr->GetAttributeValue(_T("bottom_side"), bottomSideId);
472 if (leftSideId > -1)
473 {
474 wxExpr *leftExpr = database.HashFind(_T("shape"), leftSideId);
475 if (leftExpr && leftExpr->GetClientData())
476 {
477 wxDivisionShape *leftSide = (wxDivisionShape *)leftExpr->GetClientData();
478 child->SetLeftSide(leftSide);
479 }
480 }
481 if (topSideId > -1)
482 {
483 wxExpr *topExpr = database.HashFind(_T("shape"), topSideId);
484 if (topExpr && topExpr->GetClientData())
485 {
486 wxDivisionShape *topSide = (wxDivisionShape *)topExpr->GetClientData();
487 child->SetTopSide(topSide);
488 }
489 }
490 if (rightSideId > -1)
491 {
492 wxExpr *rightExpr = database.HashFind(_T("shape"), rightSideId);
493 if (rightExpr && rightExpr->GetClientData())
494 {
495 wxDivisionShape *rightSide = (wxDivisionShape *)rightExpr->GetClientData();
496 child->SetRightSide(rightSide);
497 }
498 }
499 if (bottomSideId > -1)
500 {
501 wxExpr *bottomExpr = database.HashFind(_T("shape"), bottomSideId);
502 if (bottomExpr && bottomExpr->GetClientData())
503 {
504 wxDivisionShape *bottomSide = (wxDivisionShape *)bottomExpr->GetClientData();
505 child->SetBottomSide(bottomSide);
506 }
507 }
508 }
509 i ++;
510 idExpr = divisionExpr->Nth(i);
511 }
512 }
513 }
514
515 clause = database.FindClauseByFunctor(_T("shape"));
516 }
517 }
518
519 // Allow for modifying file
520 bool wxDiagram::OnDatabaseLoad(wxExprDatabase& WXUNUSED(db))
521 {
522 return true;
523 }
524
525 bool wxDiagram::OnDatabaseSave(wxExprDatabase& WXUNUSED(db))
526 {
527 return true;
528 }
529
530 bool wxDiagram::OnShapeSave(wxExprDatabase& db, wxShape& shape, wxExpr& expr)
531 {
532 shape.WriteAttributes(&expr);
533 db.Append(&expr);
534
535 if (shape.IsKindOf(CLASSINFO(wxCompositeShape)))
536 {
537 wxNode *node = shape.GetChildren().GetFirst();
538 while (node)
539 {
540 wxShape *childShape = (wxShape *)node->GetData();
541 wxExpr *childExpr = new wxExpr(_T("shape"));
542 OnShapeSave(db, *childShape, *childExpr);
543 node = node->GetNext();
544 }
545 }
546
547 return true;
548 }
549
550 bool wxDiagram::OnShapeLoad(wxExprDatabase& WXUNUSED(db), wxShape& shape, wxExpr& expr)
551 {
552 shape.ReadAttributes(&expr);
553 return true;
554 }
555
556 bool wxDiagram::OnHeaderSave(wxExprDatabase& WXUNUSED(db), wxExpr& WXUNUSED(expr))
557 {
558 return true;
559 }
560
561 bool wxDiagram::OnHeaderLoad(wxExprDatabase& WXUNUSED(db), wxExpr& WXUNUSED(expr))
562 {
563 return true;
564 }
565
566 #endif
567
568 void wxDiagram::SetCanvas(wxShapeCanvas *can)
569 {
570 m_diagramCanvas = can;
571 }
572
573 // Find a shape by its id
574 wxShape* wxDiagram::FindShape(long id) const
575 {
576 wxNode* node = GetShapeList()->GetFirst();
577 while (node)
578 {
579 wxShape* shape = (wxShape*) node->GetData();
580 if (shape->GetId() == id)
581 return shape;
582 node = node->GetNext();
583 }
584 return NULL;
585 }
586
587
588 //// Crossings classes
589
590 wxLineCrossings::wxLineCrossings()
591 {
592 }
593
594 wxLineCrossings::~wxLineCrossings()
595 {
596 ClearCrossings();
597 }
598
599 void wxLineCrossings::FindCrossings(wxDiagram& diagram)
600 {
601 ClearCrossings();
602 wxNode* node1 = diagram.GetShapeList()->GetFirst();
603 while (node1)
604 {
605 wxShape* shape1 = (wxShape*) node1->GetData();
606 if (shape1->IsKindOf(CLASSINFO(wxLineShape)))
607 {
608 wxLineShape* lineShape1 = (wxLineShape*) shape1;
609 // Iterate through the segments
610 wxList* pts1 = lineShape1->GetLineControlPoints();
611 size_t i;
612 for (i = 0; i < (pts1->GetCount() - 1); i++)
613 {
614 wxRealPoint* pt1_a = (wxRealPoint*) (pts1->Item(i)->GetData());
615 wxRealPoint* pt1_b = (wxRealPoint*) (pts1->Item(i+1)->GetData());
616
617 // Now we iterate through the segments again
618
619 wxNode* node2 = diagram.GetShapeList()->GetFirst();
620 while (node2)
621 {
622 wxShape* shape2 = (wxShape*) node2->GetData();
623
624 // Assume that the same line doesn't cross itself
625 if (shape2->IsKindOf(CLASSINFO(wxLineShape)) && (shape1 != shape2))
626 {
627 wxLineShape* lineShape2 = (wxLineShape*) shape2;
628 // Iterate through the segments
629 wxList* pts2 = lineShape2->GetLineControlPoints();
630 int j;
631 for (j = 0; j < (int) (pts2->GetCount() - 1); j++)
632 {
633 wxRealPoint* pt2_a = (wxRealPoint*) (pts2->Item(j)->GetData());
634 wxRealPoint* pt2_b = (wxRealPoint*) (pts2->Item(j+1)->GetData());
635
636 // Now let's see if these two segments cross.
637 double ratio1, ratio2;
638 oglCheckLineIntersection(pt1_a->x, pt1_a->y, pt1_b->x, pt1_b->y,
639 pt2_a->x, pt2_a->y, pt2_b->x, pt2_b->y,
640 & ratio1, & ratio2);
641
642 if ((ratio1 < 1.0) && (ratio1 > -1.0))
643 {
644 // Intersection!
645 wxLineCrossing* crossing = new wxLineCrossing;
646 crossing->m_intersect.x = (pt1_a->x + (pt1_b->x - pt1_a->x)*ratio1);
647 crossing->m_intersect.y = (pt1_a->y + (pt1_b->y - pt1_a->y)*ratio1);
648
649 crossing->m_pt1 = * pt1_a;
650 crossing->m_pt2 = * pt1_b;
651 crossing->m_pt3 = * pt2_a;
652 crossing->m_pt4 = * pt2_b;
653
654 crossing->m_lineShape1 = lineShape1;
655 crossing->m_lineShape2 = lineShape2;
656
657 m_crossings.Append(crossing);
658 }
659 }
660 }
661 node2 = node2->GetNext();
662 }
663 }
664 }
665
666 node1 = node1->GetNext();
667 }
668 }
669
670 void wxLineCrossings::DrawCrossings(wxDiagram& WXUNUSED(diagram), wxDC& dc)
671 {
672 dc.SetBrush(*wxTRANSPARENT_BRUSH);
673
674 long arcWidth = 8;
675
676 wxNode* node = m_crossings.GetFirst();
677 while (node)
678 {
679 wxLineCrossing* crossing = (wxLineCrossing*) node->GetData();
680 // dc.DrawEllipse((long) (crossing->m_intersect.x - (arcWidth/2.0) + 0.5), (long) (crossing->m_intersect.y - (arcWidth/2.0) + 0.5),
681 // arcWidth, arcWidth);
682
683
684 // Let's do some geometry to find the points on either end of the arc.
685 /*
686
687 (x1, y1)
688 |\
689 | \
690 | \
691 | \
692 | \
693 | |\ c c1
694 | a | \
695 | \
696 | - x <-- centre of arc
697 a1 | b |\
698 | | \ c2
699 | a2 | \
700 | - \
701 | b2 \
702 | \
703 |_______________\ (x2, y2)
704 b1
705
706 */
707
708 double a1 = wxMax(crossing->m_pt1.y, crossing->m_pt2.y) - wxMin(crossing->m_pt1.y, crossing->m_pt2.y) ;
709 double b1 = wxMax(crossing->m_pt1.x, crossing->m_pt2.x) - wxMin(crossing->m_pt1.x, crossing->m_pt2.x) ;
710 double c1 = sqrt( (a1*a1) + (b1*b1) );
711
712 double c = arcWidth / 2.0;
713 double a = c * a1/c1 ;
714 double b = c * b1/c1 ;
715
716 // I'm not sure this is right, since we don't know which direction we should be going in - need
717 // to know which way the line slopes and choose the sign appropriately.
718 double arcX1 = crossing->m_intersect.x - b;
719 double arcY1 = crossing->m_intersect.y - a;
720
721 double arcX2 = crossing->m_intersect.x + b;
722 double arcY2 = crossing->m_intersect.y + a;
723
724 dc.SetPen(*wxBLACK_PEN);
725 dc.DrawArc( (long) arcX1, (long) arcY1, (long) arcX2, (long) arcY2,
726 (long) crossing->m_intersect.x, (long) crossing->m_intersect.y);
727
728 dc.SetPen(*wxWHITE_PEN);
729 dc.DrawLine( (long) arcX1, (long) arcY1, (long) arcX2, (long) arcY2 );
730
731 node = node->GetNext();
732 }
733 }
734
735 void wxLineCrossings::ClearCrossings()
736 {
737 wxNode* node = m_crossings.GetFirst();
738 while (node)
739 {
740 wxLineCrossing* crossing = (wxLineCrossing*) node->GetData();
741 delete crossing;
742 node = node->GetNext();
743 }
744 m_crossings.Clear();
745 }
746