15#include <wx/settings.h>
20HMPlane::HMPlane(
const double& width,
const double& height,
const double limits[2]) : m_width(width), m_height(height)
23 for (
auto accHeight = 0; accHeight <= m_height + m_meshSize; accHeight += m_meshSize) {
24 std::vector<BufferMeshCoords*> line;
25 for (
auto accWidth = 0; accWidth <= m_width + m_meshSize; accWidth += m_meshSize) {
30 line.emplace_back(bmc);
32 if (accHeight < 0.1f) m_meshTickX++;
35 m_coords.emplace_back(line);
37 m_coordsT.resize(m_coords[0].size());
38 for (
auto& line : m_coordsT) line.resize(m_coords.size());
39 for (
size_t i = 0; i < m_coords.size(); ++i) {
40 const auto& line = m_coords[i];
41 for (
size_t j = 0; j < line.size(); ++j) {
42 m_coordsT[j][i] = m_coords[i][j];
46 m_limits[0] = limits[0];
47 m_limits[1] = limits[1];
52 for (
const auto& line : m_coords) {
53 for (
auto* bmv : line) {
60void HMPlane::DrawDC(wxGraphicsContext* gc)
const
62 if (m_isClear)
return;
63 gc->SetPen(*wxTRANSPARENT_PEN);
64 for (
const auto& line : m_coords) {
65 wxGraphicsGradientStops gStops;
66 gStops.Add(wxColor(255, 255, 255), 0);
68 gStops.Add(VoltToColour(coords->z), coords->x / (m_width));
70 wxGraphicsBrush brush = gc->CreateLinearGradientBrush(0, line[0]->y, m_width, line[0]->y, gStops);
72 gc->DrawRectangle(0, line[0]->y, m_width, m_meshSize);
75 for (
const auto& line : m_coordsT) {
76 wxGraphicsGradientStops gStops;
77 gStops.Add(wxColor(255, 255, 255), 0);
79 gStops.Add(VoltToColour(coords->z), coords->y / (m_height));
81 wxGraphicsBrush brush = gc->CreateLinearGradientBrush(line[0]->x, 0, line[0]->x, m_height, gStops);
83 gc->DrawRectangle(line[0]->x, 0, m_meshSize, m_height);
87void HMPlane::DrawDC(wxDC& dc)
const
89 if (m_isClear)
return;
90 dc.SetPen(*wxTRANSPARENT_PEN);
92 for (
const auto& line : m_coords)
94 int y = wxRound(line[0]->y);
101 std::vector<Stop> stops;
103 stops.push_back({ 0.0, wxColour(255,255,255) });
107 double pos = coords->x / m_width;
108 stops.push_back({ pos, VoltToColour(coords->z) });
111 auto ColorAt = [&](
double t) -> wxColour
113 if (t <= stops.front().pos)
114 return stops.front().color;
116 if (t >= stops.back().pos)
117 return stops.back().color;
119 for (
size_t i = 0; i < stops.size() - 1; ++i)
121 if (t >= stops[i].pos && t <= stops[i + 1].pos)
125 (stops[i + 1].pos - stops[i].pos);
127 const wxColour& c1 = stops[i].color;
128 const wxColour& c2 = stops[i + 1].color;
131 c1.Red() + localT * (c2.Red() - c1.Red()),
132 c1.Green() + localT * (c2.Green() - c1.Green()),
133 c1.Blue() + localT * (c2.Blue() - c1.Blue())
138 return stops.back().color;
141 for (
size_t i = 0; i < stops.size() - 1; ++i)
143 int x1 = wxRound(stops[i].pos * m_width);
144 int x2 = wxRound(stops[i + 1].pos * m_width);
149 double t1 = stops[i].pos;
150 double t2 = stops[i + 1].pos;
152 wxColour c1 = ColorAt(t1);
153 wxColour c2 = ColorAt(t2);
155 wxRect rect(x1, y, x2 - x1, m_meshSize);
157 dc.GradientFillLinear(rect, c1, c2, wxEAST);
162void HMPlane::DrawLabelDC(wxGraphicsContext* gc)
const
164 wxGraphicsMatrix identityMatrix = gc->GetTransform();
165 identityMatrix.Set();
167 gc->SetTransform(identityMatrix);
168 gc->SetPen(*wxBLACK_PEN);
169 if (wxSystemSettings::GetAppearance().IsDark())
171 gc->SetPen(wxPen(wxColour(205, 210, 215)));
173 wxGraphicsGradientStops gStops;
174 gStops.Add(VoltToColour(-1.0, 210), 0.0);
175 gStops.Add(VoltToColour(-0.5, 210), 0.25);
176 gStops.Add(VoltToColour(0.0, 210), 0.5);
177 gStops.Add(VoltToColour(0.5, 210), 0.75);
178 gStops.Add(VoltToColour(1.0, 210), 1.0);
179 wxGraphicsBrush brush = gc->CreateLinearGradientBrush(30, m_height - 45, 330, m_height - 45, gStops);
182 gc->DrawRectangle(30, m_height - 45, 300, 30);
184 wxPoint2DDouble lines[10];
185 lines[0] = wxPoint2DDouble(30, m_height - 50);
186 lines[1] = wxPoint2DDouble(30, m_height - 40);
187 lines[2] = wxPoint2DDouble(105, m_height - 50);
188 lines[3] = wxPoint2DDouble(105, m_height - 40);
189 lines[4] = wxPoint2DDouble(180, m_height - 50);
190 lines[5] = wxPoint2DDouble(180, m_height - 40);
191 lines[6] = wxPoint2DDouble(255, m_height - 50);
192 lines[7] = wxPoint2DDouble(255, m_height - 40);
193 lines[8] = wxPoint2DDouble(330, m_height - 50);
194 lines[9] = wxPoint2DDouble(330, m_height - 40);
196 for (
size_t i = 0; i < 10; i += 2) {
197 gc->StrokeLine(lines[i].m_x, lines[i].m_y, lines[i + 1].m_x, lines[i + 1].m_y);
199 wxColour fontColour = *wxBLACK;
200 if (wxSystemSettings::GetAppearance().IsDark())
202 fontColour = wxColour(205, 210, 215);
205 gc->SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL), fontColour);
206 gc->DrawText(_(
"Voltage (p.u.)"), 30, m_height - 15);
207 wxString voltageText =
"";
208 double textWidth, textHeight;
209 voltageText = wxString::Format(
"%.3f", m_limits[0]);
210 gc->GetTextExtent(voltageText, &textWidth, &textHeight);
211 gc->DrawText(voltageText, 330 - textWidth / 2, m_height - 65);
212 voltageText = wxString::Format(
"%.3f", m_limits[0] * 0.75 + m_limits[1] * 0.25);
213 gc->GetTextExtent(voltageText, &textWidth, &textHeight);
214 gc->DrawText(voltageText, 255 - textWidth / 2, m_height - 65);
215 voltageText = wxString::Format(
"%.3f", m_limits[0] * 0.5 + m_limits[1] * 0.5);
216 gc->GetTextExtent(voltageText, &textWidth, &textHeight);
217 gc->DrawText(voltageText, 180 - textWidth / 2, m_height - 65);
218 voltageText = wxString::Format(
"%.3f", m_limits[0] * 0.25 + m_limits[1] * 0.75);
219 gc->GetTextExtent(voltageText, &textWidth, &textHeight);
220 gc->DrawText(voltageText, 105 - textWidth / 2, m_height - 65);
221 voltageText = wxString::Format(
"%.3f", m_limits[1]);
222 gc->GetTextExtent(voltageText, &textWidth, &textHeight);
223 gc->DrawText(voltageText, 30 - textWidth / 2, m_height - 65);
228void HMPlane::DrawLabelDC(wxDC& dc)
const
230 dc.SetUserScale(1.0, 1.0);
231 dc.SetDeviceOrigin(0, 0);
233 dc.SetPen(*wxBLACK_PEN);
234 if (wxSystemSettings::GetAppearance().IsDark())
236 dc.SetPen(wxPen(wxColour(205, 210, 215)));
239 int yStart = m_height - 45;
248 std::vector<Stop> stops = {
249 {0.00, VoltToColour(-1.0, 210)},
250 {0.25, VoltToColour(-0.5, 210)},
251 {0.50, VoltToColour(0.0, 210)},
252 {0.75, VoltToColour(0.5, 210)},
253 {1.00, VoltToColour(1.0, 210)}
256 auto ColorAt = [&](
double t) -> wxColour
258 for (
size_t i = 0; i < stops.size() - 1; ++i)
260 if (t >= stops[i].pos && t <= stops[i + 1].pos)
264 (stops[i + 1].pos - stops[i].pos);
266 const wxColour& c1 = stops[i].color;
267 const wxColour& c2 = stops[i + 1].color;
270 c1.Red() + localT * (c2.Red() - c1.Red()),
271 c1.Green() + localT * (c2.Green() - c1.Green()),
272 c1.Blue() + localT * (c2.Blue() - c1.Blue())
276 return stops.back().color;
279 for (
int x = 0; x < width; ++x)
281 double t = (double)x / width;
283 wxColour c = ColorAt(t);
286 dc.DrawLine(xStart + x,
292 dc.SetPen(*wxBLACK_PEN);
293 if (wxSystemSettings::GetAppearance().IsDark())
295 dc.SetPen(wxPen(wxColour(205, 210, 215)));
297 dc.SetBrush(*wxTRANSPARENT_BRUSH);
298 dc.DrawRectangle(xStart, yStart, width, height);
299 wxPoint2DDouble lines[10];
300 lines[0] = wxPoint2DDouble(30, m_height - 50);
301 lines[1] = wxPoint2DDouble(30, m_height - 40);
302 lines[2] = wxPoint2DDouble(105, m_height - 50);
303 lines[3] = wxPoint2DDouble(105, m_height - 40);
304 lines[4] = wxPoint2DDouble(180, m_height - 50);
305 lines[5] = wxPoint2DDouble(180, m_height - 40);
306 lines[6] = wxPoint2DDouble(255, m_height - 50);
307 lines[7] = wxPoint2DDouble(255, m_height - 40);
308 lines[8] = wxPoint2DDouble(330, m_height - 50);
309 lines[9] = wxPoint2DDouble(330, m_height - 40);
311 for (
size_t i = 0; i < 10; i += 2) {
312 dc.DrawLine(lines[i].m_x, lines[i].m_y, lines[i + 1].m_x, lines[i + 1].m_y);
315 dc.SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
316 dc.DrawText(_(
"Voltage (p.u.)"), 30, m_height - 15);
317 wxString voltageText =
"";
318 int textWidth, textHeight;
319 voltageText = wxString::Format(
"%.3f", m_limits[0]);
320 dc.GetTextExtent(voltageText, &textWidth, &textHeight);
321 dc.DrawText(voltageText, 330 - textWidth / 2, m_height - 65);
322 voltageText = wxString::Format(
"%.3f", m_limits[0] * 0.75 + m_limits[1] * 0.25);
323 dc.GetTextExtent(voltageText, &textWidth, &textHeight);
324 dc.DrawText(voltageText, 255 - textWidth / 2, m_height - 65);
325 voltageText = wxString::Format(
"%.3f", m_limits[0] * 0.5 + m_limits[1] * 0.5);
326 dc.GetTextExtent(voltageText, &textWidth, &textHeight);
327 dc.DrawText(voltageText, 180 - textWidth / 2, m_height - 65);
328 voltageText = wxString::Format(
"%.3f", m_limits[0] * 0.25 + m_limits[1] * 0.75);
329 dc.GetTextExtent(voltageText, &textWidth, &textHeight);
330 dc.DrawText(voltageText, 105 - textWidth / 2, m_height - 65);
331 voltageText = wxString::Format(
"%.3f", m_limits[1]);
332 dc.GetTextExtent(voltageText, &textWidth, &textHeight);
333 dc.DrawText(voltageText, 30 - textWidth / 2, m_height - 65);
336void HMPlane::SetLabelLimits(
const double& min,
const double& max)
342void HMPlane::SetRectSlope(
const wxRect2DDouble& rect,
const double& angle,
const double& depth)
344 for (
const auto& line : m_coords) {
345 for (
auto* coord : line) {
346 wxPoint2DDouble pt(coord->x, coord->y);
347 if (std::abs(angle) > 0.01) {
349 wxPoint2DDouble rotPt;
350 rotPt.m_x = cos(angle) * (pt.m_x - rect.GetCentre().m_x) + sin(angle) * (pt.m_y - rect.GetCentre().m_y) + rect.GetCentre().m_x;
351 rotPt.m_y = sin(angle) * (pt.m_x - rect.GetCentre().m_x) - cos(angle) * (pt.m_y - rect.GetCentre().m_y) + rect.GetCentre().m_y;
355 if (rect.Contains(pt)) {
357 coord->z = std::min<double>(coord->z, 1.0f);
358 coord->z = std::max<double>(coord->z, -1.0f);
366void HMPlane::Resize(
const double& width,
const double& height)
374 for (
const auto& line : m_coords) {
375 for (
auto* bmv : line) {
382 for (
auto accHeight = 0; accHeight < m_height + m_meshSize; accHeight += m_meshSize) {
383 std::vector<BufferMeshCoords*> line;
384 for (
auto accWidth = 0; accWidth < m_width + m_meshSize; accWidth += m_meshSize) {
389 line.emplace_back(bmc);
391 if (accHeight < 0.1f) m_meshTickX++;
394 m_coords.emplace_back(line);
400void HMPlane::ResizeDC(
const double& width,
const double& height)
407 for (
const auto& line : m_coords) {
408 for (
auto* bmv : line) {
416 for (
auto accHeight = 0; accHeight < m_height + m_meshSize; accHeight += m_meshSize) {
417 std::vector<BufferMeshCoords*> line;
418 for (
auto accWidth = 0; accWidth < m_width + m_meshSize; accWidth += m_meshSize) {
423 line.emplace_back(bmc);
425 if (accHeight < 0.1f) m_meshTickX++;
428 m_coords.emplace_back(line);
430 m_coordsT.resize(m_coords[0].size());
431 for (
auto& line : m_coordsT) line.resize(m_coords.size());
432 for (
size_t i = 0; i < m_coords.size(); ++i) {
433 const auto& line = m_coords[i];
434 for (
size_t j = 0; j < line.size(); ++j) {
435 m_coordsT[j][i] = m_coords[i][j];
442void HMPlane::SmoothPlane(
const unsigned int& iterations)
444 const int maxTickX =
static_cast<int>(m_meshTickX);
445 const int maxTickY =
static_cast<int>(m_meshTickY);
447 std::vector< std::vector<BufferMeshCoords> > tmpCoords;
448 for (
const auto& line : m_coords) {
449 std::vector<BufferMeshCoords> tmpCoordsLine;
450 for (
auto* bmv : line) {
451 tmpCoordsLine.push_back(*bmv);
453 tmpCoords.push_back(tmpCoordsLine);
477 float gaussianKernel[5][5] =
479 {1.0f / 256.0f, 4.0f / 256.0f, 6 / 256.0f, 4.0f / 256, 1.0f / 256.0f},
480 {4.0f / 256.0f, 16.0f / 256.0f, 24 / 256.0f, 16.0f / 256, 4.0f / 256.0f},
481 {6.0f / 256.0f, 24.0f / 256.0f, 36 / 256.0f, 24.0f / 256, 6.0f / 256.0f},
482 {4.0f / 256.0f, 16.0f / 256.0f, 24 / 256.0f, 16.0f / 256, 4.0f / 256.0f},
483 {1.0f / 256.0f, 4.0f / 256.0f, 6 / 256.0f, 4.0f / 256, 1.0f / 256.0f}
486 for (
size_t it = 0; it < iterations; ++it) {
487 for (
size_t i = 0; i < m_meshTickY; ++i) {
488 for (
size_t j = 0; j < m_meshTickX; ++j) {
492 for (
size_t ii = i - 2; ii <= i + 2; ++ii) {
493 for (
size_t jj = j - 2; jj <= j + 2; ++jj) {
495 if (ii >= 0 && ii < m_meshTickY && jj >= 0 && jj < m_meshTickX) {
496 value += tmpCoords[ii][jj].z * gaussianKernel[ii - i + 2][jj - j + 2];
500 m_coords[i][j]->z = value;
503 if (it < iterations - 1) {
504 for (
size_t i = 0; i < m_meshTickY; ++i) {
505 for (
size_t j = 0; j < m_meshTickX; ++j) {
506 tmpCoords[i][j].z = m_coords[i][j]->z;
519 for (
const auto& line : m_coords) {
520 for (
auto* bmv : line) {
529void HMPlane::FillCoordsBuffer()
531 m_bufferCoords.clear();
533 for (
const auto& line : m_coords) {
534 for (
auto* bmv : line) {
535 m_bufferCoords.push_back(bmv->x);
536 m_bufferCoords.push_back(bmv->y);
537 m_bufferCoords.push_back(bmv->z);
542wxColor HMPlane::VoltToColour(
double volt,
int alpha)
const
544 int red = 255, green = 255, blue = 255;
545 if (wxSystemSettings::GetAppearance().IsDark())
552 red = 100 * volt + 100;
553 green = -160 * volt + 40;
554 blue = -150 * volt + 105;
556 else if (volt > -0.5 && volt < 0.0) {
557 red = -14 * volt + 43;
558 green = -148 * volt + 46;
559 blue = -256 * volt + 52;
561 else if (volt >= 0.0 && volt < 0.5) {
562 red = 304 * volt + 43;
563 green = 198 * volt + 46;
564 blue = -44 * volt + 52;
567 red = 80 * volt + 155;
568 green = 70 * volt + 110;
569 blue = 30 * volt + 15;
575 red = -100 * volt - 50;
576 green = 200 * volt + 300;
579 else if (volt > -0.5 && volt < 0) {
580 red = 510 * volt + 255;
581 green = 110 * volt + 255;
584 else if (volt >= 0.0 && volt < 0.5) {
586 green = -110 * volt + 255;
587 blue = -510 * volt + 255;
589 else if (volt >= 0.5) {
591 green = -200 * volt + 300;
592 blue = 100 * volt - 50;
595 return wxColor(red, green, blue, alpha);