SPH
CurveDialog.cpp
Go to the documentation of this file.
2 #include "gui/Utils.h"
3 #include "post/Plot.h"
4 #include "post/Point.h"
5 #include <wx/graphics.h>
6 
8 
9 CurvePanel::CurvePanel(wxWindow* parent)
10  : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(800, 600)) {
11  this->Connect(wxEVT_PAINT, wxPaintEventHandler(CurvePanel::onPaint));
12  this->Connect(wxEVT_MOTION, wxMouseEventHandler(CurvePanel::onMouseMotion));
13  this->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(CurvePanel::onLeftDown));
14  this->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(CurvePanel::onLeftUp));
15  this->Connect(wxEVT_RIGHT_UP, wxMouseEventHandler(CurvePanel::onRightUp));
16 }
17 
18 const float radius = 6;
19 
20 void CurvePanel::onPaint(wxPaintEvent& UNUSED(evt)) {
21  wxPaintDC dc(this);
22 
23  dc.SetPen(*wxWHITE_PEN);
24 
25  wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
26  wxPen pen = *wxWHITE_PEN;
27  pen.SetWidth(2);
28  gc->SetPen(pen);
29  wxBrush brush = *wxWHITE_BRUSH;
30  brush.SetColour(wxColour(100, 100, 100));
31  gc->SetBrush(brush);
32 
33  // draw the curve
34  for (Size i = 0; i < curve.getPointCnt() - 1; ++i) {
35  wxGraphicsPath path = gc->CreatePath();
36  const int x1 = curveToWindow<wxPoint>(curve.getPoint(i)).x;
37  const int x2 = curveToWindow<wxPoint>(curve.getPoint(i + 1)).x;
38 
39  if (i == highlightSegment) {
40  pen.SetColour(wxColour(255, 100, 50));
41  } else {
42  pen.SetColour(wxColour(180, 180, 180));
43  }
44  gc->SetPen(pen);
45 
46  for (int x = x1; x <= x2; ++x) {
47  const float f = float(curve(windowToCurve(wxPoint(x, 0)).x));
48  const float y = float(curveToWindow<wxPoint2DDouble, double>(CurvePoint{ 0.f, f }).m_y);
49  const wxPoint2DDouble p(x, y);
50  if (x > padding) {
51  path.AddLineToPoint(p);
52  } else {
53  path.MoveToPoint(p);
54  }
55  }
56  gc->StrokePath(path);
57  }
58 
59  // draw the points
60  pen.SetColour(wxColour(180, 180, 180));
61  gc->SetPen(pen);
62  for (Size i = 0; i < curve.getPointCnt(); ++i) {
63  const wxPoint p = curveToWindow<wxPoint>(curve.getPoint(i));
64  if (highlightIdx == i) {
65  brush.SetColour(wxColour(255, 100, 50));
66  } else {
67  brush.SetColour(wxColour(100, 100, 100));
68  }
69  gc->SetBrush(brush);
70  gc->DrawEllipse(p.x - radius, p.y - radius, 2 * radius, 2 * radius);
71  }
72 
73  // draw axes
74  wxSize size = this->GetSize() - wxSize(2 * padding, 2 * padding);
75  dc.DrawLine(wxPoint(padding, padding + size.y), wxPoint(padding + size.x, padding + size.y));
76  dc.DrawLine(wxPoint(padding, padding), wxPoint(padding, padding + size.y));
77 
78  // draw tics
79  const Interval rangeX = curve.rangeX();
80  Array<Float> ticsX = getLinearTics(rangeX, 4);
81  for (Size i = 0; i < ticsX.size(); ++i) {
82  const std::wstring label = toPrintableString(ticsX[i], 1);
83  const int x = padding + i * size.x / (ticsX.size() - 1);
84  drawTextWithSubscripts(dc, label, wxPoint(x - 6, size.y + padding + 6));
85  dc.DrawLine(wxPoint(x, size.y + padding - 2), wxPoint(x, size.y + padding + 2));
86  }
87  const Interval rangeY = curve.rangeY();
88  Array<Float> ticsY = getLinearTics(rangeY, 3);
89  for (Size i = 0; i < ticsY.size(); ++i) {
90  const std::wstring label = toPrintableString(ticsY[i], 1);
91  const int y = padding + size.y - i * size.y / (ticsY.size() - 1);
92  drawTextWithSubscripts(dc, label, wxPoint(2, y - 8));
93  dc.DrawLine(wxPoint(padding - 2, y), wxPoint(padding + 2, y));
94  }
95 
96  // draw mouse position and curve value
97  if (mousePosition != wxDefaultPosition) {
98  dc.SetPen(*wxGREY_PEN);
99  // project the mouse position to the curve
100  CurvePoint curvePos = windowToCurve(mousePosition);
101  curvePos.y = curve(curvePos.x);
102  const wxPoint center = curveToWindow<wxPoint>(curvePos);
103  dc.DrawLine(wxPoint(padding, center.y), wxPoint(padding + size.x, center.y));
104  dc.DrawLine(wxPoint(center.x, padding), wxPoint(center.x, padding + size.y));
105  dc.SetTextForeground(wxColour(128, 128, 128));
106  wxFont font = dc.GetFont().Smaller();
107  dc.SetFont(font);
108  const std::wstring labelX = toPrintableString(curvePos.x, 2);
109  const std::wstring labelY = toPrintableString(curvePos.y, 2);
110  drawTextWithSubscripts(dc, L"(" + labelX, center + wxPoint(-65, -15));
111  drawTextWithSubscripts(dc, labelY + L")", center + wxPoint(5, -15));
112  }
113 
114  delete gc;
115 }
116 
117 void CurvePanel::onMouseMotion(wxMouseEvent& evt) {
118  mousePosition = evt.GetPosition();
119  highlightIdx = this->getIdx(mousePosition);
120  highlightSegment = this->getSegment(mousePosition);
121  if (evt.Dragging() && lockedIdx) {
122  CurvePoint newPos = windowToCurve(evt.GetPosition());
123  curve.setPoint(lockedIdx.value(), newPos);
124  }
125  this->Refresh();
126 }
127 
128 void CurvePanel::onLeftDown(wxMouseEvent& evt) {
129  mousePosition = evt.GetPosition();
130  Optional<Size> idx = this->getIdx(mousePosition);
131  if (idx) {
132  lockedIdx = idx;
133  } else {
134  const CurvePoint newPos = windowToCurve(mousePosition);
135  curve.addPoint(newPos);
136  lockedIdx = this->getIdx(mousePosition);
137  }
138  this->Refresh();
139 }
140 
141 void CurvePanel::onLeftUp(wxMouseEvent& UNUSED(evt)) {
142  lockedIdx = NOTHING;
143 }
144 
145 void CurvePanel::onRightUp(wxMouseEvent& evt) {
146  mousePosition = evt.GetPosition();
147  if (Optional<Size> pointIdx = this->getIdx(mousePosition)) {
148  curve.deletePoint(pointIdx.value());
149  } else if (Optional<Size> segmentIdx = this->getSegment(mousePosition)) {
150  curve.setSegment(segmentIdx.value(), !curve.getSegment(segmentIdx.value()));
151  }
152  this->Refresh();
153 }
154 
155 template <typename TPoint, typename T>
156 TPoint CurvePanel::curveToWindow(const CurvePoint& p) const {
157  wxSize size = this->GetSize() - wxSize(2 * padding, 2 * padding);
158  const Interval rangeX = curve.rangeX();
159  const Interval rangeY = curve.rangeY();
160 
161  return TPoint(T(padding + (p.x - rangeX.lower()) / rangeX.size() * size.x),
162  T(padding + size.y - (p.y - rangeY.lower()) / rangeY.size() * size.y));
163 }
164 
165 CurvePoint CurvePanel::windowToCurve(const wxPoint2DDouble p) const {
166  wxSize size = this->GetSize() - wxSize(2 * padding, 2 * padding);
167  const Interval rangeX = curve.rangeX();
168  const Interval rangeY = curve.rangeY();
169  return CurvePoint{ float((p.m_x - padding) * rangeX.size() / size.x + rangeX.lower()),
170  float(rangeY.size() - (p.m_y - padding) * rangeY.size() / size.y + rangeY.lower()) };
171 }
172 
173 Optional<Size> CurvePanel::getIdx(const wxPoint mousePos) const {
174  for (Size i = 0; i < curve.getPointCnt(); ++i) {
175  wxPoint dist = curveToWindow<wxPoint>(curve.getPoint(i)) - mousePos;
176  if (sqr(dist.x) + sqr(dist.y) < sqr(radius)) {
177  return i;
178  }
179  }
180  return NOTHING;
181 }
182 
183 Optional<Size> CurvePanel::getSegment(const wxPoint mousePos) const {
184  for (Size i = 0; i < curve.getPointCnt() - 1; ++i) {
185  const wxPoint p1 = curveToWindow<wxPoint>(curve.getPoint(i));
186  const wxPoint p2 = curveToWindow<wxPoint>(curve.getPoint(i + 1));
187  if (mousePos.x > p1.x && mousePos.x < p2.x) {
188  CurvePoint m = windowToCurve(mousePos);
189  m.y = curve(m.x);
190  const wxPoint projPos = curveToWindow<wxPoint>(m);
191  if (abs(mousePos.y - projPos.y) < radius) {
192  return i;
193  }
194  }
195  }
196  return NOTHING;
197 }
198 
199 wxPGWindowList CurveEditor::CreateControls(wxPropertyGrid* propgrid,
200  wxPGProperty* property,
201  const wxPoint& pos,
202  const wxSize& size) const {
203  (void)propgrid;
204  (void)property;
205  (void)pos;
206  (void)size;
207  CurveProperty* curve = dynamic_cast<CurveProperty*>(property);
208  CurveDialog* dialog =
209  new CurveDialog(propgrid, curve->getCurve(), [propgrid, property](const Curve& curve) {
210  wxPropertyGridEvent* changeEvent = new wxPropertyGridEvent(wxEVT_PG_CHANGED);
211  changeEvent->SetProperty(property);
212  CurveProperty* curveProp = dynamic_cast<CurveProperty*>(property);
213  SPH_ASSERT(curveProp);
214  curveProp->setCurve(curve);
215  propgrid->GetEventHandler()->ProcessEvent(*changeEvent);
216  });
217  dialog->Show();
218  return wxPGWindowList(nullptr);
219 }
220 
221 void CurveEditor::UpdateControl(wxPGProperty* property, wxWindow* ctrl) const {
222  (void)property;
223  (void)ctrl;
224 }
225 
227  const wxRect& rect,
228  wxPGProperty* property,
229  const wxString& text) const {
230  dc.SetBrush(*wxBLACK_BRUSH);
231  dc.DrawRectangle({ 0, 0 }, { 200, 100 });
232  (void)rect;
233  (void)property;
234  (void)text;
235 }
236 
237 bool CurveEditor::OnEvent(wxPropertyGrid* propgrid,
238  wxPGProperty* property,
239  wxWindow* wnd_primary,
240  wxEvent& event) const {
241  (void)propgrid;
242  (void)property;
243  (void)wnd_primary;
244  (void)event;
245  return true;
246 }
247 
248 CurveDialog::CurveDialog(wxWindow* parent, const Curve& curve, Function<void(const Curve&)> curveChanged)
249  : wxFrame(parent, wxID_ANY, "Curve", wxDefaultPosition, wxSize(600, 450))
250  , curveChanged(curveChanged) {
251  wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
252  CurvePanel* panel = new CurvePanel(this);
253  panel->setCurve(curve);
254  sizer->Add(panel, 1, wxEXPAND | wxALL);
255 
256  wxBoxSizer* buttonSizer = new wxBoxSizer(wxHORIZONTAL);
257  wxButton* okButton = new wxButton(this, wxID_ANY, "OK");
258  okButton->Bind(wxEVT_BUTTON, [this, panel](wxCommandEvent& UNUSED(evt)) {
259  this->curveChanged(panel->getCurve());
260  this->Close();
261  });
262  buttonSizer->Add(okButton, 0);
263 
264  wxButton* cancelButton = new wxButton(this, wxID_ANY, "Cancel");
265  cancelButton->Bind(wxEVT_BUTTON, [this](wxCommandEvent& UNUSED(evt)) { this->Close(); });
266  buttonSizer->Add(cancelButton, 0);
267 
268  sizer->Add(buttonSizer);
269 
270  this->SetSizer(sizer);
271  this->Layout();
272 }
273 
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
const float radius
Definition: CurveDialog.cpp:18
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
constexpr INLINE T sqr(const T &f) noexcept
Return a squared value.
Definition: MathUtils.h:67
INLINE auto abs(const T &f)
Definition: MathUtils.h:276
#define UNUSED(x)
Definition: Object.h:37
#define NAMESPACE_SPH_END
Definition: Object.h:12
const NothingType NOTHING
Definition: Optional.h:16
Array< Float > getLinearTics(const Interval &interval, const Size minCount)
Returns the tics to be drawn on a linear axis of a plot.
Definition: Plot.cpp:423
Drawing quantity values as functions of time or spatial coordinates.
void drawTextWithSubscripts(wxDC &dc, const std::wstring &text, const wxPoint point)
Definition: Utils.cpp:86
std::wstring toPrintableString(const Float value, const Size precision, const Float decimalThreshold)
Converts the value to a printable string.
Definition: Utils.cpp:121
Random utility functions for drawing stuff to DC.
INLINE TCounter size() const noexcept
Definition: Array.h:193
CurveDialog(wxWindow *parent, const Curve &curve, Function< void(const Curve &)> curveChanged)
virtual wxPGWindowList CreateControls(wxPropertyGrid *propgrid, wxPGProperty *property, const wxPoint &pos, const wxSize &size) const override
virtual void DrawValue(wxDC &dc, const wxRect &rect, wxPGProperty *property, const wxString &text) const override
virtual void UpdateControl(wxPGProperty *property, wxWindow *ctrl) const override
virtual bool OnEvent(wxPropertyGrid *propgrid, wxPGProperty *property, wxWindow *wnd_primary, wxEvent &event) const override
void setCurve(const Curve &newCurve)
Definition: CurveDialog.h:31
Curve getCurve() const
Definition: CurveDialog.h:35
CurvePanel(wxWindow *parent)
Definition: CurveDialog.cpp:9
const Curve & getCurve() const
Definition: CurveDialog.h:100
Represents a user-defined function, defined by a set of points interpolated by either piecewise linea...
Definition: Curve.h:19
const CurvePoint & getPoint(const Size idx) const
Returns the position of idx-th point.
Definition: Curve.cpp:99
Interval rangeY() const
Returns the extent of the curve in y-direction.
Definition: Curve.cpp:142
void setPoint(const Size idx, const CurvePoint &newPoint)
Modifies the position of idx-th points.
Definition: Curve.cpp:103
Size getPointCnt() const
Returns the number of points defining the curve.
Definition: Curve.cpp:95
void addPoint(const CurvePoint &newPoint)
Adds a new point to the curve.
Definition: Curve.cpp:108
void setSegment(const Size idx, const bool cubic)
Modifies the interpolation type of idx-th segment.
Definition: Curve.cpp:130
Interval rangeX() const
Returns the extent of the curve in x-direction.
Definition: Curve.cpp:134
bool getSegment(const Size idx) const
Returns the interpolation type of idx-th segment.
Definition: Curve.cpp:126
void deletePoint(const Size idx)
Removes idx-th point from curve.
Definition: Curve.cpp:120
Object representing a 1D interval of real numbers.
Definition: Interval.h:17
INLINE Float lower() const
Returns lower bound of the interval.
Definition: Interval.h:74
INLINE Float size() const
Returns the size of the interval.
Definition: Interval.h:89
INLINE Type & value()
Returns the reference to the stored value.
Definition: Optional.h:172
2D point and other primitives for 2D geometry
Float x
Definition: Curve.h:9
Float y
Definition: Curve.h:10