1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Miscellaneous OGL support functions
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
24 #include "wx/deprecated/wxexpr.h"
37 #include "wx/ogl/ogl.h"
40 wxFont
* g_oglNormalFont
;
42 wxPen
* g_oglWhiteBackgroundPen
;
43 wxPen
* g_oglTransparentPen
;
44 wxBrush
* g_oglWhiteBackgroundBrush
;
45 wxPen
* g_oglBlackForegroundPen
;
46 wxCursor
* g_oglBullseyeCursor
= NULL
;
48 wxChar
* oglBuffer
= NULL
;
50 wxList
oglObjectCopyMapping(wxKEY_INTEGER
);
54 void wxOGLInitialize()
56 g_oglBullseyeCursor
= new wxCursor(wxCURSOR_BULLSEYE
);
58 g_oglNormalFont
= new wxFont(10, wxSWISS
, wxNORMAL
, wxNORMAL
);
60 g_oglBlackPen
= new wxPen(wxT("BLACK"), 1, wxSOLID
);
62 g_oglWhiteBackgroundPen
= new wxPen(wxT("WHITE"), 1, wxSOLID
);
63 g_oglTransparentPen
= new wxPen(wxT("WHITE"), 1, wxTRANSPARENT
);
64 g_oglWhiteBackgroundBrush
= new wxBrush(wxT("WHITE"), wxSOLID
);
65 g_oglBlackForegroundPen
= new wxPen(wxT("BLACK"), 1, wxSOLID
);
67 OGLInitializeConstraintTypes();
69 // Initialize big buffer used when writing images
70 oglBuffer
= new wxChar
[3000];
83 if (g_oglBullseyeCursor
)
85 delete g_oglBullseyeCursor
;
86 g_oglBullseyeCursor
= NULL
;
91 delete g_oglNormalFont
;
92 g_oglNormalFont
= NULL
;
99 if (g_oglWhiteBackgroundPen
)
101 delete g_oglWhiteBackgroundPen
;
102 g_oglWhiteBackgroundPen
= NULL
;
104 if (g_oglTransparentPen
)
106 delete g_oglTransparentPen
;
107 g_oglTransparentPen
= NULL
;
109 if (g_oglWhiteBackgroundBrush
)
111 delete g_oglWhiteBackgroundBrush
;
112 g_oglWhiteBackgroundBrush
= NULL
;
114 if (g_oglBlackForegroundPen
)
116 delete g_oglBlackForegroundPen
;
117 g_oglBlackForegroundPen
= NULL
;
120 OGLCleanUpConstraintTypes();
123 wxFont
*oglMatchFont(int point_size
)
125 wxFont
*font
= wxTheFontList
->FindOrCreateFont(point_size
, wxSWISS
, wxNORMAL
, wxNORMAL
);
139 font
= swiss_font_12
;
142 font
= swiss_font_14
;
145 font
= swiss_font_18
;
148 font
= swiss_font_24
;
152 font
= swiss_font_10
;
159 int FontSizeDialog(wxFrame
*parent
, int old_size
)
165 wxString ans
= wxGetTextFromUser(wxT("Enter point size"), wxT("Font size"), buf
, parent
);
166 if (ans
.Length() == 0)
170 ans
.ToLong(&new_size
);
171 if ((new_size
<= 0) || (new_size
> 40))
173 wxMessageBox(wxT("Invalid point size!"), wxT("Error"), wxOK
);
187 char *ans = wxGetSingleChoice("Choose", "Choose a font size", 8, strings, parent);
191 sscanf(ans, "%d", &size);
192 return oglMatchFont(size);
198 // Centre a list of strings in the given box. xOffset and yOffset are the
199 // the positions that these lines should be relative to, and this might be
200 // the same as m_xpos, m_ypos, but might be zero if formatting from left-justifying.
201 void oglCentreText(wxDC
& dc
, wxList
*text_list
,
202 double m_xpos
, double m_ypos
, double width
, double height
,
205 int n
= text_list
->GetCount();
207 if (!text_list
|| (n
== 0))
210 // First, get maximum dimensions of box enclosing text
212 long char_height
= 0;
214 long current_width
= 0;
216 // Store text extents for speed
217 double *widths
= new double[n
];
219 wxObjectList::compatibility_iterator current
= text_list
->GetFirst();
223 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->GetData();
224 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
225 widths
[i
] = current_width
;
227 if (current_width
> max_width
)
228 max_width
= current_width
;
229 current
= current
->GetNext();
233 double max_height
= n
*char_height
;
235 double xoffset
, yoffset
, xOffset
, yOffset
;
237 if (formatMode
& FORMAT_CENTRE_VERT
)
239 if (max_height
< height
)
240 yoffset
= (double)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
242 yoffset
= (double)(m_ypos
- (height
/2.0));
251 if (formatMode
& FORMAT_CENTRE_HORIZ
)
253 xoffset
= (double)(m_xpos
- width
/2.0);
262 current
= text_list
->GetFirst();
267 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->GetData();
270 if ((formatMode
& FORMAT_CENTRE_HORIZ
) && (widths
[i
] < width
))
271 x
= (double)((width
- widths
[i
])/2.0 + xoffset
);
274 double y
= (double)(i
*char_height
+ yoffset
);
276 line
->SetX( x
- xOffset
); line
->SetY( y
- yOffset
);
277 current
= current
->GetNext();
284 // Centre a list of strings in the given box
285 void oglCentreTextNoClipping(wxDC
& dc
, wxList
*text_list
,
286 double m_xpos
, double m_ypos
, double width
, double height
)
288 int n
= text_list
->GetCount();
290 if (!text_list
|| (n
== 0))
293 // First, get maximum dimensions of box enclosing text
295 long char_height
= 0;
297 long current_width
= 0;
299 // Store text extents for speed
300 double *widths
= new double[n
];
302 wxObjectList::compatibility_iterator current
= text_list
->GetFirst();
306 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->GetData();
307 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
308 widths
[i
] = current_width
;
310 if (current_width
> max_width
)
311 max_width
= current_width
;
312 current
= current
->GetNext();
316 double max_height
= n
*char_height
;
318 double yoffset
= (double)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
320 double xoffset
= (double)(m_xpos
- width
/2.0);
322 current
= text_list
->GetFirst();
327 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->GetData();
329 double x
= (double)((width
- widths
[i
])/2.0 + xoffset
);
330 double y
= (double)(i
*char_height
+ yoffset
);
332 line
->SetX( x
- m_xpos
); line
->SetY( y
- m_ypos
);
333 current
= current
->GetNext();
339 void oglGetCentredTextExtent(wxDC
& dc
, wxList
*text_list
,
340 double WXUNUSED(m_xpos
), double WXUNUSED(m_ypos
), double WXUNUSED(width
), double WXUNUSED(height
),
341 double *actual_width
, double *actual_height
)
343 int n
= text_list
->GetCount();
345 if (!text_list
|| (n
== 0))
352 // First, get maximum dimensions of box enclosing text
354 long char_height
= 0;
356 long current_width
= 0;
358 wxObjectList::compatibility_iterator current
= text_list
->GetFirst();
361 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->GetData();
362 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
364 if (current_width
> max_width
)
365 max_width
= current_width
;
366 current
= current
->GetNext();
369 *actual_height
= n
*char_height
;
370 *actual_width
= max_width
;
373 // Format a string to a list of strings that fit in the given box.
374 // Interpret %n and 10 or 13 as a new line.
375 wxStringList
*oglFormatText(wxDC
& dc
, const wxString
& text
, double width
, double WXUNUSED(height
), int formatMode
)
377 // First, parse the string into a list of words
378 wxStringList word_list
;
380 // Make new lines into NULL strings at this point
381 int i
= 0; int j
= 0; int len
= text
.Length();
382 wxChar word
[400]; word
[0] = 0;
383 bool end_word
= false; bool new_line
= false;
392 { word
[j
] = wxT('%'); j
++; }
395 if (text
[i
] == wxT('n'))
396 { new_line
= true; end_word
= true; i
++; }
398 { word
[j
] = wxT('%'); j
++; word
[j
] = text
[i
]; j
++; i
++; }
404 new_line
= true; end_word
= true; i
++;
409 new_line
= true; end_word
= true; i
++;
425 if (i
== len
) end_word
= true;
435 word_list
.Append(NULL
);
439 // Now, make a list of strings which can fit in the box
440 wxStringList
*string_list
= new wxStringList
;
443 wxStringList::compatibility_iterator node
= word_list
.GetFirst();
448 wxString
oldBuffer(buffer
);
450 wxString s
= node
->GetData();
454 if (buffer
.Length() > 0)
455 string_list
->Add(buffer
);
461 if (buffer
.Length() != 0)
465 dc
.GetTextExtent(buffer
, &x
, &y
);
467 // Don't fit within the bounding box if we're fitting shape to contents
468 if ((x
> width
) && !(formatMode
& FORMAT_SIZE_TO_CONTENTS
))
470 // Deal with first word being wider than box
471 if (oldBuffer
.Length() > 0)
472 string_list
->Add(oldBuffer
);
479 node
= node
->GetNext();
481 if (buffer
.Length() != 0)
482 string_list
->Add(buffer
);
487 void oglDrawFormattedText(wxDC
& dc
, wxList
*text_list
,
488 double m_xpos
, double m_ypos
, double width
, double height
,
491 double xoffset
, yoffset
;
492 if (formatMode
& FORMAT_CENTRE_HORIZ
)
495 xoffset
= (double)(m_xpos
- (width
/ 2.0));
497 if (formatMode
& FORMAT_CENTRE_VERT
)
500 yoffset
= (double)(m_ypos
- (height
/ 2.0));
502 dc
.SetClippingRegion(
503 (long)(m_xpos
- width
/2.0), (long)(m_ypos
- height
/2.0),
504 (long)width
+1, (long)height
+1); // +1 to allow for rounding errors
506 wxObjectList::compatibility_iterator current
= text_list
->GetFirst();
509 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->GetData();
511 dc
.DrawText(line
->GetText(), WXROUND(xoffset
+ line
->GetX()), WXROUND(yoffset
+ line
->GetY()));
512 current
= current
->GetNext();
515 dc
.DestroyClippingRegion();
519 * Find centroid given list of points comprising polyline
523 void oglFindPolylineCentroid(wxList
*points
, double *x
, double *y
)
528 wxObjectList::compatibility_iterator node
= points
->GetFirst();
531 wxRealPoint
*point
= (wxRealPoint
*)node
->GetData();
534 node
= node
->GetNext();
537 *x
= (xcount
/points
->GetCount());
538 *y
= (ycount
/points
->GetCount());
542 * Check that (x1, y1) -> (x2, y2) hits (x3, y3) -> (x4, y4).
543 * If so, ratio1 gives the proportion along the first line
544 * that the intersection occurs (or something like that).
545 * Used by functions below.
548 void oglCheckLineIntersection(double x1
, double y1
, double x2
, double y2
,
549 double x3
, double y3
, double x4
, double y4
,
550 double *ratio1
, double *ratio2
)
552 double denominator_term
= (y4
- y3
)*(x2
- x1
) - (y2
- y1
)*(x4
- x3
);
553 double numerator_term
= (x3
- x1
)*(y4
- y3
) + (x4
- x3
)*(y1
- y3
);
555 double line_constant
;
556 double length_ratio
= 1.0;
559 // Check for parallel lines
560 if ((denominator_term
< 0.005) && (denominator_term
> -0.005))
561 line_constant
= -1.0;
563 line_constant
= numerator_term
/denominator_term
;
565 // Check for intersection
566 if ((line_constant
< 1.0) && (line_constant
> 0.0))
568 // Now must check that other line hits
569 if (((y4
- y3
) < 0.005) && ((y4
- y3
) > -0.005))
570 k_line
= ((x1
- x3
) + line_constant
*(x2
- x1
))/(x4
- x3
);
572 k_line
= ((y1
- y3
) + line_constant
*(y2
- y1
))/(y4
- y3
);
574 if ((k_line
>= 0.0) && (k_line
< 1.0))
575 length_ratio
= line_constant
;
579 *ratio1
= length_ratio
;
584 * Find where (x1, y1) -> (x2, y2) hits one of the lines in xvec, yvec.
585 * (*x3, *y3) is the point where it hits.
588 void oglFindEndForPolyline(double n
, double xvec
[], double yvec
[],
589 double x1
, double y1
, double x2
, double y2
, double *x3
, double *y3
)
592 double lastx
= xvec
[0];
593 double lasty
= yvec
[0];
595 double min_ratio
= 1.0;
599 for (i
= 1; i
< n
; i
++)
601 oglCheckLineIntersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[i
], yvec
[i
],
602 &line_ratio
, &other_ratio
);
606 if (line_ratio
< min_ratio
)
607 min_ratio
= line_ratio
;
610 // Do last (implicit) line if last and first doubles are not identical
611 if (!(xvec
[0] == lastx
&& yvec
[0] == lasty
))
613 oglCheckLineIntersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[0], yvec
[0],
614 &line_ratio
, &other_ratio
);
616 if (line_ratio
< min_ratio
)
617 min_ratio
= line_ratio
;
620 *x3
= (x1
+ (x2
- x1
)*min_ratio
);
621 *y3
= (y1
+ (y2
- y1
)*min_ratio
);
626 * Find where the line hits the box.
630 void oglFindEndForBox(double width
, double height
,
631 double x1
, double y1
, // Centre of box (possibly)
632 double x2
, double y2
, // other end of line
633 double *x3
, double *y3
) // End on box edge
638 xvec
[0] = (double)(x1
- width
/2.0);
639 yvec
[0] = (double)(y1
- height
/2.0);
640 xvec
[1] = (double)(x1
- width
/2.0);
641 yvec
[1] = (double)(y1
+ height
/2.0);
642 xvec
[2] = (double)(x1
+ width
/2.0);
643 yvec
[2] = (double)(y1
+ height
/2.0);
644 xvec
[3] = (double)(x1
+ width
/2.0);
645 yvec
[3] = (double)(y1
- height
/2.0);
646 xvec
[4] = (double)(x1
- width
/2.0);
647 yvec
[4] = (double)(y1
- height
/2.0);
649 oglFindEndForPolyline(5, xvec
, yvec
, x2
, y2
, x1
, y1
, x3
, y3
);
653 * Find where the line hits the circle.
657 void oglFindEndForCircle(double radius
,
658 double x1
, double y1
, // Centre of circle
659 double x2
, double y2
, // Other end of line
660 double *x3
, double *y3
)
662 double H
= (double)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
671 *y3
= radius
* (y2
- y1
)/H
+ y1
;
672 *x3
= radius
* (x2
- x1
)/H
+ x1
;
677 * Given the line (x1, y1) -> (x2, y2), and an arrow size of given length and width,
678 * return the position of the tip of the arrow and the left and right vertices of the arrow.
682 void oglGetArrowPoints(double x1
, double y1
, double x2
, double y2
,
683 double length
, double width
,
684 double *tip_x
, double *tip_y
,
685 double *side1_x
, double *side1_y
,
686 double *side2_x
, double *side2_y
)
688 double l
= (double)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
693 double i_bar
= (x2
- x1
)/l
;
694 double j_bar
= (y2
- y1
)/l
;
696 double x3
= (- length
*i_bar
) + x2
;
697 double y3
= (- length
*j_bar
) + y2
;
699 *side1_x
= width
*(-j_bar
) + x3
;
700 *side1_y
= width
*i_bar
+ y3
;
702 *side2_x
= -width
*(-j_bar
) + x3
;
703 *side2_y
= -width
*i_bar
+ y3
;
705 *tip_x
= x2
; *tip_y
= y2
;
709 * Given an ellipse and endpoints of a line, returns the point at which
710 * the line touches the ellipse in values x4, y4.
711 * This function assumes that the centre of the ellipse is at x1, y1, and the
712 * ellipse has a width of width1 and a height of height1. It also assumes you are
713 * wanting to draw an arc FROM point x2, y2 TOWARDS point x3, y3.
714 * This function calculates the x,y coordinates of the intersection point of
715 * the arc with the ellipse.
716 * Author: Ian Harrison
719 void oglDrawArcToEllipse(double x1
, double y1
, double width1
, double height1
, double x2
, double y2
, double x3
, double y3
,
720 double *x4
, double *y4
)
722 double a1
= (double)(width1
/2.0);
723 double b1
= (double)(height1
/2.0);
725 // These are required to give top left x and y coordinates for DrawEllipse
726 // double top_left_x1 = (double)(x1 - a1);
727 // double top_left_y1 = (double)(y1 - b1);
729 // Check for vertical line
730 if (fabs(x2 - x3) < 0.05)
734 *y4 = (double)(y1 - b1);
736 *y4 = (double)(y1 + b1);
740 // Check that x2 != x3
741 if (fabs(x2
- x3
) < 0.05)
745 *y4
= (double)(y1
- sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
747 *y4
= (double)(y1
+ sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
751 // Calculate the x and y coordinates of the point where arc intersects ellipse
753 double A
, B
, C
, D
, E
, F
, G
, H
, K
;
754 double ellipse1_x
, ellipse1_y
;
756 A
= (double)(1/(a1
* a1
));
757 B
= (double)((y3
- y2
) * (y3
- y2
)) / ((x3
- x2
) * (x3
- x2
) * b1
* b1
);
758 C
= (double)(2 * (y3
- y2
) * (y2
- y1
)) / ((x3
- x2
) * b1
* b1
);
759 D
= (double)((y2
- y1
) * (y2
- y1
)) / (b1
* b1
);
761 F
= (double)(C
- (2 * A
* x1
) - (2 * B
* x2
));
762 G
= (double)((A
* x1
* x1
) + (B
* x2
* x2
) - (C
* x2
) + D
- 1);
763 H
= (double)((y3
- y2
) / (x3
- x2
));
764 K
= (double)((F
* F
) - (4 * E
* G
));
767 // In this case the line intersects the ellipse, so calculate intersection
771 ellipse1_x
= (double)(((F
* -1) + sqrt(K
)) / (2 * E
));
772 ellipse1_y
= (double)((H
* (ellipse1_x
- x2
)) + y2
);
776 ellipse1_x
= (double)(((F
* -1) - sqrt(K
)) / (2 * E
));
777 ellipse1_y
= (double)((H
* (ellipse1_x
- x2
)) + y2
);
781 // in this case, arc does not intersect ellipse, so just draw arc
790 // Draw a little circle (radius = 2) at the end of the arc where it hits
793 double circle_x = ellipse1_x - 2.0;
794 double circle_y = ellipse1_y - 2.0;
795 m_canvas->DrawEllipse(circle_x, circle_y, 4.0, 4.0);
799 // Update a list item from a list of strings
800 void UpdateListBox(wxListBox
*item
, wxList
*list
)
806 wxObjectList::compatibility_iterator node
= list
->GetFirst();
809 wxChar
*s
= (wxChar
*)node
->GetData();
811 node
= node
->GetNext();
815 bool oglRoughlyEqual(double val1
, double val2
, double tol
)
817 return ( (val1
< (val2
+ tol
)) && (val1
> (val2
- tol
)) &&
818 (val2
< (val1
+ tol
)) && (val2
> (val1
- tol
)));
822 * Hex<->Dec conversion
825 // Array used in DecToHex conversion routine.
826 static wxChar sg_HexArray
[] = { wxT('0'), wxT('1'), wxT('2'), wxT('3'),
827 wxT('4'), wxT('5'), wxT('6'), wxT('7'),
828 wxT('8'), wxT('9'), wxT('A'), wxT('B'),
829 wxT('C'), wxT('D'), wxT('E'), wxT('F')
832 // Convert 2-digit hex number to decimal
833 unsigned int oglHexToDec(wxChar
* buf
)
835 int firstDigit
, secondDigit
;
837 if (buf
[0] >= wxT('A'))
838 firstDigit
= buf
[0] - wxT('A') + 10;
840 firstDigit
= buf
[0] - wxT('0');
842 if (buf
[1] >= wxT('A'))
843 secondDigit
= buf
[1] - wxT('A') + 10;
845 secondDigit
= buf
[1] - wxT('0');
847 return firstDigit
* 16 + secondDigit
;
850 // Convert decimal integer to 2-character hex string
851 void oglDecToHex(unsigned int dec
, wxChar
*buf
)
853 int firstDigit
= (int)(dec
/16.0);
854 int secondDigit
= (int)(dec
- (firstDigit
*16.0));
855 buf
[0] = sg_HexArray
[firstDigit
];
856 buf
[1] = sg_HexArray
[secondDigit
];
860 // 3-digit hex to wxColour
861 wxColour
oglHexToColour(const wxString
& hex
)
863 if (hex
.Length() == 6)
867 hex
.Mid(0,2).ToLong(&r
, 16);
868 hex
.Mid(2,2).ToLong(&g
, 16);
869 hex
.Mid(4,2).ToLong(&b
, 16);
870 return wxColour((unsigned char)r
,
878 // RGB to 3-digit hex
879 wxString
oglColourToHex(const wxColour
& colour
)
882 unsigned int red
= colour
.Red();
883 unsigned int green
= colour
.Green();
884 unsigned int blue
= colour
.Blue();
886 oglDecToHex(red
, buf
);
887 oglDecToHex(green
, buf
+2);
888 oglDecToHex(blue
, buf
+4);
890 return wxString(buf
);