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