1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: Miscellaneous OGL support functions
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "misc.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include <wx/wxprec.h>
28 #include <wx/wxexpr.h>
48 wxFont
*g_oglNormalFont
;
51 wxPen
*white_background_pen
;
52 wxPen
*transparent_pen
;
53 wxBrush
*white_background_brush
;
54 wxPen
*black_foreground_pen
;
56 char *GraphicsBuffer
= NULL
;
57 wxCursor
*GraphicsBullseyeCursor
= NULL
;
59 wxList
wxObjectCopyMapping(wxKEY_INTEGER
);
61 void wxOGLInitialize()
63 GraphicsBullseyeCursor
= new wxCursor(wxCURSOR_BULLSEYE
);
65 g_oglNormalFont
= new wxFont(12, wxMODERN
, wxNORMAL
, wxNORMAL
);
67 black_pen
= new wxPen("BLACK", 1, wxSOLID
);
69 white_background_pen
= new wxPen("WHITE", 1, wxSOLID
);
70 transparent_pen
= new wxPen("WHITE", 1, wxTRANSPARENT
);
71 white_background_brush
= new wxBrush("WHITE", wxSOLID
);
72 black_foreground_pen
= new wxPen("BLACK", 1, wxSOLID
);
74 OGLInitializeConstraintTypes();
76 // Initialize big buffer used when writing images
77 GraphicsBuffer
= new char[3000];
79 if (!oglPopupDivisionMenu
)
81 oglPopupDivisionMenu
= new wxMenu("", (wxFunction
)oglGraphicsDivisionMenuProc
);
82 oglPopupDivisionMenu
->Append(DIVISION_MENU_SPLIT_HORIZONTALLY
, "Split horizontally");
83 oglPopupDivisionMenu
->Append(DIVISION_MENU_SPLIT_VERTICALLY
, "Split vertically");
84 oglPopupDivisionMenu
->AppendSeparator();
85 oglPopupDivisionMenu
->Append(DIVISION_MENU_EDIT_LEFT_EDGE
, "Edit left edge");
86 oglPopupDivisionMenu
->Append(DIVISION_MENU_EDIT_TOP_EDGE
, "Edit top edge");
94 delete[] GraphicsBuffer
;
95 GraphicsBuffer
= NULL
;
97 GraphicsBuffer
= NULL
;
98 if (oglPopupDivisionMenu
)
100 delete oglPopupDivisionMenu
;
101 oglPopupDivisionMenu
= NULL
;
105 wxFont
*MatchFont(int point_size
)
107 wxFont
*font
= wxTheFontList
->FindOrCreateFont(point_size
, wxSWISS
, wxNORMAL
, wxNORMAL
);
121 font
= swiss_font_12
;
124 font
= swiss_font_14
;
127 font
= swiss_font_18
;
130 font
= swiss_font_24
;
134 font
= swiss_font_10
;
141 int FontSizeDialog(wxFrame
*parent
, int old_size
)
146 sprintf(buf
, "%d", old_size
);
147 wxString ans
= wxGetTextFromUser("Enter point size", "Font size", buf
, parent
);
151 int new_size
= atoi(ans
);
152 if ((new_size
<= 0) || (new_size
> 40))
154 wxMessageBox("Invalid point size!", "Error", wxOK
);
168 char *ans = wxGetSingleChoice("Choose", "Choose a font size", 8, strings, parent);
172 sscanf(ans, "%d", &size);
173 return MatchFont(size);
179 // Centre a list of strings in the given box. xOffset and yOffset are the
180 // the positions that these lines should be relative to, and this might be
181 // the same as m_xpos, m_ypos, but might be zero if formatting from left-justifying.
182 void CentreText(wxDC
& dc
, wxList
*text_list
,
183 float m_xpos
, float m_ypos
, float width
, float height
,
186 int n
= text_list
->Number();
188 if (!text_list
|| (n
== 0))
191 // First, get maximum dimensions of box enclosing text
193 float char_height
= 0;
195 float current_width
= 0;
197 // Store text extents for speed
198 float *widths
= new float[n
];
200 wxNode
*current
= text_list
->First();
204 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
205 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
206 widths
[i
] = current_width
;
208 if (current_width
> max_width
)
209 max_width
= current_width
;
210 current
= current
->Next();
214 float max_height
= n
*char_height
;
216 float xoffset
, yoffset
, xOffset
, yOffset
;
218 if (formatMode
& FORMAT_CENTRE_VERT
)
220 if (max_height
< height
)
221 yoffset
= (float)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
223 yoffset
= (float)(m_ypos
- (height
/2.0));
232 if (formatMode
& FORMAT_CENTRE_HORIZ
)
234 xoffset
= (float)(m_xpos
- width
/2.0);
243 current
= text_list
->First();
248 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
251 if ((formatMode
& FORMAT_CENTRE_HORIZ
) && (widths
[i
] < width
))
252 x
= (float)((width
- widths
[i
])/2.0 + xoffset
);
255 float y
= (float)(i
*char_height
+ yoffset
);
257 line
->SetX( x
- xOffset
); line
->SetY( y
- yOffset
);
258 current
= current
->Next();
265 // Centre a list of strings in the given box
266 void CentreTextNoClipping(wxDC
& dc
, wxList
*text_list
,
267 float m_xpos
, float m_ypos
, float width
, float height
)
269 int n
= text_list
->Number();
271 if (!text_list
|| (n
== 0))
274 // First, get maximum dimensions of box enclosing text
276 float char_height
= 0;
278 float current_width
= 0;
280 // Store text extents for speed
281 float *widths
= new float[n
];
283 wxNode
*current
= text_list
->First();
287 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
288 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
289 widths
[i
] = current_width
;
291 if (current_width
> max_width
)
292 max_width
= current_width
;
293 current
= current
->Next();
297 float max_height
= n
*char_height
;
299 float yoffset
= (float)(m_ypos
- (height
/2.0) + (height
- max_height
)/2.0);
301 float xoffset
= (float)(m_xpos
- width
/2.0);
303 current
= text_list
->First();
308 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
310 float x
= (float)((width
- widths
[i
])/2.0 + xoffset
);
311 float y
= (float)(i
*char_height
+ yoffset
);
313 line
->SetX( x
- m_xpos
); line
->SetY( y
- m_ypos
);
314 current
= current
->Next();
320 void GetCentredTextExtent(wxDC
& dc
, wxList
*text_list
,
321 float m_xpos
, float m_ypos
, float width
, float height
,
322 float *actual_width
, float *actual_height
)
324 int n
= text_list
->Number();
326 if (!text_list
|| (n
== 0))
333 // First, get maximum dimensions of box enclosing text
335 float char_height
= 0;
337 float current_width
= 0;
339 wxNode
*current
= text_list
->First();
343 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
344 dc
.GetTextExtent(line
->GetText(), ¤t_width
, &char_height
);
346 if (current_width
> max_width
)
347 max_width
= current_width
;
348 current
= current
->Next();
352 *actual_height
= n
*char_height
;
353 *actual_width
= max_width
;
356 // Format a string to a list of strings that fit in the given box.
357 // Interpret %n and 10 or 13 as a new line.
358 wxList
*FormatText(wxDC
& dc
, const wxString
& text
, float width
, float height
, int formatMode
)
360 // First, parse the string into a list of words
363 // Make new lines into NULL strings at this point
364 int i
= 0; int j
= 0; int len
= strlen(text
);
365 char word
[200]; word
[0] = 0;
366 bool end_word
= FALSE
; bool new_line
= FALSE
;
375 { word
[j
] = '%'; j
++; }
379 { new_line
= TRUE
; end_word
= TRUE
; i
++; }
381 { word
[j
] = '%'; j
++; word
[j
] = text
[i
]; j
++; i
++; }
387 new_line
= TRUE
; end_word
= TRUE
; i
++;
392 new_line
= TRUE
; end_word
= TRUE
; i
++;
407 if (i
== len
) end_word
= TRUE
;
412 word_list
.Append((wxObject
*)copystring(word
));
417 word_list
.Append((wxObject
*)NULL
);
421 // Now, make a list of strings which can fit in the box
422 wxList
*string_list
= new wxList
;
426 wxNode
*node
= word_list
.First();
431 char *keep_string
= copystring(buffer
);
433 char *s
= (char *)node
->Data();
437 if (strlen(keep_string
) > 0)
438 string_list
->Append((wxObject
*)keep_string
);
440 delete[] keep_string
;
450 dc
.GetTextExtent(buffer
, &x
, &y
);
452 // Don't fit within the bounding box if we're fitting shape to contents
453 if ((x
> width
) && !(formatMode
& FORMAT_SIZE_TO_CONTENTS
))
455 // Deal with first word being wider than box
456 if (strlen(keep_string
) > 0)
457 string_list
->Append((wxObject
*)keep_string
);
459 delete[] keep_string
;
466 delete[] keep_string
;
472 string_list
->Append((wxObject
*)copystring(buffer
));
477 void DrawFormattedText(wxDC
& dc
, wxList
*text_list
,
478 float m_xpos
, float m_ypos
, float width
, float height
,
481 float xoffset
, yoffset
;
482 if (formatMode
& FORMAT_CENTRE_HORIZ
)
485 xoffset
= (float)(m_xpos
- (width
/ 2.0));
487 if (formatMode
& FORMAT_CENTRE_VERT
)
490 yoffset
= (float)(m_ypos
- (height
/ 2.0));
492 dc
.SetClippingRegion(
493 (float)(m_xpos
- width
/2.0), (float)(m_ypos
- height
/2.0),
494 (float)width
, (float)height
);
496 wxNode
*current
= text_list
->First();
499 wxShapeTextLine
*line
= (wxShapeTextLine
*)current
->Data();
501 dc
.DrawText(line
->GetText(), xoffset
+ line
->GetX(), yoffset
+ line
->GetY());
502 current
= current
->Next();
505 dc
.DestroyClippingRegion();
509 * Find centroid given list of points comprising polyline
513 void find_polyline_centroid(wxList
*points
, float *x
, float *y
)
518 wxNode
*node
= points
->First();
521 wxRealPoint
*point
= (wxRealPoint
*)node
->Data();
527 *x
= (xcount
/points
->Number());
528 *y
= (ycount
/points
->Number());
532 * Check that (x1, y1) -> (x2, y2) hits (x3, y3) -> (x4, y4).
533 * If so, ratio1 gives the proportion along the first line
534 * that the intersection occurs (or something like that).
535 * Used by functions below.
538 void check_line_intersection(float x1
, float y1
, float x2
, float y2
,
539 float x3
, float y3
, float x4
, float y4
,
540 float *ratio1
, float *ratio2
)
542 float denominator_term
= (y4
- y3
)*(x2
- x1
) - (y2
- y1
)*(x4
- x3
);
543 float numerator_term
= (x3
- x1
)*(y4
- y3
) + (x4
- x3
)*(y1
- y3
);
546 float length_ratio
= 1.0;
549 // Check for parallel lines
550 if ((denominator_term
< 0.005) && (denominator_term
> -0.005))
551 line_constant
= -1.0;
553 line_constant
= numerator_term
/denominator_term
;
555 // Check for intersection
556 if ((line_constant
< 1.0) && (line_constant
> 0.0))
558 // Now must check that other line hits
559 if (((y4
- y3
) < 0.005) && ((y4
- y3
) > -0.005))
560 k_line
= ((x1
- x3
) + line_constant
*(x2
- x1
))/(x4
- x3
);
562 k_line
= ((y1
- y3
) + line_constant
*(y2
- y1
))/(y4
- y3
);
564 if ((k_line
>= 0.0) && (k_line
< 1.0))
565 length_ratio
= line_constant
;
569 *ratio1
= length_ratio
;
574 * Find where (x1, y1) -> (x2, y2) hits one of the lines in xvec, yvec.
575 * (*x3, *y3) is the point where it hits.
578 void find_end_for_polyline(float n
, float xvec
[], float yvec
[],
579 float x1
, float y1
, float x2
, float y2
, float *x3
, float *y3
)
582 float lastx
= xvec
[0];
583 float lasty
= yvec
[0];
585 float min_ratio
= 1.0;
589 for (i
= 1; i
< n
; i
++)
591 check_line_intersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[i
], yvec
[i
],
592 &line_ratio
, &other_ratio
);
596 if (line_ratio
< min_ratio
)
597 min_ratio
= line_ratio
;
600 // Do last (implicit) line if last and first floats are not identical
601 if (!(xvec
[0] == lastx
&& yvec
[0] == lasty
))
603 check_line_intersection(x1
, y1
, x2
, y2
, lastx
, lasty
, xvec
[0], yvec
[0],
604 &line_ratio
, &other_ratio
);
606 if (line_ratio
< min_ratio
)
607 min_ratio
= line_ratio
;
610 *x3
= (x1
+ (x2
- x1
)*min_ratio
);
611 *y3
= (y1
+ (y2
- y1
)*min_ratio
);
616 * Find where the line hits the box.
620 void find_end_for_box(float width
, float height
,
621 float x1
, float y1
, // Centre of box (possibly)
622 float x2
, float y2
, // other end of line
623 float *x3
, float *y3
) // End on box edge
628 xvec
[0] = (float)(x1
- width
/2.0);
629 yvec
[0] = (float)(y1
- height
/2.0);
630 xvec
[1] = (float)(x1
- width
/2.0);
631 yvec
[1] = (float)(y1
+ height
/2.0);
632 xvec
[2] = (float)(x1
+ width
/2.0);
633 yvec
[2] = (float)(y1
+ height
/2.0);
634 xvec
[3] = (float)(x1
+ width
/2.0);
635 yvec
[3] = (float)(y1
- height
/2.0);
636 xvec
[4] = (float)(x1
- width
/2.0);
637 yvec
[4] = (float)(y1
- height
/2.0);
639 find_end_for_polyline(5, xvec
, yvec
, x2
, y2
, x1
, y1
, x3
, y3
);
643 * Find where the line hits the circle.
647 void find_end_for_circle(float radius
,
648 float x1
, float y1
, // Centre of circle
649 float x2
, float y2
, // Other end of line
650 float *x3
, float *y3
)
652 float H
= (float)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
661 *y3
= radius
* (y2
- y1
)/H
+ y1
;
662 *x3
= radius
* (x2
- x1
)/H
+ x1
;
667 * Given the line (x1, y1) -> (x2, y2), and an arrow size of given length and width,
668 * return the position of the tip of the arrow and the left and right vertices of the arrow.
672 void get_arrow_points(float x1
, float y1
, float x2
, float y2
,
673 float length
, float width
,
674 float *tip_x
, float *tip_y
,
675 float *side1_x
, float *side1_y
,
676 float *side2_x
, float *side2_y
)
678 float l
= (float)sqrt((x2
- x1
)*(x2
- x1
) + (y2
- y1
)*(y2
- y1
));
683 float i_bar
= (x2
- x1
)/l
;
684 float j_bar
= (y2
- y1
)/l
;
686 float x3
= (- length
*i_bar
) + x2
;
687 float y3
= (- length
*j_bar
) + y2
;
689 *side1_x
= width
*(-j_bar
) + x3
;
690 *side1_y
= width
*i_bar
+ y3
;
692 *side2_x
= -width
*(-j_bar
) + x3
;
693 *side2_y
= -width
*i_bar
+ y3
;
695 *tip_x
= x2
; *tip_y
= y2
;
699 * Given an ellipse and endpoints of a line, returns the point at which
700 * the line touches the ellipse in values x4, y4.
701 * This function assumes that the centre of the ellipse is at x1, y1, and the
702 * ellipse has a width of width1 and a height of height1. It also assumes you are
703 * wanting to draw an arc FROM point x2, y2 TOWARDS point x3, y3.
704 * This function calculates the x,y coordinates of the intersection point of
705 * the arc with the ellipse.
706 * Author: Ian Harrison
709 void draw_arc_to_ellipse(float x1
, float y1
, float width1
, float height1
, float x2
, float y2
, float x3
, float y3
,
710 float *x4
, float *y4
)
712 float a1
= (float)(width1
/2.0);
713 float b1
= (float)(height1
/2.0);
715 // These are required to give top left x and y coordinates for DrawEllipse
716 // float top_left_x1 = (float)(x1 - a1);
717 // float top_left_y1 = (float)(y1 - b1);
719 // Check for vertical line
720 if (fabs(x2 - x3) < 0.05)
724 *y4 = (float)(y1 - b1);
726 *y4 = (float)(y1 + b1);
730 // Check that x2 != x3
731 if (fabs(x2
- x3
) < 0.05)
735 *y4
= (float)(y1
- sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
737 *y4
= (float)(y1
+ sqrt((b1
*b1
- (((x2
-x1
)*(x2
-x1
))*(b1
*b1
)/(a1
*a1
)))));
741 // Calculate the x and y coordinates of the point where arc intersects ellipse
743 float A
, B
, C
, D
, E
, F
, G
, H
, K
;
744 float ellipse1_x
, ellipse1_y
;
746 A
= (float)(1/(a1
* a1
));
747 B
= (float)((y3
- y2
) * (y3
- y2
)) / ((x3
- x2
) * (x3
- x2
) * b1
* b1
);
748 C
= (float)(2 * (y3
- y2
) * (y2
- y1
)) / ((x3
- x2
) * b1
* b1
);
749 D
= (float)((y2
- y1
) * (y2
- y1
)) / (b1
* b1
);
751 F
= (float)(C
- (2 * A
* x1
) - (2 * B
* x2
));
752 G
= (float)((A
* x1
* x1
) + (B
* x2
* x2
) - (C
* x2
) + D
- 1);
753 H
= (float)((y3
- y2
) / (x3
- x2
));
754 K
= (float)((F
* F
) - (4 * E
* G
));
757 // In this case the line intersects the ellipse, so calculate intersection
761 ellipse1_x
= (float)(((F
* -1) + sqrt(K
)) / (2 * E
));
762 ellipse1_y
= (float)((H
* (ellipse1_x
- x2
)) + y2
);
766 ellipse1_x
= (float)(((F
* -1) - sqrt(K
)) / (2 * E
));
767 ellipse1_y
= (float)((H
* (ellipse1_x
- x2
)) + y2
);
771 // in this case, arc does not intersect ellipse, so just draw arc
780 // Draw a little circle (radius = 2) at the end of the arc where it hits
783 float circle_x = ellipse1_x - 2.0;
784 float circle_y = ellipse1_y - 2.0;
785 m_canvas->DrawEllipse(circle_x, circle_y, 4.0, 4.0);
789 // Update a list item from a list of strings
790 void UpdateListBox(wxListBox
*item
, wxList
*list
)
796 wxNode
*node
= list
->First();
799 char *s
= (char *)node
->Data();