Skip to content

WxWidgets:Example:GraphPanel

wxWidgets를 사용한 그래프를 그려주는 패널 구현.

Header file

/**
 * @file   MugcupGraph.hpp
 * @brief  MugcupGraph class prototype.
 * @author your
 * @date   2018-06-15
 */

#ifndef __INCLUDE_LIBMS__LIBMUGCUP_SRC_UI_COMPONENTS_MUGCUPGRAPH_HPP__
#define __INCLUDE_LIBMS__LIBMUGCUP_SRC_UI_COMPONENTS_MUGCUPGRAPH_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/predef.hpp>
#include <libmugcup/src/config.h>

#include <map>
#include <vector>

#include <wx/wx.h>

// ---------------------
NAMESPACE_LIBMUGCUP_OPEN
// ---------------------

/**
 * MugcupGraph class prototype.
 *
 * @author your
 * @date   2018-06-15
 */
class MugcupGraph : public wxPanel
{
public:
    wxDECLARE_EVENT_TABLE();

public:
    TBAG_CONSTEXPR static unsigned long const   BACKGROUND_COLOR = 0x403e3b;
    TBAG_CONSTEXPR static unsigned long const REMARKS_TEXT_COLOR = 0xd8d9da;
    TBAG_CONSTEXPR static unsigned long const   GUIDE_LINE_COLOR = 0x828282;
    TBAG_CONSTEXPR static unsigned long const   GUIDE_TEXT_COLOR = 0xd8d9da;
    TBAG_CONSTEXPR static unsigned long const    DEBUGGING_COLOR = 0x0024ff;
    TBAG_CONSTEXPR static unsigned long const    DATA_LINE_COLOR = 0x0024ff;
    TBAG_CONSTEXPR static unsigned long const    DATA_TEXT_COLOR = 0xd8d9da;
    TBAG_CONSTEXPR static unsigned long const DISABLE_RECT_COLOR = 0x242424;
    TBAG_CONSTEXPR static unsigned long const DISABLE_TEXT_COLOR = 0x999999;

    TBAG_CONSTEXPR static int const      PADDING_SIZE =  8;
    TBAG_CONSTEXPR static int const STEP_PADDING_SIZE =  4;
    TBAG_CONSTEXPR static int const   TITLE_TEXT_SIZE = 14;
    TBAG_CONSTEXPR static int const REMARKS_TEXT_SIZE = 10;
    TBAG_CONSTEXPR static int const  GUIDE_LINE_WIDTH =  1;
    TBAG_CONSTEXPR static int const   GUIDE_TEXT_SIZE =  8;
    TBAG_CONSTEXPR static int const   DATA_LINE_WIDTH =  2;
    TBAG_CONSTEXPR static int const    DATA_TEXT_SIZE =  8;

public:
    using Points = std::vector<wxRealPoint>;

public:
    struct Remarks
    {
        bool     show  = true;
        int      size  = REMARKS_TEXT_SIZE;
        wxColour color = REMARKS_TEXT_COLOR;
        wxString text;
    };

    struct Guide
    {
        bool     show       = true;
        int      line_width = GUIDE_LINE_WIDTH;
        int      text_size  = GUIDE_TEXT_SIZE;
        wxColour line_color = GUIDE_LINE_COLOR;
        wxColour text_color = GUIDE_TEXT_COLOR;

        // Dynamic graph properties.
        double start_data  = 0.0; ///< The starting value of the data.
        double print_begin = 0.0; ///< The beginning value of the print data.
        double print_end   = 1.0; ///< The end value of the print data.
        double print_step  = 0.1; ///< Guidelines for print data.

        bool show_begin = true;
        bool show_end   = true;

        bool show_text    = true;
        bool integer_text = false;
    };

    struct Data
    {
        bool     show       = true;
        int      line_width = DATA_LINE_WIDTH;
        int      text_size  = DATA_TEXT_SIZE;
        wxColour line_color = DATA_LINE_COLOR;
        wxColour text_color = DATA_TEXT_COLOR;

        wxString text;
        Points points;

