载入中...
搜索中...
未找到
ArchSpaceEditingCommands.cpp
浏览该文件的文档.
2
5
6#include <cmath>
7#include <cstdint>
8#include <string>
9
11namespace {
12
13const editing::Value* field(const editing::Value& value, const char* key) {
14 const auto* object = value.getIf<editing::Value::Object>();
15 if (!object) return nullptr;
16 const auto found = object->find(key);
17 return found == object->end() ? nullptr : &found->second;
18}
19
20const std::string* stringField(const editing::Value& value, const char* key) {
21 const editing::Value* entry = field(value, key);
22 return entry ? entry->getIf<std::string>() : nullptr;
23}
24
25bool readNumber(const editing::Value& value, const char* key, double& output, bool required) {
26 const editing::Value* entry = field(value, key);
27 if (!entry) return !required;
28 if (const auto* real = entry->getIf<double>()) {
29 output = *real;
30 return std::isfinite(output);
31 }
32 if (const auto* integer = entry->getIf<std::int64_t>()) {
33 output = static_cast<double>(*integer);
34 return true;
35 }
36 return false;
37}
38
39editing::Result<void> addCommand(editing::IEditingCommandRegistry& registry, const char* id, const char* displayName,
43 descriptor.ownerModule = "archspace_editing";
44 descriptor.displayName = displayName;
45 descriptor.category = "ArchSpace";
46 descriptor.automationAllowed = true;
47 return registry.registerPlannedCommand(std::move(descriptor), std::move(planner));
48}
49
51 return dynamic_cast<ArchSpaceDocumentTarget*>(&target);
52}
53
55 editing::Value summary) {
56 if (!operation.ok())
57 return eve::editing::failed<editing::CommandPlan>(operation.code(),
58 editing::RuleId("archspace.editing.operation"),
59 "ArchSpace target rejected the planned edit");
61 plan.operations.push_back(std::move(operation).value());
62 plan.summary = std::move(summary);
63 return eve::editing::applied<editing::CommandPlan>(std::move(plan));
64}
65
66} // namespace
67
69 auto registered = addCommand(registry, "archspace.bootstrap.v1", "Bootstrap ArchSpace site",
71 auto* doc = asArchSpace(target);
72 const std::string* site = stringField(request.payload, "siteId");
73 const std::string* building = stringField(request.payload, "buildingId");
74 const std::string* level = stringField(request.payload, "levelId");
75 double height = 3.0;
76 if (!doc || !site || site->empty() || !building || building->empty() || !level ||
77 level->empty() || !readNumber(request.payload, "levelHeight", height, false))
78 return eve::editing::failed<editing::CommandPlan>(
79 editing::Status::Rejected, editing::RuleId("archspace.editing.bootstrap"),
80 "ArchSpace bootstrap requires siteId, buildingId and levelId");
81 return planFrom(doc->makeBootstrap(*site, *building, *level, height),
82 editing::Value::Object{{"levelId", *level}});
83 });
84 if (!registered.ok()) return registered;
85
86 registered = addCommand(
87 registry, "archspace.room.create.v1", "Create ArchSpace rectangular room",
89 auto* doc = asArchSpace(target);
90 const std::string* level = stringField(request.payload, "levelId");
91 const std::string* room = stringField(request.payload, "roomId");
92 const std::string* name = stringField(request.payload, "name");
93 double originX = 0, originZ = 0, sizeX = 0, sizeZ = 0, wallHeight = 3, wallThickness = 0.2,
94 slabThickness = 0.2;
95 if (!doc || !level || level->empty() || !room || room->empty() ||
96 !readNumber(request.payload, "originX", originX, true) ||
97 !readNumber(request.payload, "originZ", originZ, true) ||
98 !readNumber(request.payload, "sizeX", sizeX, true) ||
99 !readNumber(request.payload, "sizeZ", sizeZ, true) ||
100 !readNumber(request.payload, "wallHeight", wallHeight, false) ||
101 !readNumber(request.payload, "wallThickness", wallThickness, false) ||
102 !readNumber(request.payload, "slabThickness", slabThickness, false))
103 return eve::editing::failed<editing::CommandPlan>(
104 editing::Status::Rejected, editing::RuleId("archspace.editing.room"),
105 "ArchSpace room create requires levelId, roomId and size");
106 return planFrom(doc->makeCreateRectRoom(*level, *room, name ? *name : *room, originX, originZ, sizeX, sizeZ,
107 wallHeight, wallThickness, slabThickness),
108 editing::Value::Object{{"roomId", *room}});
109 });
110 if (!registered.ok()) return registered;
111
112 registered = addCommand(
113 registry, "archspace.wall.create.v1", "Create ArchSpace wall",
114 [](editing::IEditableTarget& target, const editing::CommandRequest& request) {
115 auto* doc = asArchSpace(target);
116 const std::string* level = stringField(request.payload, "levelId");
117 const std::string* wall = stringField(request.payload, "wallId");
118 const std::string* name = stringField(request.payload, "name");
119 double x0 = 0, z0 = 0, x1 = 0, z1 = 0, height = 3.0, thickness = 0.2;
120 if (!doc || !level || level->empty() || !wall || wall->empty() ||
121 !readNumber(request.payload, "startX", x0, true) ||
122 !readNumber(request.payload, "startZ", z0, true) ||
123 !readNumber(request.payload, "endX", x1, true) || !readNumber(request.payload, "endZ", z1, true) ||
124 !readNumber(request.payload, "height", height, false) ||
125 !readNumber(request.payload, "thickness", thickness, false))
126 return eve::editing::failed<editing::CommandPlan>(
127 editing::Status::Rejected, editing::RuleId("archspace.editing.wall"),
128 "ArchSpace wall create requires levelId, wallId and endpoints");
129 return planFrom(doc->makeCreateWall(*level, *wall, name ? *name : *wall, archspace::Vec2{x0, z0},
130 archspace::Vec2{x1, z1}, height, thickness),
131 editing::Value::Object{{"wallId", *wall}});
132 });
133 if (!registered.ok()) return registered;
134
135 registered = addCommand(
136 registry, "archspace.opening.create.v1", "Create ArchSpace opening",
137 [](editing::IEditableTarget& target, const editing::CommandRequest& request) {
138 auto* doc = asArchSpace(target);
139 const std::string* wall = stringField(request.payload, "wallId");
140 const std::string* opening = stringField(request.payload, "openingId");
141 const std::string* kindText = stringField(request.payload, "kind");
142 double t = 0.5, width = 0.9, height = 2.1, sill = 0.0;
143 if (!doc || !wall || wall->empty() || !opening || opening->empty() || !kindText ||
144 !readNumber(request.payload, "t", t, false) || !readNumber(request.payload, "width", width, false) ||
145 !readNumber(request.payload, "height", height, false) ||
146 !readNumber(request.payload, "sill", sill, false))
147 return eve::editing::failed<editing::CommandPlan>(
148 editing::Status::Rejected, editing::RuleId("archspace.editing.opening"),
149 "ArchSpace opening requires wallId, openingId and kind");
150 auto kind = archspace::parseOpeningKind(*kindText);
151 if (!kind.ok())
152 return eve::editing::failed<editing::CommandPlan>(
153 editing::Status::Rejected, editing::RuleId("archspace.editing.opening"),
154 "ArchSpace opening requires wallId, openingId and kind");
155 return planFrom(doc->makeCreateOpening(*wall, *opening, kind.value(), t, width, height, sill),
156 editing::Value::Object{{"openingId", *opening}});
157 });
158 if (!registered.ok()) return registered;
159
160 registered = addCommand(
161 registry, "archspace.item.place.v1", "Place ArchSpace item",
162 [](editing::IEditableTarget& target, const editing::CommandRequest& request) {
163 auto* doc = asArchSpace(target);
164 const std::string* level = stringField(request.payload, "levelId");
165 const std::string* item = stringField(request.payload, "itemId");
166 const std::string* catalog = stringField(request.payload, "catalogId");
167 double x = 0, y = 0, z = 0, yaw = 0;
168 if (!doc || !level || level->empty() || !item || item->empty() || !catalog || catalog->empty() ||
169 !readNumber(request.payload, "x", x, true) || !readNumber(request.payload, "y", y, false) ||
170 !readNumber(request.payload, "z", z, true) || !readNumber(request.payload, "yawDegrees", yaw, false))
171 return eve::editing::failed<editing::CommandPlan>(
172 editing::Status::Rejected, editing::RuleId("archspace.editing.item"),
173 "ArchSpace item place requires levelId, itemId, catalogId, x, z");
174 return planFrom(doc->makePlaceItem(*level, *item, *catalog, archspace::Vec3{x, y, z}, yaw),
175 editing::Value::Object{{"itemId", *item}});
176 });
177 if (!registered.ok()) return registered;
178
179 registered = addCommand(
180 registry, "archspace.property.set.v1", "Set ArchSpace property",
181 [](editing::IEditableTarget& target, const editing::CommandRequest& request) {
182 auto* doc = asArchSpace(target);
183 const std::string* object = stringField(request.payload, "objectId");
184 const std::string* path = stringField(request.payload, "path");
185 const editing::Value* value = field(request.payload, "value");
186 if (!doc || !object || object->empty() || !path || path->empty() || !value)
187 return eve::editing::failed<editing::CommandPlan>(
188 editing::Status::Rejected, editing::RuleId("archspace.editing.property"),
189 "ArchSpace property set requires objectId, path and value");
190 editing::SelectionSnapshot selection;
191 selection.channel = "archspace";
192 selection.items.push_back({editing::SelectionDomain::Asset, doc->targetId(), editing::StableId(*object),
193 std::string("archspace.node")});
194 return planFrom(doc->makeSet(selection, editing::PropertyPath(*path), *value,
195 editing::PropertySetMode::Absolute),
196 editing::Value::Object{{"objectId", *object}, {"path", *path}});
197 });
198 if (!registered.ok()) return registered;
199
200 return addCommand(registry, "archspace.node.delete.v1", "Delete ArchSpace node",
201 [](editing::IEditableTarget& target, const editing::CommandRequest& request) {
202 auto* doc = asArchSpace(target);
203 const std::string* id = stringField(request.payload, "id");
204 if (!doc || !id || id->empty())
205 return eve::editing::failed<editing::CommandPlan>(
206 editing::Status::Rejected, editing::RuleId("archspace.editing.delete"),
207 "ArchSpace delete requires id");
208 return planFrom(doc->makeDeleteNode(editing::ObjectId(*id)),
209 editing::Value::Object{{"id", *id}});
210 });
211}
212
213} // namespace eve::archspace_editing
LogicalId target
ActionParameterOperation operation
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string output
std::string descriptor
float thickness
Definition CaveMesh.cpp:83
const GltfImportRequest & request
std::uint32_t key
std::uint32_t height
std::uint32_t width
TokenKind kind
bool required
std::string name
int level
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
float t
bool found
Json object
Move-only operation result carrying either a value or Status.
Definition Result.h:155
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
Revisioned ArchSpace document target for Inspector, tools and automation.
IEditableTarget public API.
Lower-layer registration boundary implemented by an editor or runtime editing host.
virtual Result< void > registerPlannedCommand(EditingCommandDescriptor descriptor, EditingCommandPlanner planner)=0
Register or replace one domain-owned planned command.
Deterministic owning value tree shared by authoring hosts.
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
editing::Result< void > registerEditingCommands(editing::IEditingCommandRegistry &registry)
Register ArchSpace-owned planned commands with a generic editing host.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
std::function< Result< CommandPlan >(IEditableTarget &, const CommandRequest &)> EditingCommandPlanner
Side-effect-free planner invoked with the resolved target.
StrongId< CommandIdTag > CommandId
Definition EditingIds.h:58
const EditorValue * field(const EditorValue &value, const char *name)
bool readNumber(const EditorValue &value, const char *name, float &output)
CommandPlan public API.
std::vector< DomainOperation > operations
CommandRequest public API.
Domain-owned metadata for one planned editing command.