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