        /** Internal variable. */
        struct {
            wxRect rect;
        } internal;
    };

public:
    using Datas = std::vector<Data>;

private:
    Remarks _title;

private:
    wxString _integer_format;
    wxString _floating_format;

private:
    Remarks  _top;
    Remarks  _left;
    Remarks  _right;
    Remarks  _bottom;

private:
    Guide _guide_x;
    Guide _guide_y;

private:
    // @formatter:off
    wxCoord      _padding =      PADDING_SIZE;
    wxCoord _step_padding = STEP_PADDING_SIZE;
    // @formatter:on

private:
    Datas _datas;

public:
    MugcupGraph(wxWindow * parent,
                wxWindowID id = wxID_ANY,
                wxPoint const & pos = wxDefaultPosition,
                wxSize const & size = wxDefaultSize);
    virtual ~MugcupGraph();

public:
    inline Remarks       & atTitle()       TBAG_NOEXCEPT { return _title; }
    inline Remarks const & atTitle() const TBAG_NOEXCEPT { return _title; }

public:
    inline wxString  getIntegerFormat() const TBAG_NOEXCEPT { return  _integer_format; }
    inline wxString getFloatingFormat() const TBAG_NOEXCEPT { return _floating_format; }

public:
    inline Remarks       & atTop   ()       TBAG_NOEXCEPT { return _top;    }
    inline Remarks const & atTop   () const TBAG_NOEXCEPT { return _top;    }
    inline Remarks       & atLeft  ()       TBAG_NOEXCEPT { return _left;   }
    inline Remarks const & atLeft  () const TBAG_NOEXCEPT { return _left;   }
    inline Remarks       & atRight ()       TBAG_NOEXCEPT { return _right;  }
    inline Remarks const & atRight () const TBAG_NOEXCEPT { return _right;  }
    inline Remarks       & atBottom()       TBAG_NOEXCEPT { return _bottom; }
    inline Remarks const & atBottom() const TBAG_NOEXCEPT { return _bottom; }

public:
    inline Guide       & atGuideX()       TBAG_NOEXCEPT { return _guide_x; }
    inline Guide const & atGuideX() const TBAG_NOEXCEPT { return _guide_x; }
    inline Guide       & atGuideY()       TBAG_NOEXCEPT { return _guide_y; }
    inline Guide const & atGuideY() const TBAG_NOEXCEPT { return _guide_y; }

public:
    inline wxCoord     getPadding() const TBAG_NOEXCEPT { return      _padding; }
    inline wxCoord getStepPadding() const TBAG_NOEXCEPT { return _step_padding; }

    inline void     setPadding(wxCoord value) TBAG_NOEXCEPT {      _padding = value; }
    inline void setStepPadding(wxCoord value) TBAG_NOEXCEPT { _step_padding = value; }

public:
    inline Datas       & atDatas()       TBAG_NOEXCEPT { return _datas; }
    inline Datas const & atDatas() const TBAG_NOEXCEPT { return _datas; }

public:
    wxFont getDefaultFont(int size);

protected:
    void onPaint(wxPaintEvent & event);
    void onLeftDown(wxMouseEvent & event);
    void onLeftUp(wxMouseEvent & event);
    void onMotion(wxMouseEvent & event);

private:
    void drawTitle(wxDC & dc, int & top);
    void drawTop(wxDC & dc, int & top);
    void drawBottom(wxDC & dc, int & bottom);
    void drawLeft(wxDC & dc, int & left);
    void drawRight(wxDC & dc, int & right);

private:
    void drawDataRemarks(wxDC & dc, int top, int & right, int rect_width = 24);
    void findMaxTextSize(wxDC & dc, wxSize & size);

private:
    void updateGuideXOffset(wxDC & dc, int & right, int & bottom);
    void updateGuideYOffset(wxDC & dc, int & left, int & top);

private:
    enum class GuideDirection
    {
        GD_HORIZONTAL,
        GD_VERTICAL,
    };

private:
    void drawGuide(wxDC & dc, Guide const & guide,
                   int graph_begin, int graph_end, int line_begin, int line_end,
                   GuideDirection direction);
    void drawGuideLine(wxDC & dc, bool show_text, bool integer_text, GuideDirection direction,
                       double text_value, int graph_value, int line_begin, int line_end,
                       wxRect & prev_drawing_label);

private:
    void drawPoints(wxDC & dc, wxPoint const & lt, wxPoint const & rb);

private:
    void drawDebuggingRect(wxDC & dc, wxRect const & rect);
};

// ----------------------
NAMESPACE_LIBMUGCUP_CLOSE
// ----------------------

#endif // __INCLUDE_LIBMS__LIBMUGCUP_SRC_UI_COMPONENTS_MUGCUPGRAPH_HPP__

Source file

/**
 * @file   MugcupGraph.cpp
 * @brief  MugcupGraph class implementation.
 * @author your
 * @date   2018-06-15
 */

