载入中...
搜索中...
未找到
MaterialBatchTarget.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <optional>
5#include <set>
6
8namespace {
9const EditorValue* field(const EditorValue& value, const char* key) {
10 const auto* object = value.getIf<EditorValue::Object>();
11 if (!object) return nullptr;
12 const auto found = object->find(key);
13 return found == object->end() ? nullptr : &found->second;
14}
15
17 SelectionSnapshot result;
20 item.target = material.targetId();
21 item.item = editing::StableId(material.targetId().value());
22 item.type = "graphics.material";
23 result.items.push_back(item);
24 result.primary = item;
25 return result;
26}
27
28EditorValue content(const std::vector<MaterialDocumentTarget>& materials) {
29 EditorValue::Array result;
30 result.reserve(materials.size());
31 for (const auto& material : materials)
32 result.emplace_back(
33 EditorValue::Object{{"id", material.targetId().value()}, {"snapshot", material.snapshotValue()}});
34 return result;
35}
36} // namespace
37
38MaterialBatchTarget::MaterialBatchTarget(std::string id, std::vector<MaterialDocumentTarget> materials,
40 : id_(std::move(id)), materials_(std::move(materials)), sink_(sink) {}
41
49
50void* MaterialBatchTarget::queryCapability(const CapabilityId& capability) {
51 if (capability == IPropertyProvider::editingCapabilityId()) return static_cast<IPropertyProvider*>(this);
53 return static_cast<editing::IEditingSnapshotProvider*>(this);
54 return nullptr;
55}
56
57Result<std::vector<std::size_t>> MaterialBatchTarget::selectedIndices(const SelectionSnapshot& selection) const {
58 if (selection.items.empty())
59 return editing::failed<std::vector<std::size_t>>(EditorStatus::Rejected,
60 RuleId("editor.material-batch.empty-selection"),
61 "Material batch selection is empty");
62 std::vector<std::size_t> result;
63 std::set<std::size_t> unique;
64 for (const auto& item : selection.items) {
65 if (item.target != targetId())
66 return editing::failed<std::vector<std::size_t>>(EditorStatus::Rejected,
67 RuleId("editor.material-batch.target-mismatch"),
68 "Material batch selection targets another document");
69 const auto found = std::find_if(materials_.begin(), materials_.end(), [&](const auto& material) {
70 return material.targetId().value() == item.item.value();
71 });
72 if (found == materials_.end())
73 return editing::failed<std::vector<std::size_t>>(EditorStatus::NotFound,
74 RuleId("editor.material-batch.material-not-found"),
75 "Selected material is not in this batch");
76 const auto index = static_cast<std::size_t>(found - materials_.begin());
77 if (!unique.insert(index).second)
78 return editing::failed<std::vector<std::size_t>>(EditorStatus::Rejected,
79 RuleId("editor.material-batch.duplicate-selection"),
80 "Material batch selection contains a duplicate");
81 result.push_back(index);
82 }
83 return editing::applied<std::vector<std::size_t>>(std::move(result));
84}
85
91
93 return materials_.empty() ? PropertySchema{} : materials_.front().schema({});
94}
95
97 const auto indices = selectedIndices(selection);
98 if (!indices.ok()) return {PropertyReadState::Error, {}, indices.status().diagnostics()};
99 std::optional<EditorValue> common;
100 for (const auto index : indices.value()) {
101 const auto value = materials_[index].read(one(materials_[index]), path);
102 if (value.state != PropertyReadState::Value) return value;
103 if (!common)
104 common = value.value;
105 else if (*common != value.value)
106 return {PropertyReadState::Mixed, {}, {}};
107 }
108 return {PropertyReadState::Value, *common, {}};
109}
110
111Result<DomainOperation> MaterialBatchTarget::replacement(std::vector<MaterialDocumentTarget> candidates,
112 const PropertyPath& path) const {
114 operation.type = "material.batch.replace.v1";
115 operation.inverseType = operation.type;
116 operation.target = targetId();
117 operation.payload = EditorValue::Object{{"materials", content(candidates)}};
118 operation.inverse = EditorValue::Object{{"materials", content(materials_)}};
119 operation.hasInverse = true;
120 operation.affectedProperties.push_back(path.value());
121 operation.mergeKey = "material-batch:" + id_ + ":" + path.value();
122 return editing::applied<DomainOperation>(std::move(operation));
123}
124
126 const EditorValue& value, PropertySetMode mode) const {
127 if (mode == PropertySetMode::Reset) return makeReset(selection, path);
128 const auto indices = selectedIndices(selection);
129 if (!indices.ok()) return Result<DomainOperation>::failure(indices.status());
130 auto candidates = materials_;
131 for (const auto index : indices.value()) {
132 auto operation = candidates[index].makeSet(one(candidates[index]), path, value, mode);
133 if (!operation.ok()) return Result<DomainOperation>::failure(operation.status());
134 auto applied = candidates[index].applyDomainOperation(operation.value());
135 if (!applied.ok()) return Result<DomainOperation>::failure(applied.status());
136 }
137 return replacement(std::move(candidates), path);
138}
139
141 const PropertyPath& path) const {
142 const auto descriptor = schema(selection).find(path);
143 if (!descriptor)
144 return editing::failed<DomainOperation>(EditorStatus::Unsupported, RuleId("editor.material-batch.property"),
145 "Unknown material batch property");
146 return makeSet(selection, path, descriptor->defaultValue, PropertySetMode::Absolute);
147}
148
149Result<void> MaterialBatchTarget::publishAndAdopt(std::vector<MaterialDocumentTarget> candidates,
150 editing::Revision candidateRevision,
151 const EditRegion& candidateDirty) {
152 if (sink_) {
153 auto published = sink_->publish(candidates);
154 if (!published.ok()) return published;
155 }
156 materials_ = std::move(candidates);
157 setRevision(candidateRevision);
158 widenDirty(candidateDirty);
159 return editing::applied<void>();
160}
161
163 if (operation.target != targetId() || operation.type != "material.batch.replace.v1")
164 return editing::failed<void>(EditorStatus::Rejected, RuleId("editor.material-batch.operation"),
165 "Material batch operation mismatch");
166 const auto* values = field(operation.payload, "materials");
167 const auto* array = values ? values->getIf<EditorValue::Array>() : nullptr;
168 if (!array || array->size() != materials_.size() || !operation.payload.isWithinLimits(16, 200000, 16 * 1024 * 1024))
169 return editing::failed<void>(EditorStatus::Rejected, RuleId("editor.material-batch.payload"),
170 "Material batch payload is incomplete or exceeds limits");
171 std::vector<MaterialDocumentTarget> candidates;
172 candidates.reserve(materials_.size());
173 std::set<std::string> ids;
174 for (const auto& item : *array) {
175 const auto* idValue = field(item, "id");
176 const auto* snapshot = field(item, "snapshot");
177 const auto* id = idValue ? idValue->getIf<std::string>() : nullptr;
178 if (!id || !snapshot || !ids.insert(*id).second)
179 return editing::failed<void>(EditorStatus::Rejected, RuleId("editor.material-batch.entry"),
180 "Material batch entry is invalid or duplicated");
181 const auto original = std::find_if(materials_.begin(), materials_.end(),
182 [&](const auto& material) { return material.targetId().value() == *id; });
183 if (original == materials_.end())
184 return editing::failed<void>(EditorStatus::Conflict, RuleId("editor.material-batch.identity"),
185 "Material batch identities changed");
186 MaterialDocumentTarget candidate(*id);
187 auto loaded = candidate.loadSnapshot(*snapshot);
188 if (!loaded.ok()) return Result<void>::failure(loaded.status());
189 candidates.push_back(std::move(candidate));
190 }
192 dirty.include(0, 0);
193 return publishAndAdopt(std::move(candidates), revisionValue() + 1, dirty);
194}
195
196std::unique_ptr<IDomainOperationTarget> MaterialBatchTarget::cloneDomainState() const {
197 auto candidate = std::make_unique<MaterialBatchTarget>(*this);
198 candidate->sink_ = nullptr;
199 return candidate;
200}
201
202Result<void> MaterialBatchTarget::commitDomainState(std::unique_ptr<IDomainOperationTarget> candidate) {
203 auto* typed = dynamic_cast<MaterialBatchTarget*>(candidate.get());
204 if (!typed || typed->id_ != id_ || typed->materials_.size() != materials_.size())
205 return editing::failed<void>(EditorStatus::Conflict, RuleId("editor.material-batch.candidate"),
206 "Material batch candidate does not match this target");
207 return publishAndAdopt(std::move(typed->materials_), typed->revisionValue(), typed->dirtyRegion());
208}
209
211 return EditorValue::Object{{"schemaVersion", int64_t{1}}, {"materials", content(materials_)}};
212}
213
214std::vector<editing::StableId> MaterialBatchTarget::materialIds() const {
215 std::vector<editing::StableId> result;
216 result.reserve(materials_.size());
217 for (const auto& material : materials_) result.emplace_back(material.targetId().value());
218 return result;
219}
220
221} // namespace eve::material_editing
ActionParameterOperation operation
double value
std::string descriptor
std::map< std::string, Var > values
std::uint32_t key
glm::uvec4 ids
std::vector< std::uint32_t > indices
bool dirty
std::string path
Definition PlayHost.cpp:110
std::string id
Definition PlayHost.cpp:108
Material * material
bool found
uint32_t index
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
void setRevision(Revision revision)
Adopt a revision observed elsewhere, without advancing it.
Revision revisionValue() const noexcept
Return the revision as the shared protocol revision type.
void widenDirty()
Mark an unknown area dirty without advancing the revision.
eve::OptionalRef< C > capability()
Query a capability through its interface-owned stable identity.
Optional capability exposing a deterministic, persistence-safe editing snapshot.
static CapabilityId editingCapabilityId()
Stable capability identity shared by every editable domain target.
UI-independent property access for an immutable selection snapshot.
static CapabilityId editingCapabilityId()
Stable capability identity for generic property editing.
Dotted property path (layout.size) compared and persisted as text.
Definition EditingIds.h:78
Deterministic owning value tree shared by authoring hosts.
std::vector< Value > Array
std::map< std::string, Value > Object
const T * getIf() const
Return an immediately borrowed typed value.
Atomic publication boundary for a complete multi-material candidate.
virtual Result< void > publish(std::span< const MaterialDocumentTarget > candidates)=0
Publish every candidate as one transaction.
One editor transaction target for mixed-value, multi-material property editing. Owns all authoring do...
PropertySchema schema(const SelectionSnapshot &selection) const override
Schema.
Result< void > commitDomainState(std::unique_ptr< IDomainOperationTarget > candidate) override
Commits domain state.
Result< DomainOperation > makeSet(const SelectionSnapshot &selection, const PropertyPath &path, const EditorValue &value, PropertySetMode mode) const override
Make set.
eve::Result< eve::Revision > currentRevision(const SelectionSnapshot &selection) const override
Current revision.
EditorValue snapshotValue() const override
Snapshot value.
TargetId targetId() const override
Target id.
Result< void > applyDomainOperation(const DomainOperation &operation) override
Applies domain operation.
Result< DomainOperation > makeReset(const SelectionSnapshot &selection, const PropertyPath &path) const override
Make reset.
std::vector< editing::StableId > materialIds() const
Return owning material IDs in batch order.
TargetDescriptor describe() const override
Describe.
std::unique_ptr< IDomainOperationTarget > cloneDomainState() const override
Clones domain state.
void * queryCapability(const CapabilityId &capability) override
Query property or snapshot capabilities.
PropertyReadResult read(const SelectionSnapshot &selection, const PropertyPath &path) const override
Reads .
UI-neutral, serializable material authoring target.
Result< void > loadSnapshot(const EditorValue &snapshot)
Replace content when opening a persisted material document.
std::uint64_t Revision
StrongId< StableIdTag > StableId
Definition EditingIds.h:64
Result< T > failed(Status status, eve::DiagnosticCode code, RuleId rule, std::string message)
Construct a failed editing result with explicit status and diagnostic categories.
DomainOperation public API.
EditRegion public API.
void include(int x, int y)
Include.
PropertyReadResult public API.
EVENGINE_API_PLATFORM public API.
PropertyDescriptorLookup find(const PropertyPath &path) const
Finds find.
SelectionItem public API.
SelectionSnapshot public API.
std::vector< SelectionItem > items
std::optional< SelectionItem > primary
Stable discovery metadata exposed by every editable target.