diff --git a/examples/00_graphics/graphic_examples.py b/examples/00_graphics/graphic_examples.py index 97777710..ae9dcb45 100644 --- a/examples/00_graphics/graphic_examples.py +++ b/examples/00_graphics/graphic_examples.py @@ -37,7 +37,7 @@ DefaultView.set(fig1) DefaultView.draw_box([[2.2,2.5],[2.2,2.5]],[Color.blue(),Color.cyan(0.8)]) -fig2.draw_AUV([1,1,3.14/2],1.,[Color.black(),Color.yellow()]) +fig2.draw_AUV([1,1,3.14/2],2.,[Color.black(),Color.yellow()]) fig2.draw_tank([2,1,3.14/2],1.,[Color.black(),Color.yellow()]) fig2.draw_pie([2,2],[1.5,2.5],[(3*3.14/4)-0.5,(3*3.14/4)+0.5],[Color.blue(),Color.cyan()]) fig2.draw_polyline([[2,-0.5],[4,0.5],[3,1.5],[4,2.5],[3,3]], Color.red()) diff --git a/src/graphics/3rd/vibes/vibes.cpp b/src/graphics/3rd/vibes/vibes.cpp index 63038abd..e55a9c13 100644 --- a/src/graphics/3rd/vibes/vibes.cpp +++ b/src/graphics/3rd/vibes/vibes.cpp @@ -1,7 +1,7 @@ // This file is part of VIBes' C++ API // // Copyright (c) 2013-2015 Vincent Drevelle, Jeremy Nicola, Simon Rohou, -// Benoit Desrochers +// Benoit Desrochers, Maël Godard // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -28,6 +28,7 @@ #include #include #include +#include // // Vibes properties key,value system implementation @@ -94,7 +95,8 @@ namespace vibes namespace { /// Current communication file descriptor - FILE *channel=0; + // FILE *channel=nullptr; + std::shared_ptr channel; /// Current figure name (client-maintained state) string current_fig="default"; @@ -125,59 +127,83 @@ namespace vibes void beginDrawing(const std::string &fileName) { - channel=fopen(fileName.c_str(),"a"); + if (!channel) + channel = std::shared_ptr( + fopen(fileName.c_str(),"a"), + // Callback for automatically closing the file + // when the shared_ptr is released: + [](FILE* file) { + if(file) + fclose(file); + } + ); + } + + void beginDrawingIfNeeded() + { + if (!channel) + { + beginDrawing(); + } } void endDrawing() { - fclose(channel); + } + + // // Figure management // void newFigure(const std::string &figureName) { + beginDrawingIfNeeded(); std::string msg; if (!figureName.empty()) current_fig = figureName; msg ="{\"action\":\"new\"," "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void clearFigure(const std::string &figureName) { + beginDrawingIfNeeded(); std::string msg; msg="{\"action\":\"clear\"," "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void closeFigure(const std::string &figureName) { + beginDrawingIfNeeded(); std::string msg; msg="{\"action\":\"close\"," "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void saveImage(const std::string &fileName, const std::string &figureName) { - std::string msg; - msg="{\"action\":\"export\"," - "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"," - "\"file\":\""+fileName+"\"}\n\n"; - fputs(msg.c_str(),channel); - fflush(channel); + beginDrawingIfNeeded(); + std::string msg; + msg="{\"action\":\"export\"," + "\"figure\":\""+(figureName.empty()?current_fig:figureName)+"\"," + "\"file\":\""+fileName+"\"}\n\n"; + fputs(msg.c_str(),channel.get()); + fflush(channel.get()); } void selectFigure(const std::string &figureName) { - current_fig = figureName; + beginDrawingIfNeeded(); + current_fig = figureName; } @@ -187,17 +213,20 @@ namespace vibes void axisAuto(const std::string &figureName) { + beginDrawingIfNeeded(); setFigureProperty(figureName.empty()?current_fig:figureName, "viewbox", "auto"); } void axisLimits(const double &x_lb, const double &x_ub, const double &y_lb, const double &y_ub, const std::string &figureName) { + beginDrawingIfNeeded(); Vec4d v4d = { x_lb, x_ub, y_lb, y_ub }; setFigureProperty(figureName.empty()?current_fig:figureName, "viewbox", v4d); } void axisLabels(const std::string &x_label, const std::string &y_label, const std::string &figureName) { + beginDrawingIfNeeded(); vector labels; labels.push_back(x_label); labels.push_back(y_label); @@ -206,6 +235,7 @@ namespace vibes void axisLabels(const std::vector &labels, const std::string &figureName) { + beginDrawingIfNeeded(); setFigureProperty( figureName.empty()?current_fig:figureName, "axislabels", labels); } @@ -216,18 +246,20 @@ namespace vibes void drawBox(const double &x_lb, const double &x_ub, const double &y_lb, const double &y_ub, Params params) { + beginDrawingIfNeeded(); Vec4d v4d = { x_lb, x_ub, y_lb, y_ub }; Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "box", "bounds", v4d); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawBox(const vector &bounds, Params params) { + beginDrawingIfNeeded(); assert(!bounds.empty()); assert(bounds.size()%2 == 0); @@ -236,13 +268,14 @@ namespace vibes msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "box", "bounds", vector(bounds.begin(),bounds.end())); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawEllipse(const double &cx, const double &cy, const double &a, const double &b, const double &rot, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Vec2d va = {a, b}; Params msg; @@ -253,14 +286,15 @@ namespace vibes "axis", va, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawConfidenceEllipse(const double &cx, const double &cy, const double &sxx, const double &sxy, const double &syy, const double &K, Params params) { + beginDrawingIfNeeded(); Vec2d vc = {cx, cy}; Vec4d vcov = { sxx, sxy, sxy, syy }; Params msg; @@ -271,13 +305,14 @@ namespace vibes "covariance", vcov, "sigma", K); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawConfidenceEllipse(const vector ¢er, const vector &cov, const double &K, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); @@ -286,13 +321,14 @@ namespace vibes "covariance", vector(cov.begin(),cov.end()), "sigma", K); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawSector(const double &cx, const double &cy, const double &a, const double &b, const double &startAngle, const double &endAngle, Params params) { + beginDrawingIfNeeded(); // Angle need to be in degree Params msg; Vec2d cxy={ cx, cy }; @@ -306,13 +342,14 @@ namespace vibes "orientation", 0, "angles", startEnd); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPie(const double &cx, const double &cy, const double &r_min, const double &r_max, const double &theta_min, const double &theta_max, Params params) { + beginDrawingIfNeeded(); // Angle need to be in degree Params msg; Vec2d cxy = { cx, cy }; @@ -325,36 +362,39 @@ namespace vibes "rho", rMinMax, "theta", thetaMinMax); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPoint(const double &cx, const double &cy, Params params) { + beginDrawingIfNeeded(); Params msg; Vec2d cxy = { cx, cy }; msg["action"]="draw"; msg["figure"]=params.pop("figure",current_fig); msg["shape"]=(params, "type","point", "point",cxy); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPoint(const double &cx, const double &cy, const double &radius, Params params) { + beginDrawingIfNeeded(); Params msg; Vec2d cxy = { cx, cy }; msg["action"]="draw"; msg["figure"]=params.pop("figure",current_fig); msg["shape"]=(params, "type","point", "point",cxy,"Radius",radius); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawRing(const double &cx, const double &cy, const double &r_min, const double &r_max, Params params) { + beginDrawingIfNeeded(); Params msg; Vec2d cxy = { cx, cy }; Vec2d rMinMax = { r_min, r_max }; @@ -363,48 +403,52 @@ namespace vibes msg["shape"] = (params, "type", "ring", "center", cxy, "rho", rMinMax); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawBoxes(const std::vector > &bounds, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "boxes", "bounds", bounds); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawBoxesUnion(const std::vector > &bounds, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "boxes union", "bounds", bounds); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawLine(const std::vector > &points, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); msg["shape"] = (params, "type", "line", "points", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawLine(const std::vector &x, const std::vector &y, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -422,8 +466,8 @@ namespace vibes msg["shape"] = (params, "type", "line", "points", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } //void drawPoints(const std::vector > &points, Params params) @@ -433,8 +477,8 @@ namespace vibes // msg["figure"] = params.pop("figure",current_fig); // msg["shape"] = (params, "type", "points", // "points", points); - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} //void drawPoints(const std::vector > &points, const std::vector &colorLevels, const std::vector &radiuses, Params params) @@ -446,12 +490,13 @@ namespace vibes // "points", points, // "colorLevels", colorLevels, // "radiuses", radiuses); - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} void drawPoints(const std::vector &x, const std::vector &y, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -469,8 +514,8 @@ namespace vibes msg["shape"] = (params, "type", "points", "centers", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } //void drawPoints(const std::vector &x, const std::vector y, const std::vector &colorLevels, Params params) @@ -493,8 +538,8 @@ namespace vibes // "points", points, // "colorLevels", colorLevels); // - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} //void drawPoints(const std::vector &x, const std::vector y, const std::vector &colorLevels, const std::vector &radiuses, Params params) @@ -518,12 +563,13 @@ namespace vibes // "colorLevels", colorLevels, // "radiuses", radiuses); // - // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - // fflush(channel); + // fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + // fflush(channel.get()); //} void drawArrow(const double &xA, const double &yA, const double &xB, const double &yB, const double &tip_length, Params params) { + beginDrawingIfNeeded(); // Reshape A and B into a vector of points std::vector points; Vec2d va = { xA, yA }; @@ -539,12 +585,13 @@ namespace vibes "points", points, "tip_length", tip_length); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawArrow(const std::vector > &points, const double &tip_length, Params params) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "draw"; msg["figure"] = params.pop("figure",current_fig); @@ -552,12 +599,13 @@ namespace vibes "points", points, "tip_length", tip_length); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawArrow(const std::vector &x, const std::vector &y, const double &tip_length, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -576,12 +624,13 @@ namespace vibes "points", points, "tip_length", tip_length); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawPolygon(const std::vector &x, const std::vector &y, Params params) { + beginDrawingIfNeeded(); // Reshape x and y into a vector of points std::vector points; std::vector::const_iterator itx = x.begin(); @@ -599,12 +648,13 @@ namespace vibes msg["shape"] = (params, "type", "polygon", "bounds", points); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawVehicle(const double &cx, const double &cy, const double &rot, const double &length, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Params msg; msg["action"] = "draw"; @@ -614,12 +664,13 @@ namespace vibes "length", length, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawAUV(const double &cx, const double &cy, const double &rot, const double &length, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Params msg; msg["action"] = "draw"; @@ -629,12 +680,13 @@ namespace vibes "length", length, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawTank(const double &cx, const double &cy, const double &rot, const double &length, Params params) { + beginDrawingIfNeeded(); Vec2d vc = { cx, cy }; Params msg; msg["action"] = "draw"; @@ -644,12 +696,13 @@ namespace vibes "length", length, "orientation", rot); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void drawRaster(const std::string& rasterFilename, const double &xlb, const double &yub, const double &xres, const double &yres, Params params) { + beginDrawingIfNeeded(); Vec2d ul_corner = { xlb, yub }; Vec2d scale = { xres, yres }; @@ -662,13 +715,14 @@ namespace vibes "scale", scale ); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void newGroup(const std::string &name, Params params) { + beginDrawingIfNeeded(); // Send message Params msg; msg["action"] = "draw"; @@ -676,19 +730,20 @@ namespace vibes msg["shape"] = (params, "type", "group", "name", name); - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void clearGroup(const std::string &figureName, const std::string &groupName) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "clear"; msg["figure"] = figureName; msg["group"] = groupName; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void clearGroup(const std::string &groupName) @@ -699,13 +754,14 @@ namespace vibes void removeObject(const std::string &figureName, const std::string &objectName) { + beginDrawingIfNeeded(); Params msg; msg["action"] = "delete"; msg["figure"] = figureName; msg["object"] = objectName; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void removeObject(const std::string &objectName) @@ -716,23 +772,26 @@ namespace vibes // Property modification void setFigureProperties(const std::string &figureName, const Params &properties) { + beginDrawingIfNeeded(); // Send message Params msg; msg["action"] = "set"; msg["figure"] = figureName; msg["properties"] = properties; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void setFigureProperties(const Params &properties) { + beginDrawingIfNeeded(); setFigureProperties(current_fig, properties); } void setObjectProperties(const std::string &figureName, const std::string &objectName, const Params &properties) { + beginDrawingIfNeeded(); // Send message Params msg; msg["action"] = "set"; @@ -740,12 +799,12 @@ namespace vibes msg["object"] = objectName; msg["properties"] = properties; - fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel); - fflush(channel); + fputs(Value(msg).toJSONString().append("\n\n").c_str(), channel.get()); + fflush(channel.get()); } void setObjectProperties(const std::string &objectName, const Params &properties) { setObjectProperties(current_fig, objectName, properties); } -} +} \ No newline at end of file diff --git a/src/graphics/3rd/vibes/vibes.h b/src/graphics/3rd/vibes/vibes.h index 7fb859a9..bdfb094e 100644 --- a/src/graphics/3rd/vibes/vibes.h +++ b/src/graphics/3rd/vibes/vibes.h @@ -7,7 +7,7 @@ // This file is part of VIBes' C++ API // // Copyright (c) 2013-2015 Vincent Drevelle, Jeremy Nicola, Simon Rohou, -// Benoit Desrochers +// Benoit Desrochers, Maël Godard // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -221,9 +221,13 @@ namespace vibes { /// Start VIBes in file saving mode. All commands are saved to the specified file. void beginDrawing(const std::string &fileName); + /// Start VIBes in connected mode: connects to the VIBes viewer if needed. + void beginDrawingIfNeeded(); + /// Close connection to the viewer or the drawing file. void endDrawing(); + /** @} */ // end of group connection @@ -460,7 +464,7 @@ namespace vibes { * \param params Optional attributes * * Draws a circle of radius \a r with center at (\a cx , \a cy ). - * This functions internally calls drawEllipse + * This functions internally calls \fn drawEllipse */ inline void drawCircle(const double &cx, const double &cy, const double &r, Params params) { @@ -537,4 +541,4 @@ namespace vibes { #define vibesDrawCircle(cx,cy,r,...) vibes::drawCircle(cx,cy,r,vibesParams(__VA_ARGS__)) #endif //#ifdef VIBES_GENERATE_vibesXXX_MACROS -#endif //#ifndef VIBES_CPP_API_H +#endif //#ifndef VIBES_CPP_API_H \ No newline at end of file diff --git a/src/graphics/styles/codac2_Color.cpp b/src/graphics/styles/codac2_Color.cpp index 8e6c6a78..879e0c27 100644 --- a/src/graphics/styles/codac2_Color.cpp +++ b/src/graphics/styles/codac2_Color.cpp @@ -2,7 +2,7 @@ * codac2_Color.cpp * ---------------------------------------------------------------------------- * \date 2024 - * \author Simon Rohou + * \author Simon Rohou, Maël Godard * \copyright Copyright 2024 Codac Team * \license GNU Lesser General Public License (LGPL) */ diff --git a/src/graphics/styles/codac2_Color.h b/src/graphics/styles/codac2_Color.h index b3e3efd4..6d8a992c 100644 --- a/src/graphics/styles/codac2_Color.h +++ b/src/graphics/styles/codac2_Color.h @@ -2,7 +2,7 @@ * \file codac2_Color.h * ---------------------------------------------------------------------------- * \date 2024 - * \author Simon Rohou + * \author Simon Rohou, Maël Godard * \copyright Copyright 2024 Codac Team * \license GNU Lesser General Public License (LGPL) */