#include <libmugcup/src/ui/components/MugcupGraph.hpp>
#include <libtbag/random/Random.hpp>

#include <wx/dcbuffer.h>

#define ENABLE_DUMMY_DATA
//#define ENABLE_ON_PAINT_DEBUGGING

// ---------------------
NAMESPACE_LIBMUGCUP_OPEN
// ---------------------

wxBEGIN_EVENT_TABLE(MugcupGraph, wxPanel)
    EVT_PAINT(MugcupGraph::onPaint)
    EVT_LEFT_DOWN(MugcupGraph::onLeftDown)
    EVT_LEFT_UP(MugcupGraph::onLeftUp)
    EVT_MOTION(MugcupGraph::onMotion)
wxEND_EVENT_TABLE()

MugcupGraph::MugcupGraph(wxWindow * parent,
                         wxWindowID id,
                         wxPoint const & pos,
                         wxSize const & size)
        : wxPanel(parent, id, pos, size),
          _integer_format(wxT("%d")),
          _floating_format(wxT("%.2f"))
{
    SetBackgroundColour(BACKGROUND_COLOR);

#if defined(ENABLE_DUMMY_DATA)
    // @formatter:off
    _title .text = wxT("[TITLE]");
    _title .size = TITLE_TEXT_SIZE;
    _top   .text = wxT("Top content");
    _top   .show = true;
    _left  .text = wxT("Left content");
    _left  .show = true;
    _right .text = wxT("Right content");
    _right .show = true;
    _bottom.text = wxT("Bottom content");
    _bottom.show = true;
    // @formatter:on

    // @formatter:off
    _guide_x.start_data  = 0.05;
    _guide_x.print_begin = 0.5;
    _guide_x.print_end   = 1.0;
    _guide_x.print_step  = 0.1;
    // ------------------------
    _guide_y.start_data  = 0.0;
    _guide_y.print_begin = 0.0;
    _guide_y.print_end   = 1.0;
    _guide_y.print_step  = 0.5;
    // @formatter:on

    _datas.resize(2U);
    _datas[0].line_color.Set(wxT("#fa8e00"));
    _datas[0].text = wxT("First");
    _datas[1].line_color.Set(wxT("#619e1b"));
    _datas[1].text = wxT("Second");
    int const DATA_COUNT = 10;
    double const STEP = (_guide_x.print_end - _guide_x.print_begin) / DATA_COUNT;
    for (int i = 0; i <= DATA_COUNT; ++i) {
        auto y1 = libtbag::random::gen<int>(0, 99);
        _datas[0].points.emplace_back(_guide_x.print_begin + (STEP * i), y1*0.01);
        auto y2 = libtbag::random::gen<int>(0, 99);
        _datas[1].points.emplace_back(_guide_x.print_begin + (STEP * i), y2*0.01);
    }
#endif
}

MugcupGraph::~MugcupGraph()
{
    // EMPTY.
}

wxFont MugcupGraph::getDefaultFont(int size)
{
    return wxFont(size, wxFontFamily::wxFONTFAMILY_DEFAULT,
                  wxFontStyle::wxFONTSTYLE_NORMAL,
                  wxFontWeight::wxFONTWEIGHT_NORMAL);
}

void MugcupGraph::onPaint(wxPaintEvent & event)
{
    wxPaintDC dc(this);
    dc.SetBackground(wxBrush(GetBackgroundColour()));
    dc.Clear();

    wxSize const WINDOW_SIZE = GetSize();
    wxPoint lt(_padding, _padding);
    wxPoint rb(WINDOW_SIZE.x - _padding, WINDOW_SIZE.y - _padding);

    // @formatter:off
    if (_title .show) { drawTitle (dc, lt.y); }
    if (_top   .show) { drawTop   (dc, lt.y); }
    if (_bottom.show) { drawBottom(dc, rb.y); }
    if (_left  .show) { drawLeft  (dc, lt.x); }
    drawDataRemarks(dc, lt.y, rb.x);
    if (_right .show) { drawRight (dc, rb.x); }
    // @formatter:on

#if defined(ENABLE_ON_PAINT_DEBUGGING)
    drawDebuggingRect(dc, wxRect(lt, rb));
#endif

    // @formatter:off
    if (_guide_x.show && _guide_x.show_text) { updateGuideXOffset(dc, rb.x, rb.y); }
    if (_guide_y.show && _guide_y.show_text) { updateGuideYOffset(dc, lt.x, lt.y); }
    // @formatter:on

    // @formatter:off
    if (_guide_x.show) { drawGuide(dc, _guide_x, lt.x, rb.x, lt.y, rb.y, GuideDirection::GD_HORIZONTAL); }
    if (_guide_y.show) { drawGuide(dc, _guide_y, lt.y, rb.y, lt.x, rb.x, GuideDirection::GD_VERTICAL); }
    // @formatter:on

    drawPoints(dc, lt, rb);
}

