载入中...
搜索中...
未找到
SceneComponentPayload.cpp
浏览该文件的文档.
2
3#include <algorithm>
4
6namespace {
7
8} // namespace
9
11 : componentType_(std::move(componentType)) {}
12
14 SceneComponentPayloadRef component, SelectionItem moduleSelection,
16 if (componentType_.empty() || component.target.empty() || component.object.empty() ||
17 component.component.empty() || component.type != componentType_ || !properties || !operations ||
18 moduleSelection.target != TargetId(operations->targetId()))
19 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.scene.component-binding-invalid"),
20 "Component binding requires matching stable ids and module targets");
21 if (find(component.target, component.component))
22 return eve::editing::failed<void>(EditorStatus::Conflict, RuleId("editor.scene.component-binding-duplicate"),
23 "Scene component is already bound: " + component.component.value());
24 component.revision = operations->revision();
25 bindings_.push_back({std::move(component), std::move(moduleSelection), properties, operations,
26 std::move(validator)});
27 return eve::editing::applied<void>();
28}
29
30SceneComponentChange SceneComponentPropertyBindings::unbind(const TargetId& scene, const StableId& component) {
31 const auto found = std::find_if(bindings_.begin(), bindings_.end(), [&](const Binding& binding) {
32 return binding.component.target == scene && binding.component.component == component;
33 });
34 if (found == bindings_.end()) return SceneComponentChange::Unchanged;
35 bindings_.erase(found);
37}
38
39const SceneComponentPropertyBindings::Binding* SceneComponentPropertyBindings::find(
40 const TargetId& scene, const StableId& component) const {
41 const auto found = std::find_if(bindings_.begin(), bindings_.end(), [&](const Binding& binding) {
42 return binding.component.target == scene && binding.component.component == component;
43 });
44 return found == bindings_.end() ? nullptr : &*found;
45}
46
47std::vector<SceneComponentPayloadRef> SceneComponentPropertyBindings::components(
48 const TargetId& scene, const ObjectId& object) const {
49 std::vector<SceneComponentPayloadRef> result;
50 for (const Binding& binding : bindings_) {
51 if (binding.component.target != scene || binding.component.object != object) continue;
52 SceneComponentPayloadRef component = binding.component;
53 component.revision = binding.operations->revision();
54 result.push_back(std::move(component));
55 }
56 return result;
57}
58
60SceneComponentPropertyBindings::translate(const SelectionSnapshot& selection) const {
61 if (selection.items.empty())
62 return eve::editing::failed<std::pair<const Binding*, SelectionSnapshot>>(
63 EditorStatus::Rejected, RuleId("editor.scene.component-selection-empty"),
64 "Component property selection cannot be empty");
65 const Binding* first = find(selection.items.front().target, selection.items.front().item);
66 if (!first || selection.items.front().type != componentType_)
67 return eve::editing::failed<std::pair<const Binding*, SelectionSnapshot>>(
68 EditorStatus::NotFound, RuleId("editor.scene.component-binding-missing"),
69 "Selected scene component is not bound to this provider");
70 SelectionSnapshot translated;
71 translated.channel = selection.channel;
72 translated.sequence = selection.sequence;
73 for (const SelectionItem& item : selection.items) {
74 const Binding* binding = find(item.target, item.item);
75 if (!binding || item.type != componentType_ || binding->properties != first->properties ||
76 binding->operations != first->operations)
77 return eve::editing::failed<std::pair<const Binding*, SelectionSnapshot>>(
78 EditorStatus::Rejected, RuleId("editor.scene.component-binding-mixed-target"),
79 "Multi-edit requires components owned by one module property target");
80 translated.items.push_back(binding->moduleSelection);
81 }
82 translated.primary = translated.items.front();
83 return eve::editing::applied<std::pair<const Binding*, SelectionSnapshot>>(
84 {first, std::move(translated)});
85}
86
88 const SelectionSnapshot& selection) const {
89 auto translated = translate(selection);
90 if (!translated.ok()) return Result<IDomainOperationTarget*>::failure(translated.status());
91 return eve::editing::applied<IDomainOperationTarget*>(translated.value().first->operations);
92}
93
95 const SceneComponentPayloadRef& component) const {
96 const Binding* binding = find(component.target, component.component);
97 if (!binding)
99 eve::DiagnosticCode::NotFound, RuleId("editor.scene.component-binding-missing"),
100 DiagnosticSeverity::Error, "Scene component binding no longer exists")};
101 return binding->validator ? binding->validator() : std::vector<EditorDiagnostic>{};
102}
103
105 const SelectionSnapshot& selection) const {
106 auto translated = translate(selection);
107 if (!translated.ok())
109 eve::DiagnosticCode::InvalidArgument, "Scene component selection is not bound",
110 "editor.scene.component-binding", {}, "editor.SceneComponentPropertyBindings"));
111 return translated.value().first->properties->currentRevision(translated.value().second);
112}
113
115 auto translated = translate(selection);
116 if (!translated.ok()) return {};
117 return translated.value().first->properties->schema(translated.value().second);
118}
119
121 const PropertyPath& path) const {
122 auto translated = translate(selection);
123 if (!translated.ok()) return {PropertyReadState::Error, {}, translated.diagnostics()};
124 return translated.value().first->properties->read(translated.value().second, path);
125}
126
128 const SelectionSnapshot& selection, const PropertyPath& path, const EditorValue& value,
129 PropertySetMode mode) const {
130 auto translated = translate(selection);
131 if (!translated.ok()) return Result<DomainOperation>::failure(translated.status());
132 return translated.value().first->properties->makeSet(translated.value().second, path, value, mode);
133}
134
136 const SelectionSnapshot& selection, const PropertyPath& path) const {
137 auto translated = translate(selection);
138 if (!translated.ok()) return Result<DomainOperation>::failure(translated.status());
139 return translated.value().first->properties->makeReset(translated.value().second, path);
140}
141
144 if (!provider || provider->componentType().empty())
145 return eve::editing::failed<void>(EditorStatus::Rejected, RuleId("editor.scene.component-provider-invalid"),
146 "Component payload provider and type are required");
147 const auto [iterator, inserted] = providers_.emplace(provider->componentType(), provider);
148 if (!inserted)
149 return eve::editing::failed<void>(
150 EditorStatus::Conflict, RuleId("editor.scene.component-provider-duplicate"),
151 "A component payload provider is already registered for type: " + iterator->first);
152 return eve::editing::applied<void>();
153}
154
156 if (!provider) return SceneComponentChange::Unchanged;
157 const auto found = providers_.find(provider->componentType());
158 if (found == providers_.end() || found->second != provider) return SceneComponentChange::Unchanged;
159 providers_.erase(found);
161}
162
164 const TargetId& scene, const ObjectId& object) const {
165 if (scene.empty() || object.empty())
166 return eve::editing::failed<std::vector<SceneComponentPayloadRef>>(
167 EditorStatus::Rejected, RuleId("editor.scene.component-owner-invalid"),
168 "Scene target and object ids are required for component discovery");
169 std::vector<SceneComponentPayloadRef> result;
170 for (const auto& [type, provider] : providers_) {
171 auto components = provider->components(scene, object);
172 for (SceneComponentPayloadRef& component : components) {
173 if (component.target != scene || component.object != object || component.type != type ||
174 component.component.empty())
175 return eve::editing::failed<std::vector<SceneComponentPayloadRef>>(
176 EditorStatus::Failed, RuleId("editor.scene.component-provider-contract"),
177 "Component provider returned an invalid or foreign stable reference: " + type);
178 result.push_back(std::move(component));
179 }
180 }
181 std::sort(result.begin(), result.end(), [](const auto& left, const auto& right) {
182 if (left.type != right.type) return left.type < right.type;
183 return left.component < right.component;
184 });
185 return eve::editing::applied<std::vector<SceneComponentPayloadRef>>(std::move(result));
186}
187
188Result<ISceneComponentPayloadProvider*> SceneComponentPayloadRegistry::resolve(
189 const SelectionSnapshot& selection) const {
190 if (selection.items.empty())
191 return eve::editing::failed<ISceneComponentPayloadProvider*>(EditorStatus::Rejected,
192 RuleId("editor.scene.component-selection-empty"),
193 "Component property selection cannot be empty");
194 const std::string& type = selection.items.front().type;
195 if (type.empty())
196 return eve::editing::failed<ISceneComponentPayloadProvider*>(
197 EditorStatus::Rejected, RuleId("editor.scene.component-selection-type"),
198 "Component property selection requires a stable component type");
199 for (const SelectionItem& item : selection.items) {
200 if (item.domain != SelectionDomain::Scene || item.type != type || item.item.empty())
201 return eve::editing::failed<ISceneComponentPayloadProvider*>(
202 EditorStatus::Rejected, RuleId("editor.scene.component-selection-mixed"),
203 "Component property selections must contain one non-empty component type");
204 }
205 const auto found = providers_.find(type);
206 if (found == providers_.end())
207 return eve::editing::failed<ISceneComponentPayloadProvider*>(
208 EditorStatus::Unsupported, RuleId("editor.scene.component-provider-missing"),
209 "No component payload provider is registered for type: " + type);
210 return eve::editing::applied<ISceneComponentPayloadProvider*>(found->second);
211}
212
214 const SelectionSnapshot& selection) const {
215 auto provider = resolve(selection);
216 if (!provider.ok()) return Result<IPropertyProvider*>::failure(provider.status());
217 return eve::editing::applied<IPropertyProvider*>(static_cast<IPropertyProvider*>(provider.value()));
218}
219
221 const SelectionSnapshot& selection) const {
222 auto provider = resolve(selection);
223 if (!provider.ok()) return Result<IDomainOperationTarget*>::failure(provider.status());
224 return provider.value()->payloadOperationTarget(selection);
225}
226
228 const SceneComponentPayloadRef& component) const {
229 const auto found = providers_.find(component.type);
230 if (found == providers_.end())
231 return eve::editing::failed<std::vector<EditorDiagnostic>>(
232 EditorStatus::Unsupported, RuleId("editor.scene.component-provider-missing"),
233 "No component payload provider is registered for type: " + component.type);
234 const auto current = found->second->components(component.target, component.object);
235 const auto matching = std::find_if(current.begin(), current.end(), [&](const auto& candidate) {
236 return candidate.component == component.component && candidate.type == component.type;
237 });
238 if (matching == current.end())
239 return eve::editing::failed<std::vector<EditorDiagnostic>>(
240 EditorStatus::NotFound, RuleId("editor.scene.component-reference-missing"),
241 "The referenced component no longer exists: " + component.component.value());
242 if (matching->generation != component.generation || matching->revision != component.revision)
243 return eve::editing::failed<std::vector<EditorDiagnostic>>(
244 EditorStatus::Conflict, RuleId("editor.scene.component-reference-stale"),
245 "The component changed since its inspector reference was captured: " + component.component.value());
246 return eve::editing::applied<std::vector<EditorDiagnostic>>(
247 found->second->validateComponent(component));
248}
249
251 std::string channel, const std::vector<SceneComponentPayloadRef>& components,
252 std::uint64_t sequence) {
253 if (components.empty())
254 return eve::editing::failed<SelectionSnapshot>(EditorStatus::Rejected,
255 RuleId("editor.scene.component-selection-empty"),
256 "At least one component is required");
257 const std::string& type = components.front().type;
258 SelectionSnapshot result;
259 result.channel = std::move(channel);
260 result.sequence = sequence;
261 for (const SceneComponentPayloadRef& component : components) {
262 if (component.component.empty() || component.target.empty() || component.type.empty() ||
263 component.type != type)
264 return eve::editing::failed<SelectionSnapshot>(EditorStatus::Rejected,
265 RuleId("editor.scene.component-selection-mixed"),
266 "All selected components must have one non-empty type");
267 result.items.push_back(
268 {SelectionDomain::Scene, component.target, component.component, component.type});
269 }
270 result.primary = result.items.front();
271 return eve::editing::applied<SelectionSnapshot>(std::move(result));
272}
273
274} // namespace eve::scene_editing
double value
HexVec3 left
HexVec3 right
std::int32_t first
std::map< std::string, PropertyRule > properties
std::unique_ptr< gpgpu::Sequence > sequence
Definition OnnxGpgpu.cpp:43
eve::action::ActionVfxBinding binding
std::string path
Definition PlayHost.cpp:110
std::weak_ptr< PrimitiveScene > scene
bool found
double current
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Editable-target capability that applies owning domain operations.
virtual TargetId targetId() const =0
Target id.
virtual std::uint64_t revision() const =0
Revision.
UI-independent property access for an immutable selection snapshot.
Dotted property path (layout.size) compared and persisted as text.
Definition EditingIds.h:78
Deterministic owning value tree shared by authoring hosts.
Module-owned adapter for one scene component payload type.
virtual const std::string & componentType() const =0
Stable component type handled by this provider.
Result< std::vector< EditorDiagnostic > > validatePayload(const SceneComponentPayloadRef &component) const override
Validate payload.
Result< IDomainOperationTarget * > operationTarget(const SelectionSnapshot &selection) const override
Operation target.
Result< IPropertyProvider * > propertyProvider(const SelectionSnapshot &selection) const override
Property provider.
SceneComponentChange unregisterProvider(ISceneComponentPayloadProvider *provider)
Remove a provider only when the exact registered instance matches.
Result< void > registerProvider(ISceneComponentPayloadProvider *provider)
Register one provider; component types must be non-empty and unique.
Result< std::vector< SceneComponentPayloadRef > > componentPayloads(const TargetId &scene, const ObjectId &object) const override
Component payloads.
Result< DomainOperation > makeSet(const SelectionSnapshot &selection, const PropertyPath &path, const EditorValue &value, PropertySetMode mode) const override
Make set.
Result< void > bind(SceneComponentPayloadRef component, SelectionItem moduleSelection, IPropertyProvider *properties, IDomainOperationTarget *operations, Validator validator={})
Bind one stable scene component to an existing module editor target.
std::vector< EditorDiagnostic > validateComponent(const SceneComponentPayloadRef &component) const override
Validate component.
PropertySchema schema(const SelectionSnapshot &selection) const override
Schema.
SceneComponentChange unbind(const TargetId &scene, const StableId &component)
Remove one exact scene component binding.
std::function< std::vector< EditorDiagnostic >()> Validator
Result< IDomainOperationTarget * > payloadOperationTarget(const SelectionSnapshot &selection) const override
Payload operation target.
PropertyReadResult read(const SelectionSnapshot &selection, const PropertyPath &path) const override
Reads .
std::vector< SceneComponentPayloadRef > components(const TargetId &scene, const ObjectId &object) const override
Components.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &selection) const override
Current revision.
SceneComponentPropertyBindings(std::string componentType)
Create bindings for one stable component type.
Result< DomainOperation > makeReset(const SelectionSnapshot &selection, const PropertyPath &path) const override
Make reset.
Result< T > failed(Status status, eve::DiagnosticCode code, RuleId rule, std::string message)
Construct a failed editing result with explicit status and diagnostic categories.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
Result< SelectionSnapshot > makeSceneComponentSelection(std::string channel, const std::vector< SceneComponentPayloadRef > &components, std::uint64_t sequence)
Build a property selection for one or more homogeneous component references.
SceneComponentChange
Whether a registry or binding removal changed authoritative state.
PropertyReadResult public API.
EVENGINE_API_PLATFORM public API.
SelectionItem public API.
SelectionSnapshot public API.
std::vector< SelectionItem > items
std::optional< SelectionItem > primary
Stable, pointer-free reference to one editable component instance.