Repository navigation
Expand file tree
/
Copy pathpy_pipeline.cpp
More file actions
177 lines (152 loc) · 7.51 KB
/
Copy pathpy_pipeline.cpp
File metadata and controls
177 lines (152 loc) · 7.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* SPDX-FileCopyrightText: 2025 LichtFeld Studio Authors
*
* SPDX-License-Identifier: GPL-3.0-or-later */
#include "py_pipeline.hpp"
#include "visualizer/core/services.hpp"
#include "visualizer/operation/operation.hpp"
#include "visualizer/operation/ops/edit_ops.hpp"
#include "visualizer/operation/ops/select_ops.hpp"
#include "visualizer/operation/ops/transform_ops.hpp"
#include "visualizer/operation/pipeline.hpp"
#include <glm/glm.hpp>
#include <nanobind/stl/optional.h>
#include <nanobind/stl/string.h>
#include <nanobind/stl/vector.h>
#include <stdexcept>
namespace lfs::python {
namespace {
vis::op::OperatorProperties dict_to_props(const nb::dict& properties) {
vis::op::OperatorProperties props;
for (const auto& [key, value] : properties) {
const auto key_str = nb::cast<std::string>(key);
if (nb::isinstance<nb::float_>(value)) {
props.set(key_str, nb::cast<float>(value));
} else if (nb::isinstance<nb::int_>(value)) {
props.set(key_str, nb::cast<int>(value));
} else if (nb::isinstance<nb::bool_>(value)) {
props.set(key_str, nb::cast<bool>(value));
} else if (nb::isinstance<nb::str>(value)) {
props.set(key_str, nb::cast<std::string>(value));
} else if (nb::isinstance<nb::tuple>(value) || nb::isinstance<nb::list>(value)) {
const nb::sequence seq = nb::cast<nb::sequence>(value);
const auto len = nb::len(seq);
if (len == 3 && nb::isinstance<nb::float_>(seq[0])) {
props.set(key_str, glm::vec3(nb::cast<float>(seq[0]),
nb::cast<float>(seq[1]),
nb::cast<float>(seq[2])));
} else if (len == 16 && nb::isinstance<nb::float_>(seq[0])) {
glm::mat4 m;
for (int i = 0; i < 16; ++i) {
(&m[0][0])[i] = nb::cast<float>(seq[i]);
}
props.set(key_str, m);
}
}
}
return props;
}
class PyStage {
public:
vis::op::OperationFactory factory;
vis::op::OperatorProperties props;
PyStage(vis::op::OperationFactory f, vis::op::OperatorProperties p = {})
: factory(std::move(f)),
props(std::move(p)) {}
};
class PyPipeline {
public:
vis::op::Pipeline pipeline;
PyPipeline() = default;
explicit PyPipeline(std::string name)
: pipeline(std::move(name)) {}
PyPipeline& add(const PyStage& stage) {
pipeline.add(stage.factory, stage.props);
return *this;
}
PyPipeline& operator_or(const PyStage& stage) {
return add(stage);
}
nb::dict execute() {
auto* scene = vis::services().sceneOrNull();
if (!scene) {
nb::dict result;
result["ok"] = false;
result["error"] = "No scene available";
return result;
}
auto op_result = pipeline.execute(*scene);
nb::dict result;
result["ok"] = op_result.ok();
result["error"] = op_result.error;
return result;
}
bool poll() const {
auto* scene = vis::services().sceneOrNull();
if (!scene) {
return false;
}
return pipeline.poll(*scene);
}
};
template <typename OpClass>
PyStage make_stage(const nb::kwargs& kwargs) {
return PyStage(
[] { return std::make_unique<OpClass>(); },
kwargs ? dict_to_props(nb::dict(kwargs)) : vis::op::OperatorProperties{});
}
template <typename OpClass>
PyStage make_selection_iteration_stage(const nb::kwargs& kwargs) {
if (kwargs && kwargs.contains("iterations") && nb::cast<int>(kwargs["iterations"]) <= 0)
throw std::invalid_argument("iterations must be positive");
return make_stage<OpClass>(kwargs);
}
} // namespace
void register_pipeline(nb::module_& m) {
auto pipe = m.def_submodule("pipeline", "Compositional operations system");
nb::class_<PyStage>(pipe, "Stage")
.def(
"__or__", [](const PyStage& a, const PyStage& b) {
PyPipeline p;
p.add(a);
p.add(b);
return p;
},
"Chain two stages into a pipeline")
.def("execute", [](const PyStage& s) {
PyPipeline p;
p.add(s);
return p.execute(); }, "Execute this stage immediately");
nb::class_<PyPipeline>(pipe, "Pipeline")
.def(nb::init<>())
.def(nb::init<std::string>(), nb::arg("name"), "Create a named pipeline")
.def("add", &PyPipeline::add, nb::rv_policy::reference, "Append a stage to the pipeline")
.def("__or__", &PyPipeline::operator_or, nb::rv_policy::reference, "Append a stage via pipe operator")
.def("execute", &PyPipeline::execute, "Execute all stages and return result dict")
.def("poll", &PyPipeline::poll, "Check if all stages can execute");
auto select = pipe.def_submodule("select", "Selection operations");
select.def(
"all", [](nb::kwargs kwargs) { return make_stage<vis::op::SelectAll>(kwargs); }, "Create select-all stage");
select.def(
"none", [](nb::kwargs kwargs) { return make_stage<vis::op::SelectNone>(kwargs); }, "Create deselect-all stage");
select.def(
"invert", [](nb::kwargs kwargs) { return make_stage<vis::op::SelectInvert>(kwargs); }, "Create invert-selection stage");
select.def(
"grow", [](nb::kwargs kwargs) { return make_selection_iteration_stage<vis::op::SelectGrow>(kwargs); }, "Create grow-selection stage");
select.def(
"shrink", [](nb::kwargs kwargs) { return make_selection_iteration_stage<vis::op::SelectShrink>(kwargs); }, "Create shrink-selection stage");
auto edit = pipe.def_submodule("edit", "Edit operations");
edit.def(
"delete_", [](nb::kwargs kwargs) { return make_stage<vis::op::EditDelete>(kwargs); }, "Create delete stage");
edit.def(
"duplicate", [](nb::kwargs kwargs) { return make_stage<vis::op::EditDuplicate>(kwargs); }, "Create duplicate stage");
auto transform = pipe.def_submodule("transform", "Transform operations");
transform.def(
"translate", [](nb::kwargs kwargs) { return make_stage<vis::op::TransformTranslate>(kwargs); }, "Create translation stage");
transform.def(
"rotate", [](nb::kwargs kwargs) { return make_stage<vis::op::TransformRotate>(kwargs); }, "Create rotation stage");
transform.def(
"scale", [](nb::kwargs kwargs) { return make_stage<vis::op::TransformScale>(kwargs); }, "Create scale stage");
transform.def(
"set", [](nb::kwargs kwargs) { return make_stage<vis::op::TransformSet>(kwargs); }, "Create set-transform stage");
}
} // namespace lfs::python