void MugcupGraph::onLeftDown(wxMouseEvent & event)
{
    // EMPTY.
}

void MugcupGraph::onLeftUp(wxMouseEvent & event)
{
    int change_count = 0;
    for (auto & data : _datas) {
        if (data.internal.rect.Contains(event.m_x, event.m_y)) {
            data.show = !(data.show);
            ++change_count;
        }
    }

    if (change_count >= 1) {
        Update();
        Refresh();
    }
}

void MugcupGraph::onMotion(wxMouseEvent & event)
{
    // EMPTY.
}

void MugcupGraph::drawTitle(wxDC & dc, int & top)
{
    wxCoord width, height;
    dc.SetFont(getDefaultFont(_title.size));
    dc.SetTextForeground(_title.color);
    dc.GetTextExtent(_title.text, &width, &height);
    dc.DrawText(_title.text, (GetSize().x - width) / 2, top);
    top += (_step_padding + height);
}

void MugcupGraph::drawTop(wxDC & dc, int & top)
{
    wxCoord width, height;
    dc.SetFont(getDefaultFont(_top.size));
    dc.SetTextForeground(_top.color);
    dc.GetTextExtent(_top.text, &width, &height);
    dc.DrawText(_top.text, (GetSize().x - width) / 2, top);
    top += (_step_padding + height);
}

void MugcupGraph::drawBottom(wxDC & dc, int & bottom)
{
    wxCoord width, height;
    dc.SetFont(getDefaultFont(_bottom.size));
    dc.SetTextForeground(_bottom.color);
    dc.GetTextExtent(_bottom.text, &width, &height);
    bottom -= height;
    dc.DrawText(_bottom.text, (GetSize().x - width) / 2, bottom);
    bottom -= _step_padding;
}

void MugcupGraph::drawLeft(wxDC & dc, int & left)
{
    wxCoord width, height;
    dc.SetFont(getDefaultFont(_left.size));
    dc.SetTextForeground(_left.color);
    dc.GetTextExtent(_left.text, &width, &height);
    dc.DrawRotatedText(_left.text, left, (GetSize().y + width) / 2, 90.0);
    left += (_step_padding + height);
}

void MugcupGraph::drawRight(wxDC & dc, int & right)
{
    wxCoord width, height;
    dc.SetFont(getDefaultFont(_right.size));
    dc.SetTextForeground(_right.color);
    dc.GetTextExtent(_right.text, &width, &height);
    dc.DrawRotatedText(_right.text, right, (GetSize().y - width) / 2, -90.0);
    right -= (_step_padding + height);
}

void MugcupGraph::drawDataRemarks(wxDC & dc, int top, int & right, int rect_width)
{
    auto const HALF_PADDING = static_cast<int>(_step_padding / 2);
    auto const ROUND_RADIUS = 3.14f / 2.0f;
    auto const DATA_COUNT = _datas.size();

    wxSize max;
    findMaxTextSize(dc, max);

    for (std::size_t i = 0; i < DATA_COUNT; ++i) {
        auto & data = _datas[i];

        wxRect color_rect;
        color_rect.x      = right - _step_padding - max.x - rect_width;
        color_rect.y      = top + (int)((max.y + _step_padding) * i);
        color_rect.width  = rect_width;
        color_rect.height = max.y;

        data.internal.rect.x      = color_rect.x - HALF_PADDING;
        data.internal.rect.y      = color_rect.y - HALF_PADDING;
        data.internal.rect.width  = rect_width + max.x + _step_padding + (HALF_PADDING * 2);
        data.internal.rect.height = color_rect.height + (HALF_PADDING * 2);
        if (data.show == false) {
            dc.SetPen(wxPen(wxColour(DISABLE_RECT_COLOR)));
            dc.SetBrush(wxBrush(DISABLE_RECT_COLOR));
            dc.DrawRoundedRectangle(data.internal.rect, ROUND_RADIUS);
        }

        dc.SetFont(getDefaultFont(data.text_size));
        if (data.show) {
            dc.SetTextForeground(data.text_color);
        } else {
            dc.SetTextForeground(DISABLE_TEXT_COLOR);
        }
        dc.DrawText(data.text, right - max.x, color_rect.y);

        if (data.show) {
            dc.SetPen(wxPen(data.line_color));
            dc.SetBrush(wxBrush(data.line_color));
        } else {
            dc.SetPen(wxPen(DISABLE_TEXT_COLOR));
            dc.SetBrush(wxBrush(DISABLE_TEXT_COLOR));
        }
        dc.DrawRoundedRectangle(color_rect, ROUND_RADIUS);
    }

    right -= (_step_padding + max.x + _step_padding + rect_width);
}

