Profactor / cv-plot

fast modular opencv plotting library
MIT License
154 stars 28 forks source link

How to add legend #11

Closed Babagui closed 3 years ago

Babagui commented 4 years ago

Hello, is there a way to add a legend to a graph ? Thanks.

wpalfi commented 4 years ago

Hi Babagui,

there is no legend in the library. But you can add one on your own. Here is an example:

struct LegendLabel :public Drawable {
    std::string _text;
    cv::Point _position;
    const int _fontFace = cv::FONT_HERSHEY_SIMPLEX;
    const double _fontScale = .4;
    const int _fontThickness = 1;
    cv::Scalar _color = cv::Scalar(0, 0, 0);
    void render(RenderTarget& renderTarget) override {
        int baseline;
        cv::Size size = cv::getTextSize(_text, _fontFace, _fontScale, _fontThickness, &baseline);
        auto pos = renderTarget.innerToOuter(renderTarget.project(_position)) + cv::Point2d(size.height * 2, size.height / 2);
        cv::putText(renderTarget.outerMat(), _text, pos, _fontFace, _fontScale, _color, _fontThickness, cv::LINE_AA);
    }
};

struct Legend :public Drawable {
    Axes* _parentAxes;
    int _width = 160;
    int _height = 70;
    int _margin = 20;
    void render(RenderTarget& renderTarget) override {
        std::vector<Series*> seriesVec;
        for (const auto& drawable : _parentAxes->drawables()) {
            auto series = dynamic_cast<Series*>(drawable.get());
            if (series) {
                seriesVec.push_back(series);
            }
        }
        Axes axes;
        axes.setMargins(5, _width - 2 * _margin - 60, 5, 5)
            .setXLim({ -.2,1.2 })
            .setYLim({ -.2,seriesVec.size() - 1 + .2 })
            .setYReverse();
        for (size_t i = 0; i < seriesVec.size(); i++) {
            auto& series = *seriesVec[i];
            axes.create<Series>(std::vector<cv::Point>{ {0,(int)i},{1,(int)i} })
                .setLineType(series.getLineType())
                .setLineWidth(series.getLineWidth())
                .setColor(series.getColor())
                .setMarkerType(series.getMarkerType())
                .setMarkerSize(series.getMarkerSize());
            auto& label = axes.create<LegendLabel>();
            label._position = { 1,(int)i };
            label._text = series.getName();
        }
        cv::Rect rect(renderTarget.innerMat().cols - _width - _margin, _margin, _width, _height);
        if (rect.x >= 0 && rect.x + rect.width < renderTarget.innerMat().cols && rect.y >= 0 && rect.y + rect.height < renderTarget.innerMat().rows) {
            axes.render(renderTarget.innerMat()(rect));
            cv::rectangle(renderTarget.innerMat(), rect, cv::Scalar::all(0));
        }
    }
};

std::vector<double> x(50), y1(x.size()), y2(x.size()), y3(x.size());
for (size_t i = 0; i < x.size(); i++) {
    x[i] = i * CV_2PI / x.size();
    y1[i] = std::sin(x[i]);
    y2[i] = y1[i] * std::sin(x[i]*10);
    y3[i] = -y1[i];
}
Axes axes = makePlotAxes();
axes.create<Series>(x, y3, "-g").setName("first");
axes.create<Series>(x, y2, "-b").setName("second");
axes.create<Series>(x, y1, "-or").setName("third").setLineWidth(3);
axes.title("Legend Example");
axes.create<Legend>()._parentAxes = &axes;
show("Legend Example", axes);

image

Babagui commented 3 years ago

Awesome ! Thank you very much ! :)