11const editing::Value*
field(
const editing::Value&
value,
const char*
key) {
13 if (!
object)
return nullptr;
14 const auto found =
object->find(
key);
15 return found ==
object->end() ? nullptr : &
found->second;
18const std::string* stringField(
const editing::Value&
value,
const char*
key) {
20 return entry ? entry->getIf<std::string>() : nullptr;
26 if (
const auto* integer = entry->getIf<int64_t>()) {
27 output =
static_cast<int>(*integer);
30 if (
const auto* real = entry->getIf<
double>()) {
31 output =
static_cast<int>(*real);
40 const auto* flag = entry->getIf<
bool>();
41 if (!flag)
return false;
46editing::Result<void> addCommand(editing::IEditingCommandRegistry& registry,
const char*
id,
const char* displayName,
54 return registry.registerPlannedCommand(std::move(
descriptor), std::move(planner));
57VoxelCatalogTarget* asCatalog(editing::IEditableTarget&
target) {
58 return dynamic_cast<VoxelCatalogTarget*
>(&
target);
61editing::SelectionSnapshot selectionFor(
const VoxelCatalogTarget&
target,
const std::string& item) {
62 editing::SelectionSnapshot selection;
63 selection.channel =
"voxel-catalog";
64 editing::SelectionItem
row;
69 selection.items.push_back(row);
70 selection.primary =
row;
74editing::Result<editing::CommandPlan> planFrom(editing::Result<editing::DomainOperation>
operation,
75 editing::Value summary) {
78 "Voxel catalog rejected the planned edit");
79 editing::CommandPlan plan;
80 plan.operations.push_back(std::move(
operation).takeValue());
81 plan.summary = std::move(summary);
82 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
88 auto registered = addCommand(
89 registry,
"voxel.model.create.v1",
"Create voxel model",
91 auto* catalog = asCatalog(
target);
92 const std::string*
id = stringField(
request.payload,
"id");
93 if (!catalog || !
id ||
id->empty())
94 return eve::editing::failed<editing::CommandPlan>(editing::Status::Rejected,
96 "Voxel model create requires id");
97 const std::string*
name = stringField(
request.payload,
"name");
101 if (!readInt(
request.payload,
"sizeX",
model.sizeX,
false) ||
102 !readInt(
request.payload,
"sizeY",
model.sizeY,
false) ||
104 return eve::editing::failed<editing::CommandPlan>(editing::Status::Rejected,
106 "Voxel model size must be integers");
109 if (!registered.ok())
return registered;
111 registered = addCommand(
112 registry,
"voxel.model.delete.v1",
"Delete voxel model",
114 auto* catalog = asCatalog(
target);
115 const std::string*
id = stringField(
request.payload,
"id");
116 if (!catalog || !
id ||
id->empty())
117 return eve::editing::failed<editing::CommandPlan>(editing::Status::Rejected,
119 "Voxel model delete requires id");
122 if (!registered.ok())
return registered;
124 registered = addCommand(
125 registry,
"voxel.model.voxel.set.v1",
"Set voxel occupancy",
126 [](editing::IEditableTarget&
target,
const editing::CommandRequest&
request) {
127 auto* catalog = asCatalog(
target);
128 const std::string* item = stringField(
request.payload,
"item");
129 int x = 0,
y = 0,
z = 0;
131 if (!catalog || !item || item->empty() || !readInt(
request.payload,
"x",
x,
true) ||
132 !readInt(
request.payload,
"y",
y,
true) || !readInt(
request.payload,
"z",
z,
true) ||
134 return eve::editing::failed<editing::CommandPlan>(
135 editing::Status::Rejected, editing::RuleId(
"voxel.editing.voxel-payload"),
136 "Voxel occupancy requires item, x, y, z and occupied");
137 return planFrom(catalog->makeSetVoxel(editing::ObjectId(*item),
x,
y,
z,
occupied),
138 editing::Value::Object{{
"item", *item}});
140 if (!registered.ok())
return registered;
143 registry,
"voxel.property.set.v1",
"Set voxel catalog property",
144 [](editing::IEditableTarget&
target,
const editing::CommandRequest&
request) {
145 auto* catalog = asCatalog(
target);
146 auto*
properties =
static_cast<editing::IPropertyProvider*
>(
147 target.queryCapability(VoxelCatalogTarget::propertyCapabilityId()));
148 const std::string* item = stringField(
request.payload,
"item");
149 const std::string*
path = stringField(
request.payload,
"path");
152 return eve::editing::failed<editing::CommandPlan>(editing::Status::Rejected,
153 editing::RuleId(
"voxel.editing.property-payload"),
154 "Voxel property requires item, path and value");
155 return planFrom(
properties->makeSet(selectionFor(*catalog, *item), editing::PropertyPath(*
path), *
value,
156 editing::PropertySetMode::Absolute),
157 editing::Value::Object{{
"item", *item}, {
"path", *
path}});
ActionParameterOperation operation
const GltfImportRequest & request
std::map< std::string, PropertyRule > properties
MagicaVoxel-style sculpted models: bounded occupancy grids and hull sockets.
Move-only operation result carrying either a value or Status.
UUID-backed identifier adapter for legacy textual boundaries.
IEditableTarget public API.
Lower-layer registration boundary implemented by an editor or runtime editing host.
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
StrongId< TargetIdTag > TargetId
StrongId< ObjectIdTag > ObjectId
std::function< Result< CommandPlan >(IEditableTarget &, const CommandRequest &)> EditingCommandPlanner
Side-effect-free planner invoked with the resolved target.
StrongId< StableIdTag > StableId
StrongId< RuleIdTag > RuleId
StrongId< CommandIdTag > CommandId
const EditorValue * field(const EditorValue &value, const char *name)
WidgetDesc row(std::vector< WidgetDesc > children, std::string id)
Horizontal elastic layout row.
editing::Result< void > registerEditingCommands(editing::IEditingCommandRegistry ®istry)
Register voxel-catalog planned commands with a generic editing host.
CommandRequest public API.
One MagicaVoxel-style object: a bounded occupancy grid.