void MugcupGraph::findMaxTextSize(wxDC & dc, wxSize & size)
{
    size.x = 0;
    size.y = 0;

    for (auto & data : _datas) {
        wxCoord width, height;
        dc.SetFont(getDefaultFont(data.text_size));
        dc.SetTextForeground(data.text_color);
        dc.GetTextExtent(data.text, &width, &height);
        size.x = (width > size.x ? width : size.x);
        size.y = (height > size.y ? height : size.y);
    }
}

void MugcupGraph::updateGuideXOffset(wxDC & dc, int & right, int & bottom)
{
    dc.SetFont(getDefaultFont(_guide_x.text_size));
    dc.SetTextForeground(_guide_x.text_color);

    wxString str1, str2;
    if (_guide_x.integer_text) {
        str1 = wxString::Format(_integer_format, (int)_guide_x.print_end);
        str2 = wxString::Format(_integer_format, (int)_guide_x.print_begin);
    } else {
        str1 = wxString::Format(_floating_format, _guide_x.print_end);
        str2 = wxString::Format(_floating_format, _guide_x.print_begin);
    }

    wxCoord width1, width2, height1, height2;
    dc.GetTextExtent(str1, &width1, &height1);
    dc.GetTextExtent(str2, &width2, &height2);
    right  -= ((width1 > width2 ? width1 : width2) / 2);
    bottom -= (height1 > height2 ? height1 : height2) + (_step_padding * 2);
}

void MugcupGraph::updateGuideYOffset(wxDC & dc, int & left, int & top)
{
    dc.SetFont(getDefaultFont(_guide_y.text_size));
    dc.SetTextForeground(_guide_y.text_color);

    wxString str1, str2;
    if (_guide_y.integer_text) {
        str1 = wxString::Format(_integer_format, (int)_guide_y.print_end);
        str2 = wxString::Format(_integer_format, (int)_guide_y.print_begin);
    } else {
        str1 = wxString::Format(_floating_format, _guide_y.print_end);
        str2 = wxString::Format(_floating_format, _guide_y.print_begin);
    }

    wxCoord width1, width2, height1, height2;
    dc.GetTextExtent(str1, &width1, &height1);
    dc.GetTextExtent(str2, &width2, &height2);
    left += (width1 > width2 ? width1 : width2) + (_step_padding * 2);
    top  += ((height1 > height2 ? height1 : height2) / 2);
}

void MugcupGraph::drawGuide(wxDC & dc, Guide const & guide,
                            int graph_begin, int graph_end, int line_begin, int line_end,
                            GuideDirection direction)
{
    assert(guide.print_begin < guide.print_end);
    assert(guide.print_step > 0);

    dc.SetPen(wxPen(guide.line_color, guide.line_width));
    dc.SetFont(getDefaultFont(guide.text_size));
    dc.SetTextForeground(guide.text_color);

    double const PRINT_WIDTH = guide.print_end - guide.print_begin;
    double const PRINT_STEP  = guide.print_step;
    if (PRINT_WIDTH != 0) {

        double const GRAPH_WIDTH = graph_end - graph_begin;
        double const RATE_OF_PRINT_TO_GRAPH = GRAPH_WIDTH / PRINT_WIDTH;
        double const GRAPH_STEP = PRINT_STEP * RATE_OF_PRINT_TO_GRAPH;

        wxRect label_area;
        if (guide.show_begin) {
            drawGuideLine(dc, guide.show_text, guide.integer_text, direction,
                          guide.print_begin, graph_begin, line_begin, line_end, label_area);
        }

        double const DATA_OFFSET = guide.print_begin - guide.start_data;
        double const FIRST_PRINT_STEP = guide.start_data + (static_cast<int>(DATA_OFFSET / PRINT_STEP) * PRINT_STEP);

        double value_cursor = FIRST_PRINT_STEP;
        while (value_cursor <= guide.print_end) {
            if (value_cursor >= guide.print_begin) {
                auto graph_x = graph_begin + static_cast<int>((value_cursor - guide.print_begin) * RATE_OF_PRINT_TO_GRAPH);
                drawGuideLine(dc, guide.show_text, guide.integer_text, direction,
                              value_cursor, graph_x, line_begin, line_end, label_area);
            }
            value_cursor += PRINT_STEP;
        }

        if (guide.show_end) {
            drawGuideLine(dc, guide.show_text, guide.integer_text, direction,
                          guide.print_end, graph_end, line_begin, line_end, label_area);
        }
    }
}

