载入中...
搜索中...
未找到
LocationProfile.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cmath>
5#include <set>
6
7namespace eve::scene {
8namespace {
9bool finite(const LocationPose& pose) {
10 return std::isfinite(pose.x) && std::isfinite(pose.y) && std::isfinite(pose.z) &&
11 std::isfinite(pose.qx) && std::isfinite(pose.qy) && std::isfinite(pose.qz) &&
12 std::isfinite(pose.qw);
13}
14bool validSnapshot(const LocationSnapshot& value) {
15 return finite(value.camera) && (!value.player || finite(*value.player));
16}
17Value poseValue(const LocationPose& pose) {
18 return Value::Object{{"x", pose.x}, {"y", pose.y}, {"z", pose.z}, {"qx", pose.qx},
19 {"qy", pose.qy}, {"qz", pose.qz}, {"qw", pose.qw}};
20}
21Value snapshotValue(const LocationSnapshot& snapshot) {
22 return Value::Object{{"camera", poseValue(snapshot.camera)},
23 {"player", snapshot.player ? poseValue(*snapshot.player) : Value()}};
24}
25bool onlyFields(const Value::Object& object, std::initializer_list<std::string_view> fields) {
26 for (const auto& [name, value] : object) {
27 (void)value;
28 if (std::find(fields.begin(), fields.end(), name) == fields.end()) return false;
29 }
30 return true;
31}
32bool number(const Value::Object& object, const char* name, float& output) {
33 const auto found = object.find(name);
34 if (found == object.end() || !found->second.isNumeric()) return false;
35 output = static_cast<float>(found->second.isDouble() ? found->second.asDouble() : found->second.asInt());
36 return std::isfinite(output);
37}
38bool parsePose(const Value& value, LocationPose& output) {
39 const auto* object = value.getIf<Value::Object>();
40 if (!object || !onlyFields(*object, {"x", "y", "z", "qx", "qy", "qz", "qw"})) return false;
41 LocationPose candidate;
42 if (!number(*object,"x",candidate.x) || !number(*object,"y",candidate.y) ||
43 !number(*object,"z",candidate.z) || !number(*object,"qx",candidate.qx) ||
44 !number(*object,"qy",candidate.qy) || !number(*object,"qz",candidate.qz) ||
45 !number(*object,"qw",candidate.qw)) return false;
46 output = candidate;
47 return true;
48}
49bool parseSnapshot(const Value& value, LocationSnapshot& output) {
50 const auto* object = value.getIf<Value::Object>();
51 if (!object || !onlyFields(*object, {"camera", "player"})) return false;
52 const auto camera = object->find("camera"), player = object->find("player");
53 if (camera == object->end() || player == object->end()) return false;
54 LocationSnapshot candidate;
55 if (!parsePose(camera->second, candidate.camera)) return false;
56 if (!player->second.isNull()) {
57 LocationPose pose;
58 if (!parsePose(player->second, pose)) return false;
59 candidate.player = pose;
60 }
61 output = candidate;
62 return true;
63}
64} // namespace
65
67 const std::optional<LocationPose>& player) {
68 LocationSnapshot candidate{camera, player};
69 if (!validSnapshot(candidate))
70 return Result<void>::failure(Diagnostic::error({}, "location poses must be finite", "scene.locationProfile"));
71 saved_ = candidate;
72 return Result<void>::success();
73}
74
76 if (!saved_)
78 Diagnostic::error(DiagnosticCode::NotFound, "no saved startup location", "scene.locationProfile"));
79 auto result = *saved_;
80 saved_.reset();
81 return Result<LocationSnapshot>::success(std::move(result));
82}
83
85 if (bookmark.name.empty() || !finite(bookmark.camera) || (bookmark.player && !finite(*bookmark.player)))
87 Diagnostic::error({}, "bookmark requires a name and finite poses", "scene.locationProfile"));
88 if (std::any_of(bookmarks_.begin(), bookmarks_.end(),
89 [&](const LocationBookmark& value) { return value.name == bookmark.name; }))
91 Diagnostic::error(DiagnosticCode::Conflict, "bookmark name already exists", "scene.locationProfile"));
92 bookmarks_.push_back(std::move(bookmark));
93 return Result<int>::success(static_cast<int>(bookmarks_.size() - 1));
94}
95
97 std::string controller, std::string scene) {
98 if (index < 0 || index >= getBookmarkCount())
99 return Result<void>::failure(Diagnostic::error({}, "bookmark index is out of range", "scene.locationProfile"));
100 if (!validSnapshot(snapshot))
101 return Result<void>::failure(Diagnostic::error({}, "bookmark poses must be finite", "scene.locationProfile"));
102 auto candidate = bookmarks_[static_cast<std::size_t>(index)];
103 candidate.camera = snapshot.camera;
104 candidate.player = snapshot.player;
105 candidate.controller = std::move(controller);
106 candidate.scene = std::move(scene);
107 bookmarks_[static_cast<std::size_t>(index)] = std::move(candidate);
108 return Result<void>::success();
109}
110
112 if (index < 0 || index >= getBookmarkCount() || selectedIndex < 0 || selectedIndex >= getBookmarkCount())
114 Diagnostic::error({}, "bookmark or selection index is out of range", "scene.locationProfile"));
115 const int oldLast = getBookmarkCount() - 1;
116 bookmarks_.erase(bookmarks_.begin() + index);
117 int adjusted = selectedIndex == oldLast ? selectedIndex - 1 : selectedIndex;
118 if (!bookmarks_.empty()) adjusted = std::clamp(adjusted, 0, getBookmarkCount() - 1);
119 return Result<int>::success(adjusted);
120}
121
123 const auto* bookmark = bookmarkAt(index);
124 if (!bookmark)
126 Diagnostic::error({}, "bookmark index is out of range", "scene.locationProfile"));
127 return Result<LocationSnapshot>::success(LocationSnapshot{bookmark->camera, bookmark->player});
128}
129
131 if (bookmarks_.empty())
133 Diagnostic::error(DiagnosticCode::NotFound, "location profile has no bookmarks", "scene.locationProfile"));
134 if (selectedIndex < 0 || selectedIndex >= getBookmarkCount())
135 return Result<int>::failure(Diagnostic::error({}, "selection index is out of range", "scene.locationProfile"));
136 return Result<int>::success(selectedIndex == 0 ? getBookmarkCount() - 1 : selectedIndex - 1);
137}
138
140 if (bookmarks_.empty())
142 Diagnostic::error(DiagnosticCode::NotFound, "location profile has no bookmarks", "scene.locationProfile"));
143 if (selectedIndex < 0 || selectedIndex >= getBookmarkCount())
144 return Result<int>::failure(Diagnostic::error({}, "selection index is out of range", "scene.locationProfile"));
145 return Result<int>::success(selectedIndex == getBookmarkCount() - 1 ? 0 : selectedIndex + 1);
146}
147
149 return index >= 0 && index < getBookmarkCount() ? &bookmarks_[static_cast<std::size_t>(index)] : nullptr;
150}
151
153 const auto* value = bookmarkAt(index);
156 Diagnostic::error({}, "bookmark index is out of range", "scene.locationProfile"));
157}
159 const auto* value = bookmarkAt(index);
160 return value ? Result<std::string>::success(value->controller)
162 Diagnostic::error({}, "bookmark index is out of range", "scene.locationProfile"));
163}
165 const auto* value = bookmarkAt(index);
168 Diagnostic::error({}, "bookmark index is out of range", "scene.locationProfile"));
169}
170
172 Value::Array bookmarks;
173 bookmarks.reserve(bookmarks_.size());
174 for (const auto& bookmark : bookmarks_) {
175 bookmarks.emplace_back(Value::Object{{"name", bookmark.name}, {"camera", poseValue(bookmark.camera)},
176 {"player", bookmark.player ? poseValue(*bookmark.player) : Value()},
177 {"controller", bookmark.controller}, {"scene", bookmark.scene}});
178 }
179 Value root(Value::Object{{"schemaId", "eve.scene.location-profile"}, {"schemaVersion", 1},
180 {"saved", saved_ ? snapshotValue(*saved_) : Value()}, {"bookmarks", std::move(bookmarks)}});
181 return root.toJson();
182}
183
185 auto decoded = Value::fromJson(json);
186 if (!decoded) return Result<void>::failure(decoded.status());
187 const auto* root = decoded.value().getIf<Value::Object>();
188 const auto fail = [] {
190 Diagnostic::error(DiagnosticCode::ParseError, "invalid location profile schema", "scene.locationProfile"));
191 };
192 if (!root || !onlyFields(*root, {"schemaId", "schemaVersion", "saved", "bookmarks"})) return fail();
193 const auto schema = root->find("schemaId"), version = root->find("schemaVersion");
194 const auto saved = root->find("saved"), bookmarks = root->find("bookmarks");
195 if (schema == root->end() || !schema->second.isString() ||
196 schema->second.asString() != "eve.scene.location-profile" || version == root->end() ||
197 !version->second.isInt64() || version->second.asInt() != 1 || saved == root->end() ||
198 bookmarks == root->end() || !bookmarks->second.isArray()) return fail();
199 LocationProfile candidate;
200 if (!saved->second.isNull()) {
201 LocationSnapshot snapshot;
202 if (!parseSnapshot(saved->second, snapshot)) return fail();
203 candidate.saved_ = snapshot;
204 }
205 for (const auto& value : *bookmarks->second.getIf<Value::Array>()) {
206 const auto* object = value.getIf<Value::Object>();
207 if (!object || !onlyFields(*object, {"name", "camera", "player", "controller", "scene"})) return fail();
208 const auto name=object->find("name"), camera=object->find("camera"), player=object->find("player");
209 const auto controller=object->find("controller"), scene=object->find("scene");
210 if (name==object->end() || !name->second.isString() || camera==object->end() ||
211 player==object->end() || controller==object->end() || !controller->second.isString() ||
212 scene==object->end() || !scene->second.isString()) return fail();
213 LocationBookmark bookmark;
214 bookmark.name=name->second.asString(); bookmark.controller=controller->second.asString();
215 bookmark.scene=scene->second.asString();
216 if (!parsePose(camera->second,bookmark.camera)) return fail();
217 if (!player->second.isNull()) { LocationPose pose; if (!parsePose(player->second,pose)) return fail(); bookmark.player=pose; }
218 auto added=candidate.addBookmark(std::move(bookmark));
219 if (!added) return Result<void>::failure(added.status());
220 }
221 *this = std::move(candidate);
222 return Result<void>::success();
223}
224} // namespace eve::scene
double value
int root
Definition AnimSmr.cpp:119
std::string output
eve::EntitySpatialPose pose
float camera[2]
std::string name
bool finite
std::vector< std::string > fields
Definition PlayHost.cpp:111
std::weak_ptr< PrimitiveScene > scene
double number
bool found
Json object
uint32_t index
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 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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
Result< std::string > toJson() const
Serialize this value as deterministic compact JSON.
Definition Value.cpp:67
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Caller-owned Pcg location and bookmark state. Mutable operations validate complete candidate data bef...
Result< std::string > serializeJson() const
Serialize schema eve.scene.location-profile version 1 as deterministic JSON.
Result< std::string > getBookmarkController(int index) const
Copy a bookmark controller name, or return InvalidArgument for an invalid index.
Result< int > addBookmark(LocationBookmark bookmark)
Add a uniquely named bookmark and return its index.
Result< int > removeBookmark(int index, int selectedIndex)
Remove one bookmark and return Pcg's adjusted selected index.
Result< std::string > getBookmarkName(int index) const
Copy a bookmark name, or return InvalidArgument for an invalid index.
Result< int > nextBookmark(int selectedIndex) const
Return the next wrapped selection, or NotFound for an empty profile.
Result< void > saveLocation(const LocationPose &camera, const std::optional< LocationPose > &player=std::nullopt)
Save the one-shot startup location, replacing the previous value atomically.
const LocationBookmark * bookmarkAt(int index) const noexcept
Borrow one bookmark.
Result< void > overrideBookmark(int index, const LocationSnapshot &snapshot, std::string controller, std::string scene)
Replace one bookmark while preserving its existing name.
Result< LocationSnapshot > loadLocation()
Consume the startup location and clear hasSaved exactly once, matching Pcg LoadLocation.
Result< std::string > getBookmarkScene(int index) const
Copy a bookmark scene name, or return InvalidArgument for an invalid index.
int getBookmarkCount() const noexcept
Returns the bookmark count.
Result< void > restoreJson(std::string_view json)
Atomically restore schema version 1 JSON.
Result< LocationSnapshot > loadBookmark(int index) const
Copy a bookmark transform snapshot.
Result< int > previousBookmark(int selectedIndex) const
Return the previous wrapped selection, or NotFound for an empty profile.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
One named Pcg location bookmark with optional player transform metadata.
std::optional< LocationPose > player
Provider-neutral position and quaternion stored by Pcg's location profile.
Loaded camera/player transform pair.
std::optional< LocationPose > player