FuncSketch
Loading...
Searching...
No Matches
func_sketch_module.cpp
1#include <cstdint>
2#include <utility>
3#include <vector>
4
5#include <fmt/format.h>
6#include <nanobind/nanobind.h>
7#include <nanobind/ndarray.h>
8#include <nanobind/stl/pair.h>
9#include <nanobind/stl/string.h>
10#include <nanobind/stl/vector.h>
11
21
22namespace {
23
28struct PointList {
30 std::vector<func_sketch::plotter::Point> points;
31};
32
42using RawImage = nanobind::ndarray<uint8_t, nanobind::shape<-1, -1, 3>,
43 nanobind::c_contig, nanobind::device::cpu>;
44
54func_sketch::plotter::Image to_image(const RawImage& raw_image) {
55 return func_sketch::plotter::Image(static_cast<int>(raw_image.shape(0)),
56 static_cast<int>(raw_image.shape(1)), CV_8UC3, raw_image.data());
57}
58
59} // namespace
60
61// NOLINTNEXTLINE(*-identifier-length,*-vararg,*-c-arrays,*-array-decay,*-value-param): external library.
62NB_MODULE(_cpp, m) {
63 using nanobind::literals::operator""_a;
64
65 m.doc() = "C++ module for func_sketch";
66
68 nanobind::class_<ExpressionPtr>(m, "Expression", "Class of expressions.")
69 .def(
70 "__str__",
71 [](const ExpressionPtr& self) { return fmt::format("{}", *self); },
72 "Format the expression as a string.");
73
75 nanobind::class_<ExpressionParser>(
76 m, "ExpressionParser", R"(Class of parser for expressions.
77
78Objects of this class can be called with a string to parse it into an Expression object.)")
79 .def(nanobind::init<>(), "Constructor.")
80 .def(
81 "__call__",
82 [](const ExpressionParser& self,
83 const std::string& expression_str) {
84 return self(expression_str);
85 },
86 "expression_str"_a, "Parse a string into an Expression object.");
87
89 nanobind::class_<RGBColor>(m, "RGBColor", "Class of RGB colors.")
90 .def(nanobind::init<std::uint8_t, std::uint8_t, std::uint8_t>(), "r"_a,
91 "g"_a, "b"_a, "Constructor.")
92 .def_rw("r", &RGBColor::r, "Red component.")
93 .def_rw("g", &RGBColor::g, "Green component.")
94 .def_rw("b", &RGBColor::b, "Blue component.");
95
97 nanobind::class_<Point>(m, "Point", "Class of points.")
98 .def(nanobind::init<double, double>(), "x"_a, "y"_a, "Constructor.")
99 .def_rw("x", &Point::x, "X coordinate.")
100 .def_rw("y", &Point::y, "Y coordinate.");
101
102 nanobind::class_<PointList>(m, "PointList", "Class of lists of points.")
103 .def(nanobind::init<std::vector<Point>>(), "points"_a, "Constructor.")
104 .def_rw("points", &PointList::points, "List of points.");
105
107 nanobind::class_<PlotRange>(m, "PlotRange", "Class of ranges of plots.")
108 .def(nanobind::init<std::pair<double, double>,
109 std::pair<double, double>>(),
110 "x_range"_a, "y_range"_a, "Constructor.")
111 .def_prop_ro("x_range", &PlotRange::x_range, "Range of x-axis.")
112 .def_prop_ro("y_range", &PlotRange::y_range, "Range of y-axis.");
113
115 nanobind::class_<PlotConfig>(
116 m, "PlotConfig", "Class of configurations of plots.")
117 .def(nanobind::init<>(), "Constructor.")
118 .def_prop_rw(
119 "left_margin",
120 [](const PlotConfig& self) -> int { return self.left_margin(); },
121 [](PlotConfig& self, int value) { self.left_margin(value); },
122 "Left margin of plots in pixels.")
123 .def_prop_rw(
124 "right_margin",
125 [](const PlotConfig& self) -> int { return self.right_margin(); },
126 [](PlotConfig& self, int value) { self.right_margin(value); },
127 "Right margin of plots in pixels.")
128 .def_prop_rw(
129 "top_margin",
130 [](const PlotConfig& self) -> int { return self.top_margin(); },
131 [](PlotConfig& self, int value) { self.top_margin(value); },
132 "Top margin of plots in pixels.")
133 .def_prop_rw(
134 "bottom_margin",
135 [](const PlotConfig& self) -> int { return self.bottom_margin(); },
136 [](PlotConfig& self, int value) { self.bottom_margin(value); },
137 "Bottom margin of plots in pixels.")
138 .def_prop_rw(
139 "tick_label_font_size",
140 [](const PlotConfig& self) -> int {
141 return self.tick_label_font_size();
142 },
143 [](PlotConfig& self, int value) {
144 self.tick_label_font_size(value);
145 },
146 "Font size of tick labels in pixels.")
147 .def_prop_rw(
148 "axes_line_width",
149 [](const PlotConfig& self) -> int {
150 return self.axes_line_width();
151 },
152 [](PlotConfig& self, int value) { self.axes_line_width(value); },
153 "Line width of axes in pixels.")
154 .def_prop_rw(
155 "grid_line_width",
156 [](const PlotConfig& self) -> int {
157 return self.grid_line_width();
158 },
159 [](PlotConfig& self, int value) { self.grid_line_width(value); },
160 "Line width of grid lines in pixels.")
161 .def_prop_rw(
162 "zero_line_width",
163 [](const PlotConfig& self) -> int {
164 return self.zero_line_width();
165 },
166 [](PlotConfig& self, int value) { self.zero_line_width(value); },
167 "Line width of the grid line at zero in pixels.")
168 .def_prop_rw(
169 "curve_line_width",
170 [](const PlotConfig& self) -> int {
171 return self.curve_line_width();
172 },
173 [](PlotConfig& self, int value) { self.curve_line_width(value); },
174 "Line width of curves in pixels.")
175 .def_prop_rw(
176 "background_color",
177 [](const PlotConfig& self) -> RGBColor {
178 return self.background_color();
179 },
180 [](PlotConfig& self, const RGBColor& value) {
181 self.background_color(value);
182 },
183 "Color of background.")
184 .def_prop_rw(
185 "axes_color",
186 [](const PlotConfig& self) -> RGBColor {
187 return self.axes_color();
188 },
189 [](PlotConfig& self, const RGBColor& value) {
190 self.axes_color(value);
191 },
192 "Color of axes.")
193 .def_prop_rw(
194 "grid_color",
195 [](const PlotConfig& self) -> RGBColor {
196 return self.grid_color();
197 },
198 [](PlotConfig& self, const RGBColor& value) {
199 self.grid_color(value);
200 },
201 "Color of grid lines.")
202 .def_prop_rw(
203 "initial_num_sample_points",
204 [](const PlotConfig& self) -> std::size_t {
205 return self.initial_num_sample_points();
206 },
207 [](PlotConfig& self, std::size_t value) {
208 self.initial_num_sample_points(value);
209 },
210 "Number of points to sample initially in adaptive sampling.")
211 .def_prop_rw(
212 "max_num_sample_points",
213 [](const PlotConfig& self) -> std::size_t {
214 return self.max_num_sample_points();
215 },
216 [](PlotConfig& self, std::size_t value) {
217 self.max_num_sample_points(value);
218 },
219 "Maximum number of points to sample in adaptive sampling.\n\n"
220 "Note:\n"
221 " This value should be larger than "
222 "initial_num_sample_points. Otherwise, this configuration has "
223 "no effect.\n\n"
224 "Note:\n"
225 " This value is limited for safety limit of memory "
226 "usage.")
227 .def_prop_rw(
228 "max_coordinate_change_rate",
229 [](const PlotConfig& self) -> double {
230 return self.max_coordinate_change_rate();
231 },
232 [](PlotConfig& self, double value) {
233 self.max_coordinate_change_rate(value);
234 },
235 "Threshold of the change in coordinates of sample points "
236 "relative to the plot range in adaptive sampling.")
237 .def_prop_rw(
238 "slope_change_threshold",
239 [](const PlotConfig& self) -> double {
240 return self.slope_change_threshold();
241 },
242 [](PlotConfig& self, double value) {
243 self.slope_change_threshold(value);
244 },
245 "Threshold of the change in slope normalized by the plot range "
246 "in adaptive sampling.")
247 .def_prop_rw(
248 "min_param_change_rate",
249 [](const PlotConfig& self) -> double {
250 return self.min_param_change_rate();
251 },
252 [](PlotConfig& self, double value) {
253 self.min_param_change_rate(value);
254 },
255 "Minimum rate of parameter change in adaptive sampling.\n\n"
256 "Note:\n"
257 " This value is limited for safety limit of memory "
258 "usage.")
259 .def_prop_rw(
260 "num_pixels_per_tick_in_x_axis",
261 [](const PlotConfig& self) -> std::size_t {
262 return self.num_pixels_per_tick_in_x_axis();
263 },
264 [](PlotConfig& self, std::size_t value) {
265 self.num_pixels_per_tick_in_x_axis(value);
266 },
267 "Number of pixels per tick in the x-axis.")
268 .def_prop_rw(
269 "num_pixels_per_tick_in_y_axis",
270 [](const PlotConfig& self) -> std::size_t {
271 return self.num_pixels_per_tick_in_y_axis();
272 },
273 [](PlotConfig& self, std::size_t value) {
274 self.num_pixels_per_tick_in_y_axis(value);
275 },
276 "Number of pixels per tick in the y-axis.");
277
279 nanobind::class_<FunctionSampler>(
280 m, "FunctionSampler", "Class to sample functions for plotting.")
281 .def(nanobind::init<PlotRange, PlotConfig>(), "range"_a, "config"_a,
282 "Constructor.")
283 .def_prop_rw(
284 "range",
285 // Making the getter nullptr causes an error in mypy, so we must
286 // write a getter that throws an exception instead.
287 [](FunctionSampler& self) -> PlotRange {
288 throw std::runtime_error("Property 'range' is write-only.");
289 },
290 [](FunctionSampler& self, const PlotRange& value) {
291 self.range(value);
292 },
293 "Range of plots. (write-only)")
294 .def_prop_rw(
295 "config",
296 [](FunctionSampler& self) -> PlotConfig {
297 throw std::runtime_error("Property 'config' is write-only.");
298 },
299 [](FunctionSampler& self, const PlotConfig& value) {
300 self.config(value);
301 },
302 "Configuration of plots. (write-only)")
303 .def(
304 "__call__",
305 [](const FunctionSampler& self, const ExpressionPtr& function) {
306 return PointList{self(*function)};
307 },
308 "function"_a, "Sample a function and return a list of points.");
309
311 nanobind::class_<Plotter>(m, "Plotter", "Class for plotting.")
312 .def(nanobind::init<PlotRange, PlotConfig>(), "range"_a, "config"_a,
313 "Constructor.")
314 .def_prop_rw(
315 "range",
316 [](Plotter& self) -> PlotRange {
317 throw std::runtime_error("Property 'range' is write-only.");
318 },
319 [](Plotter& self, const PlotRange& value) { self.range(value); },
320 "Range of plots. (write-only)")
321 .def_prop_rw(
322 "config",
323 [](Plotter& self) -> PlotConfig {
324 throw std::runtime_error("Property 'config' is write-only.");
325 },
326 [](Plotter& self, const PlotConfig& value) { self.config(value); },
327 "Configuration of plots. (write-only)")
328 .def(
329 "write_background",
330 [](Plotter& self, const RawImage& raw_image) {
331 auto image = to_image(raw_image);
332 self.write_background(image);
333 },
334 "image"_a,
335 "Write background of a plot.\n\n"
336 "The pixels of image are modified in place.")
337 .def(
338 "write_curve",
339 [](Plotter& self, const PointList& point_list,
340 const RGBColor& color, const RawImage& raw_image) {
341 auto image = to_image(raw_image);
342 self.write_curve(point_list.points, color, image);
343 },
344 "point_list"_a, "color"_a, "image"_a,
345 "Write a curve on a plot.\n\n"
346 "The pixels of image are modified in place.");
347}
Class of pointers to expressions with RAII.
Class of parser for expressions.
Class to sample functions for plotting.
Class of configurations of a plot.
Definition plot_config.h:97
Class of a range of a plot.
Definition plot_range.h:35
Class for plotting.
Definition plotter.h:36
Definition of ExpressionParser class.
Definition of ExpressionPtr class.
Definition of FunctionSampler class.
Definition of Image type.
cv::Mat Image
Image type.
Definition image.h:29
Definition of PlotConfig class.
Definition of PlotRange class.
Definition of Plotter class.
Definition of Point struct.
Definition of RGBColor struct.
Struct of a point.
Definition point.h:29
Struct of RGB color.
Definition rgb_color.h:31