void MugcupGraph::drawGuideLine(wxDC & dc, bool show_text, bool integer_text, GuideDirection direction,
                                double text_value, int graph_value, int line_begin, int line_end,
                                wxRect & prev_drawing_label)
{
    if (show_text) {
        wxString str;
        if (integer_text) {
            str = wxString::Format(_integer_format, (int)text_value);
        } else {
            str = wxString::Format(_floating_format, text_value);
        }

        wxCoord width, height;
        dc.GetTextExtent(str, &width, &height);

        wxRect temp_area;
        if (direction == GuideDirection::GD_HORIZONTAL) {
            temp_area.x = graph_value - (width / 2);
            temp_area.y = line_end + _step_padding;
        } else {
            assert(direction == GuideDirection::GD_VERTICAL);
            temp_area.x = line_begin - _step_padding - width;
            temp_area.y = graph_value - (height / 2);
        }
        temp_area.width = width;
        temp_area.height = height;
        if (prev_drawing_label.Intersects(temp_area) == false) {
            dc.DrawText(str, temp_area.x, temp_area.y);
            prev_drawing_label = temp_area;
        }
    }

    if (direction == GuideDirection::GD_HORIZONTAL) {
        dc.DrawLine(graph_value, line_begin, graph_value, line_end);
    } else {
        assert(direction == GuideDirection::GD_VERTICAL);
        dc.DrawLine(line_begin, graph_value, line_end, graph_value);
    }
}

void MugcupGraph::drawPoints(wxDC & dc, wxPoint const & lt, wxPoint const & rb)
{
    double const PRINT_WIDTH  = _guide_x.print_end - _guide_x.print_begin;
    double const PRINT_HEIGHT = _guide_y.print_end - _guide_y.print_begin;
    if (PRINT_WIDTH != 0 && PRINT_HEIGHT != 0) {
        int const GRAPH_X_BEGIN = lt.x;
        int const GRAPH_Y_BEGIN = lt.y;

        double const GRAPH_WIDTH  = rb.x - lt.x;
        double const GRAPH_HEIGHT = rb.y - lt.y;

        double const  WIDTH_RATE_OF_PRINT_TO_GRAPH = GRAPH_WIDTH  / PRINT_WIDTH;
        double const HEIGHT_RATE_OF_PRINT_TO_GRAPH = GRAPH_HEIGHT / PRINT_HEIGHT;

        double const  WIDTH_DATA_OFFSET = _guide_x.print_begin - _guide_x.start_data;
        double const HEIGHT_DATA_OFFSET = _guide_y.print_begin - _guide_y.start_data;

        std::vector<wxPoint> points;
        for (auto & data : _datas) {
            if (!data.show) {
                continue;
            }

            points.clear();

            for (auto & point : data.points) {
                auto x = point.x;
                auto y = point.y;

                if (_guide_x.print_begin <= COMPARE_AND(x) <= _guide_x.print_end &&
                    _guide_y.print_begin <= COMPARE_AND(y) <= _guide_y.print_end) {

                    auto graph_x = GRAPH_X_BEGIN + static_cast<int>((x - _guide_x.print_begin) *  WIDTH_RATE_OF_PRINT_TO_GRAPH);
                    auto graph_y = GRAPH_Y_BEGIN + static_cast<int>((y - _guide_y.print_begin) * HEIGHT_RATE_OF_PRINT_TO_GRAPH);
                    points.emplace_back(graph_x, graph_y);
                }
            }

            dc.SetPen(wxPen(data.line_color, data.line_width));
            dc.DrawLines(static_cast<int>(points.size()), points.data());
        }
    }
}

void MugcupGraph::drawDebuggingRect(wxDC & dc, wxRect const & rect)
{
    dc.SetPen(wxPen(wxColour(DEBUGGING_COLOR)));
    dc.SetBrush(*wxTRANSPARENT_BRUSH);
    dc.DrawRectangle(rect);
}

// ----------------------
NAMESPACE_LIBMUGCUP_CLOSE
// ----------------------

See also