OpenCPN Partial API docs
Loading...
Searching...
No Matches
wind.cpp
1/******************************************************************************
2 * $Id: wind.cpp, v1.0 2010/08/05 SethDart Exp $
3 *
4 * Project: OpenCPN
5 * Purpose: Dashboard Plugin
6 * Author: Jean-Eudes Onfray
7 *
8 ***************************************************************************
9 * Copyright (C) 2010 by David S. Register *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
25 ***************************************************************************
26 */
27
28#include <wx/wxprec.h>
29
30#ifndef WX_PRECOMP
31#include <wx/wx.h>
32#endif // precompiled headers
33
34#include "wind.h"
35
36#ifdef __BORLANDC__
37#pragma hdrstop
38#endif
39#include <cmath>
40
41#include "wx/tokenzr.h"
42
43// Display the arrow for MainValue (wind angle)
44// We also want the extra value (wind speed) displayed inside the dial
45
46DashboardInstrument_Wind::DashboardInstrument_Wind(
47 wxWindow* parent, wxWindowID id, wxString title,
48 InstrumentProperties* Properties, DASH_CAP cap_flag)
49 : DashboardInstrument_Dial(parent, id, title, Properties, cap_flag, 0, 360,
50 0, 360) {
51 SetOptionMarker(10, DIAL_MARKER_REDGREENBAR, 3);
52 // Labels are set static because we've no logic to display them this way
53 wxString labels[] = {_T(""), _T("30"), _T("60"), _T("90"),
54 _T("120"), _T("150"), _T(""), _T("150"),
55 _T("120"), _T("90"), _T("60"), _T("30")};
56 SetOptionLabel(30, DIAL_LABEL_HORIZONTAL, wxArrayString(12, labels));
57}
58
59void DashboardInstrument_Wind::DrawBackground(wxGCDC* dc) {
60 DrawBoat(dc, m_cx, m_cy, m_radius);
61}
62
63DashboardInstrument_WindCompass::DashboardInstrument_WindCompass(
64 wxWindow* parent, wxWindowID id, wxString title,
65 InstrumentProperties* Properties, DASH_CAP cap_flag)
66 : DashboardInstrument_Dial(parent, id, title, Properties, cap_flag, 0, 360,
67 0, 360) {
68 SetOptionMarker(5, DIAL_MARKER_SIMPLE, 2);
69 wxString labels[] = {_("N"), _("NE"), _("E"), _("SE"),
70 _("S"), _("SW"), _("W"), _("NW")};
71 SetOptionLabel(45, DIAL_LABEL_HORIZONTAL, wxArrayString(8, labels));
72}
73
74void DashboardInstrument_WindCompass::DrawBackground(wxGCDC* dc) {
75 DrawCompassRose(dc, m_cx, m_cy, m_radius * 0.85, m_AngleStart, false,
76 m_Properties);
77}
78
79// Display the arrow for MainValue (wind angle)
80// We also want the extra value (wind speed) displayed inside the dial
81
82DashboardInstrument_TrueWindAngle::DashboardInstrument_TrueWindAngle(
83 wxWindow* parent, wxWindowID id, wxString title,
84 InstrumentProperties* Properties, DASH_CAP cap_flag)
85 : DashboardInstrument_Dial(parent, id, title, Properties, cap_flag, 0, 360,
86 0, 360) {
87 SetOptionMarker(10, DIAL_MARKER_REDGREENBAR, 3);
88 // Labels are set static because we've no logic to display them this way
89 wxString labels[] = {_T(""), _T("30"), _T("60"), _T("90"),
90 _T("120"), _T("150"), _T(""), _T("150"),
91 _T("120"), _T("90"), _T("60"), _T("30")};
92 SetOptionLabel(30, DIAL_LABEL_HORIZONTAL, wxArrayString(12, labels));
93}
94
95void DashboardInstrument_TrueWindAngle::DrawBackground(wxGCDC* dc) {
96 DrawBoat(dc, m_cx, m_cy, m_radius);
97}
98
99/*****************************************************************************
100 Apparent & True wind angle combined in one dial instrument
101 Author: Thomas Rauch
102******************************************************************************/
103DashboardInstrument_AppTrueWindAngle::DashboardInstrument_AppTrueWindAngle(
104 wxWindow* parent, wxWindowID id, wxString title,
105 InstrumentProperties* Properties, DASH_CAP cap_flag)
106 : DashboardInstrument_Dial(parent, id, title, Properties, cap_flag, 0, 360,
107 0, 360) {
108 SetOptionMarker(10, DIAL_MARKER_REDGREENBAR, 3);
109 // Labels are set static because we've no logic to display them this way
110 wxString labels[] = {_T(""), _T("30"), _T("60"), _T("90"),
111 _T("120"), _T("150"), _T(""), _T("150"),
112 _T("120"), _T("90"), _T("60"), _T("30")};
113 SetOptionLabel(30, DIAL_LABEL_HORIZONTAL, wxArrayString(12, labels));
114}
115
116void DashboardInstrument_AppTrueWindAngle::DrawBackground(wxGCDC* dc) {
117 DrawBoat(dc, m_cx, m_cy, m_radius);
118}
119
120void DashboardInstrument_AppTrueWindAngle::SetData(DASH_CAP st, double data,
121 wxString unit) {
122 if (st == OCPN_DBP_STC_TWA) {
123 m_MainValueTrue = data;
124 m_MainValueTrueUnit = unit;
125 m_MainValueOption2 = DIAL_POSITION_BOTTOMLEFT;
126 } else if (st == OCPN_DBP_STC_AWA) {
127 m_MainValueApp = data;
128 m_MainValueAppUnit = unit;
129 m_MainValueOption1 = DIAL_POSITION_TOPLEFT;
130 } else if (st == OCPN_DBP_STC_AWS) {
131 m_ExtraValueApp = data;
132 m_ExtraValueAppUnit = unit;
133 m_ExtraValueOption1 = DIAL_POSITION_TOPRIGHT;
134 } else if (st == OCPN_DBP_STC_TWS) {
135 m_ExtraValueTrue = data;
136 m_ExtraValueTrueUnit = unit;
137 m_ExtraValueOption2 = DIAL_POSITION_BOTTOMRIGHT;
138 }
139 Refresh();
140}
141void DashboardInstrument_AppTrueWindAngle::Draw(wxGCDC* bdc) {
142 if (m_Properties) {
143 wxBrush b1(
144 GetColourSchemeBackgroundColour(m_Properties->m_DataBackgroundColour));
145 bdc->SetBackground(
146 GetColourSchemeBackgroundColour(m_Properties->m_DataBackgroundColour));
147 } else {
148 wxColour c1;
149 GetGlobalColor(_T("DASHB"), &c1);
150 wxBrush b1(c1);
151 bdc->SetBackground(b1);
152 }
153 bdc->Clear();
154
155 wxSize size = GetClientSize();
156 int width, height;
157 wxFont f;
158 if (m_Properties)
159 f = m_Properties->m_LabelFont.GetChosenFont();
160 else
161 f = g_pFontLabel->GetChosenFont();
162 bdc->GetTextExtent(_T("000"), &width, &height, 0, 0, &f);
163 m_cx = size.x / 2;
164 int availableHeight = GetDataBottom(size.y) - m_DataTop;
165 InitTitleAndDataPosition(availableHeight);
166 availableHeight -= height;
167 m_cy = m_DataTop + height / 2;
168 m_cy += availableHeight / 2;
169 m_radius = availableHeight / 2.0 * 0.95;
170
171 DrawLabels(bdc);
172 DrawFrame(bdc);
173 DrawMarkers(bdc);
174 DrawBackground(bdc);
175 DrawData(bdc, m_MainValueApp, m_MainValueAppUnit, m_MainValueFormat,
176 m_MainValueOption1);
177 DrawData(bdc, m_MainValueTrue, m_MainValueTrueUnit, m_MainValueFormat,
178 m_MainValueOption2);
179 DrawData(bdc, m_ExtraValueApp, m_ExtraValueAppUnit, m_ExtraValueFormat,
180 m_ExtraValueOption1);
181 DrawData(bdc, m_ExtraValueTrue, m_ExtraValueTrueUnit, m_ExtraValueFormat,
182 m_ExtraValueOption2);
183 DrawForeground(bdc);
184}
185void DashboardInstrument_AppTrueWindAngle::DrawForeground(wxGCDC* dc) {
186 wxPoint points[4];
187 double data;
188 double val;
189 double value;
190 // The default foreground is the arrow used in most dials
191 wxColour cl;
192 GetGlobalColor(_T("DASH2"), &cl);
193 wxPen pen1;
194 pen1.SetStyle(wxPENSTYLE_SOLID);
195 pen1.SetColour(cl);
196 pen1.SetWidth(2);
197 dc->SetPen(pen1);
198 GetGlobalColor(_T("DASH1"), &cl);
199 wxBrush brush1;
200 brush1.SetStyle(wxBRUSHSTYLE_SOLID);
201 brush1.SetColour(cl);
202 dc->SetBrush(brush1);
203 dc->DrawCircle(m_cx, m_cy, m_radius / 8);
204
205 /*True Wind*/
206 dc->SetPen(*wxTRANSPARENT_PEN);
207 if (m_Properties)
208 cl = GetColourSchemeFont(m_Properties->m_Arrow_Second_Colour);
209 else
210 GetGlobalColor(_T("BLUE3"), &cl);
211 wxBrush brush2;
212 brush2.SetStyle(wxBRUSHSTYLE_SOLID);
213 brush2.SetColour(cl);
214 dc->SetBrush(brush2);
215
216 /* this is fix for a +/-180? round instrument, when m_MainValue is supplied as
217 * <0..180><L | R> for example TWA & AWA */
218 if (m_MainValueTrueUnit == _T("\u00B0L"))
219 data = 360 - m_MainValueTrue;
220 else
221 data = m_MainValueTrue;
222
223 // The arrow should stay inside fixed limits
224 if (data < m_MainValueMin)
225 val = m_MainValueMin;
226 else if (data > m_MainValueMax)
227 val = m_MainValueMax;
228 else
229 val = data;
230
231 value = deg2rad((val - m_MainValueMin) * m_AngleRange /
232 (m_MainValueMax - m_MainValueMin)) +
233 deg2rad(m_AngleStart - ANGLE_OFFSET);
234
235 points[0].x = m_cx + (m_radius * 0.95 * cos(value - .010));
236 points[0].y = m_cy + (m_radius * 0.95 * sin(value - .010));
237 points[1].x = m_cx + (m_radius * 0.95 * cos(value + .015));
238 points[1].y = m_cy + (m_radius * 0.95 * sin(value + .015));
239 points[2].x = m_cx + (m_radius * 0.22 * cos(value + 2.8));
240 points[2].y = m_cy + (m_radius * 0.22 * sin(value + 2.8));
241 points[3].x = m_cx + (m_radius * 0.22 * cos(value - 2.8));
242 points[3].y = m_cy + (m_radius * 0.22 * sin(value - 2.8));
243 dc->DrawPolygon(4, points, 0, 0);
244
245 /* Apparent Wind*/
246 dc->SetPen(*wxTRANSPARENT_PEN);
247 if (m_Properties)
248 cl = GetColourSchemeFont(m_Properties->m_Arrow_First_Colour);
249 else
250 GetGlobalColor(_T("DASHN"), &cl);
251 wxBrush brush;
252 brush.SetStyle(wxBRUSHSTYLE_SOLID);
253 brush.SetColour(cl);
254 dc->SetBrush(brush);
255
256 /* this is fix for a +/-180? round instrument, when m_MainValue is supplied as
257 * <0..180><L | R> for example TWA & AWA */
258 if (m_MainValueAppUnit == _T("\u00B0L"))
259 data = 360 - m_MainValueApp;
260 else
261 data = m_MainValueApp;
262
263 // The arrow should stay inside fixed limits
264 if (data < m_MainValueMin)
265 val = m_MainValueMin;
266 else if (data > m_MainValueMax)
267 val = m_MainValueMax;
268 else
269 val = data;
270
271 value = deg2rad((val - m_MainValueMin) * m_AngleRange /
272 (m_MainValueMax - m_MainValueMin)) +
273 deg2rad(m_AngleStart - ANGLE_OFFSET);
274
275 points[0].x = m_cx + (m_radius * 0.95 * cos(value - .010));
276 points[0].y = m_cy + (m_radius * 0.95 * sin(value - .010));
277 points[1].x = m_cx + (m_radius * 0.95 * cos(value + .015));
278 points[1].y = m_cy + (m_radius * 0.95 * sin(value + .015));
279 points[2].x = m_cx + (m_radius * 0.22 * cos(value + 2.8));
280 points[2].y = m_cy + (m_radius * 0.22 * sin(value + 2.8));
281 points[3].x = m_cx + (m_radius * 0.22 * cos(value - 2.8));
282 points[3].y = m_cy + (m_radius * 0.22 * sin(value - 2.8));
283 dc->DrawPolygon(4, points, 0, 0);
284}
285void DashboardInstrument_AppTrueWindAngle::DrawData(
286 wxGCDC* dc, double value, wxString unit, wxString format,
287 DialPositionOption position) {
288 if (position == DIAL_POSITION_NONE) return;
289
290 wxColour cl;
291 if (m_Properties) {
292 dc->SetFont(m_Properties->m_LabelFont.GetChosenFont());
293 cl = GetColourSchemeFont(m_Properties->m_LabelFont.GetColour());
294 } else {
295 dc->SetFont(g_pFontLabel->GetChosenFont());
296 cl = GetColourSchemeFont(g_pFontLabel->GetColour());
297 }
298 // GetGlobalColor(_T("DASHF"), &cl);
299 dc->SetTextForeground(cl);
300
301 wxSize size = GetClientSize();
302
303 wxString text;
304 if (!std::isnan(value)) {
305 if (unit == _T("\u00B0"))
306 text = wxString::Format(format, value) + DEGREE_SIGN;
307 else if (unit == _T("\u00B0L")) // No special display for now, might be
308 // XX?< (as in text-only instrument)
309 text = wxString::Format(format, value) + DEGREE_SIGN;
310 else if (unit ==
311 _T("\u00B0R")) // No special display for now, might be >XX?
312 text = wxString::Format(format, value) + DEGREE_SIGN;
313 else if (unit == _T("\u00B0T"))
314 text = wxString::Format(format, value) + DEGREE_SIGN + _T("T");
315 else if (unit == _T("\u00B0M"))
316 text = wxString::Format(format, value) + DEGREE_SIGN + _T("M");
317 else if (unit == _T("N")) // Knots
318 text = wxString::Format(format, value) + _T(" Kts");
319 else
320 text = wxString::Format(format, value) + _T(" ") + unit;
321 } else
322 text = _T("---");
323
324 int width, height;
325 wxFont f;
326 if (m_Properties)
327 f = m_Properties->m_LabelFont.GetChosenFont();
328 else
329 f = g_pFontLabel->GetChosenFont();
330 dc->GetMultiLineTextExtent(text, &width, &height, NULL, &f);
331
332 wxRect TextPoint;
333 TextPoint.width = width;
334 TextPoint.height = height;
335 wxColour c3;
336
337 switch (position) {
338 case DIAL_POSITION_NONE:
339 // This case was already handled before, it's here just
340 // to avoid compiler warning.
341 return;
342 case DIAL_POSITION_INSIDE: {
343 TextPoint.x = m_cx - (width / 2) - 1;
344 TextPoint.y = ((size.y - m_InstrumentSpacing) * .75) - height;
345 if ((g_TitleAlignment & wxALIGN_BOTTOM) != 0)
346 TextPoint.y -= m_TitleHeight;
347 GetGlobalColor(_T("DASHL"), &cl);
348 int penwidth = size.x / 100;
349 wxPen* pen =
350 wxThePenList->FindOrCreatePen(cl, penwidth, wxPENSTYLE_SOLID);
351 dc->SetPen(*pen);
352 GetGlobalColor(_T("DASHB"), &cl);
353 dc->SetBrush(cl);
354 // There might be a background drawn below
355 // so we must clear it first.
356 dc->DrawRoundedRectangle(TextPoint.x - 2, TextPoint.y - 2, width + 4,
357 height + 4, 3);
358 break;
359 }
360 case DIAL_POSITION_TOPLEFT:
361 GetGlobalColor(_T("DASHN"), &c3);
362 TextPoint.x = 0;
363 TextPoint.y = m_DataTop;
364 text = _T("A:") + text;
365 break;
366 case DIAL_POSITION_TOPRIGHT:
367 GetGlobalColor(_T("DASHN"), &c3);
368 TextPoint.x = size.x - width - 1;
369 TextPoint.y = m_DataTop;
370 break;
371 case DIAL_POSITION_BOTTOMLEFT:
372 GetGlobalColor(_T("BLUE3"), &c3);
373 text = _T("T:") + text;
374 TextPoint.x = 0;
375 TextPoint.y = GetDataBottom(size.y) - height;
376 break;
377 case DIAL_POSITION_BOTTOMRIGHT:
378 GetGlobalColor(_T("BLUE3"), &c3);
379 TextPoint.x = size.x - width - 1;
380 TextPoint.y = GetDataBottom(size.y) - height;
381 break;
382 default:
383 break;
384 }
385 wxColour c2;
386 GetGlobalColor(_T("DASHB"), &c2);
387 wxStringTokenizer tkz(text, _T("\n"));
388 wxString token;
389
390 token = tkz.GetNextToken();
391 while (token.Length()) {
392 if (m_Properties) {
393 f = m_Properties->m_LabelFont.GetChosenFont();
394 dc->GetTextExtent(token, &width, &height, NULL, NULL, &f);
395 } else {
396 f = g_pFontLabel->GetChosenFont();
397 dc->GetTextExtent(token, &width, &height, NULL, NULL, &f);
398 }
399 dc->DrawText(token, TextPoint.x, TextPoint.y);
400 TextPoint.y += height;
401 token = tkz.GetNextToken();
402 }
403}