载入中...
搜索中...
未找到
PointSetAttributeOps.cpp
浏览该文件的文档.
1#include "procgen/PointSet.h"
2
3#include <algorithm>
4#include <cmath>
5#include <cstdint>
6#include <limits>
7#include <string_view>
8#include <unordered_map>
9
10namespace eve::procgen {
11namespace {
12
13Result<float> invalidChannelFloat(std::string message, std::string path) {
16}
17
18bool isMetadataName(std::string_view name) noexcept {
19 return !name.empty() && name.front() != '$' && name.front() != '@';
20}
21
22std::string_view stripDomainPrefix(std::string_view name, std::string_view* domain) {
23 if (name.size() >= 6 && name.substr(0, 6) == "@Data.") {
24 *domain = "data";
25 return name.substr(6);
26 }
27 if (name.size() >= 10 && name.substr(0, 10) == "@Elements.") {
28 *domain = "elements";
29 return name.substr(10);
30 }
31 if (name.size() >= 6 && name.substr(0, 6) == "@Last.") {
32 *domain = "elements"; // closed-set alias of incoming Elements domain
33 return name.substr(6);
34 }
35 *domain = "elements";
36 return name;
37}
38
39void rotationBasis(float pitchDeg, float yawDeg, float rollDeg, float* forward, float* right, float* up) {
40 const float deg = 0.017453292519943295f;
41 const float cp = std::cos(pitchDeg * deg), sp = std::sin(pitchDeg * deg);
42 const float cy = std::cos(yawDeg * deg), sy = std::sin(yawDeg * deg);
43 const float cr = std::cos(rollDeg * deg), sr = std::sin(rollDeg * deg);
44 // Yaw-Pitch-Roll (Y-up) basis matching engine point transforms.
45 forward[0] = sy * cp;
46 forward[1] = -sp;
47 forward[2] = cy * cp;
48 right[0] = cy * cr + sy * sp * sr;
49 right[1] = cp * sr;
50 right[2] = -sy * cr + cy * sp * sr;
51 up[0] = -cy * sr + sy * sp * cr;
52 up[1] = cp * cr;
53 up[2] = sy * sr + cy * sp * cr;
54}
55
56void appendRow(PointSet& output, const PointSet& input, std::size_t index) {
57 output.appendPointFrom(input, index).expect("PointSet attribute op requires compatible schemas");
58}
59
60} // namespace
61
62bool isPointFloatSelector(std::string_view name) noexcept {
63 return name == "$Density" || name == "$Seed" || name == "$Steepness" || name == "$Position.X" ||
64 name == "$Position.Y" || name == "$Position.Z" || name == "$Normal.X" || name == "$Normal.Y" ||
65 name == "$Normal.Z" || name == "$Rotation.Pitch" || name == "$Rotation.Yaw" ||
66 name == "$Rotation.Roll" || name == "$Rotation.Forward.X" || name == "$Rotation.Forward.Y" ||
67 name == "$Rotation.Forward.Z" || name == "$Rotation.Right.X" || name == "$Rotation.Right.Y" ||
68 name == "$Rotation.Right.Z" || name == "$Rotation.Up.X" || name == "$Rotation.Up.Y" ||
69 name == "$Rotation.Up.Z" || name == "$Scale.X" || name == "$Scale.Y" || name == "$Scale.Z" ||
70 name == "$Color.R" || name == "$Color.G" || name == "$Color.B" || name == "$Color.A" ||
71 name == "$BoundsMin.X" || name == "$BoundsMin.Y" || name == "$BoundsMin.Z" || name == "$BoundsMax.X" ||
72 name == "$BoundsMax.Y" || name == "$BoundsMax.Z" || name == "$Position.ZYX.X" ||
73 name == "$Position.ZYX.Y" || name == "$Position.ZYX.Z" || name == "$Position.XZY.X" ||
74 name == "$Position.XZY.Y" || name == "$Position.XZY.Z" || name == "$Normal.ZYX.X" ||
75 name == "$Normal.ZYX.Y" || name == "$Normal.ZYX.Z";
76}
77
78bool isPointFloatChannel(std::string_view name) noexcept {
79 return isPointFloatSelector(name) || isMetadataName(name);
80}
81
83 if (index < 0 || index >= points.getCount())
84 return invalidChannelFloat("point index is out of range", "index");
85 if (name.empty()) return invalidChannelFloat("attribute channel must not be empty", "name");
86 if (name.front() != '$') {
87 std::string_view domain = "elements";
88 const std::string_view leaf = stripDomainPrefix(name, &domain);
89 if (leaf.empty()) return invalidChannelFloat("attribute channel must not be empty", "name");
90 if (domain == "data") {
91 if (points.dataAttributes().rowCount() == 0) return Result<float>::success(defaultValue);
92 return Result<float>::success(points.dataAttributes().getFloat(0, leaf).value_or(defaultValue));
93 }
94 return Result<float>::success(points.getFloatAttribute(index, std::string(leaf), defaultValue));
95 }
97 return invalidChannelFloat("unknown float selector '" + std::string(name) + "'", "name");
98
99 const ProcgenPoint& point = points.points()[std::size_t(index)];
100 if (name == "$Density") return Result<float>::success(point.density);
101 if (name == "$Seed") return Result<float>::success(float(point.seed));
102 if (name == "$Steepness") return Result<float>::success(point.steepness);
103 if (name == "$Position.X") return Result<float>::success(point.x);
104 if (name == "$Position.Y") return Result<float>::success(point.y);
105 if (name == "$Position.Z") return Result<float>::success(point.z);
106 if (name == "$Normal.X") return Result<float>::success(point.normalX);
107 if (name == "$Normal.Y") return Result<float>::success(point.normalY);
108 if (name == "$Normal.Z") return Result<float>::success(point.normalZ);
109 if (name == "$Rotation.Pitch") return Result<float>::success(point.pitch);
110 if (name == "$Rotation.Yaw") return Result<float>::success(point.yaw);
111 if (name == "$Rotation.Roll") return Result<float>::success(point.roll);
112 if (name == "$Scale.X") return Result<float>::success(point.scaleX);
113 if (name == "$Scale.Y") return Result<float>::success(point.scaleY);
114 if (name == "$Scale.Z") return Result<float>::success(point.scaleZ);
115 if (name == "$Color.R") return Result<float>::success(point.colorR);
116 if (name == "$Color.G") return Result<float>::success(point.colorG);
117 if (name == "$Color.B") return Result<float>::success(point.colorB);
118 if (name == "$Color.A") return Result<float>::success(point.colorA);
119 if (name == "$BoundsMin.X") return Result<float>::success(point.boundsMinX);
120 if (name == "$BoundsMin.Y") return Result<float>::success(point.boundsMinY);
121 if (name == "$BoundsMin.Z") return Result<float>::success(point.boundsMinZ);
122 if (name == "$BoundsMax.X") return Result<float>::success(point.boundsMaxX);
123 if (name == "$BoundsMax.Y") return Result<float>::success(point.boundsMaxY);
124 if (name == "$BoundsMax.Z") return Result<float>::success(point.boundsMaxZ);
125 float forward[3], right[3], up[3];
126 if (name.rfind("$Rotation.Forward.", 0) == 0 || name.rfind("$Rotation.Right.", 0) == 0 ||
127 name.rfind("$Rotation.Up.", 0) == 0) {
128 rotationBasis(point.pitch, point.yaw, point.roll, forward, right, up);
129 const float* axis = name.rfind("$Rotation.Forward.", 0) == 0 ? forward
130 : name.rfind("$Rotation.Right.", 0) == 0 ? right
131 : up;
132 if (!name.empty() && name.back() == 'X') return Result<float>::success(axis[0]);
133 if (!name.empty() && name.back() == 'Y') return Result<float>::success(axis[1]);
134 if (!name.empty() && name.back() == 'Z') return Result<float>::success(axis[2]);
135 }
136 if (name == "$Position.ZYX.X") return Result<float>::success(point.z);
137 if (name == "$Position.ZYX.Y") return Result<float>::success(point.y);
138 if (name == "$Position.ZYX.Z") return Result<float>::success(point.x);
139 if (name == "$Position.XZY.X") return Result<float>::success(point.x);
140 if (name == "$Position.XZY.Y") return Result<float>::success(point.z);
141 if (name == "$Position.XZY.Z") return Result<float>::success(point.y);
142 if (name == "$Normal.ZYX.X") return Result<float>::success(point.normalZ);
143 if (name == "$Normal.ZYX.Y") return Result<float>::success(point.normalY);
144 if (name == "$Normal.ZYX.Z") return Result<float>::success(point.normalX);
145 return invalidChannelFloat("unknown float selector '" + std::string(name) + "'", "name");
146}
147
149 if (index < 0 || index >= points.getCount()) return Result<void>::failure(
150 Diagnostic::error(DiagnosticCode::InvalidArgument, "point index is out of range", "index"));
151 if (name.empty()) return Result<void>::failure(
152 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute channel must not be empty", "name"));
153 if (name.front() != '$') {
154 std::string_view domain = "elements";
155 const std::string_view leaf = stripDomainPrefix(name, &domain);
156 if (leaf.empty()) return Result<void>::failure(
157 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute channel must not be empty", "name"));
158 if (domain == "data") {
160 return points.mutableDataAttributes().setFloat(0, leaf, value);
161 }
162 return points.trySetFloatAttribute(index, std::string(leaf), value);
163 }
166 Diagnostic::error(DiagnosticCode::InvalidArgument, "unknown float selector '" + std::string(name) + "'", "name"));
167
168 ProcgenPoint& point = points.mutablePoint(std::size_t(index));
169 if (name == "$Density") {
171 return Result<void>::success();
172 }
173 if (name == "$Seed") {
174 point.seed = value <= 0.f ? 0u : uint32_t(value);
175 return Result<void>::success();
176 }
177 if (name == "$Steepness") {
178 point.steepness = std::clamp(value, 0.f, 1.f);
179 return Result<void>::success();
180 }
181 if (name == "$Position.X") {
182 point.x = value;
183 return Result<void>::success();
184 }
185 if (name == "$Position.Y") {
186 point.y = value;
187 return Result<void>::success();
188 }
189 if (name == "$Position.Z") {
190 point.z = value;
191 return Result<void>::success();
192 }
193 if (name == "$Normal.X") {
194 point.normalX = value;
195 return Result<void>::success();
196 }
197 if (name == "$Normal.Y") {
198 point.normalY = value;
199 return Result<void>::success();
200 }
201 if (name == "$Normal.Z") {
202 point.normalZ = value;
203 return Result<void>::success();
204 }
205 if (name == "$Rotation.Pitch") {
206 point.pitch = value;
207 return Result<void>::success();
208 }
209 if (name == "$Rotation.Yaw") {
210 point.yaw = value;
211 return Result<void>::success();
212 }
213 if (name == "$Rotation.Roll") {
214 point.roll = value;
215 return Result<void>::success();
216 }
217 if (name == "$Scale.X") {
218 point.scaleX = value;
219 return Result<void>::success();
220 }
221 if (name == "$Scale.Y") {
222 point.scaleY = value;
223 return Result<void>::success();
224 }
225 if (name == "$Scale.Z") {
226 point.scaleZ = value;
227 return Result<void>::success();
228 }
229 if (name == "$Color.R") {
230 point.colorR = value;
231 return Result<void>::success();
232 }
233 if (name == "$Color.G") {
234 point.colorG = value;
235 return Result<void>::success();
236 }
237 if (name == "$Color.B") {
238 point.colorB = value;
239 return Result<void>::success();
240 }
241 if (name == "$Color.A") {
242 point.colorA = value;
243 return Result<void>::success();
244 }
245 if (name == "$BoundsMin.X") {
246 point.boundsMinX = value;
247 return Result<void>::success();
248 }
249 if (name == "$BoundsMin.Y") {
250 point.boundsMinY = value;
251 return Result<void>::success();
252 }
253 if (name == "$BoundsMin.Z") {
254 point.boundsMinZ = value;
255 return Result<void>::success();
256 }
257 if (name == "$BoundsMax.X") {
258 point.boundsMaxX = value;
259 return Result<void>::success();
260 }
261 if (name == "$BoundsMax.Y") {
262 point.boundsMaxY = value;
263 return Result<void>::success();
264 }
265 if (name == "$BoundsMax.Z") {
266 point.boundsMaxZ = value;
267 return Result<void>::success();
268 }
270 Diagnostic::error(DiagnosticCode::InvalidArgument, "unknown float selector '" + std::string(name) + "'", "name"));
271}
272
273PointSet mathPointFloatAttribute(const PointSet& input, const std::string& attribute,
274 const std::string& outputAttribute, const std::string& operation, float operand,
275 float defaultValue) {
276 PointSet result = input;
277 for (int index = 0; index < result.getCount(); ++index) {
278 const float value =
279 readPointFloatChannel(result, index, attribute, defaultValue).expect("mathPointFloatAttribute input");
280 float output = value;
281 if (operation == "add")
282 output += operand;
283 else if (operation == "subtract")
284 output -= operand;
285 else if (operation == "multiply")
286 output *= operand;
287 else if (operation == "divide")
288 output /= operand;
289 else if (operation == "min")
290 output = std::min(output, operand);
291 else if (operation == "max")
292 output = std::max(output, operand);
293 writePointFloatChannel(result, index, outputAttribute, output).expect("mathPointFloatAttribute output");
294 }
295 return result;
296}
297
298PointSet filterPointFloatAttribute(const PointSet& input, const std::string& name, float minValue, float maxValue,
299 bool invert) {
300 if (minValue > maxValue) std::swap(minValue, maxValue);
302 for (int index = 0; index < input.getCount(); ++index) {
303 bool keep = false;
304 if (isMetadataName(name)) {
305 const auto stored = input.attributes().getFloat(std::size_t(index), name);
306 keep = stored && *stored >= minValue && *stored <= maxValue;
307 } else {
308 const float value =
309 readPointFloatChannel(input, index, name, 0.f).expect("filterPointFloatAttribute channel");
310 keep = value >= minValue && value <= maxValue;
311 }
312 if (keep != invert) appendRow(output, input, std::size_t(index));
313 }
314 return output;
315}
316
317PointSet filterPointIntAttribute(const PointSet& input, const std::string& name, std::int64_t minValue,
318 std::int64_t maxValue, bool invert) {
319 if (minValue > maxValue) std::swap(minValue, maxValue);
321 for (std::size_t index = 0; index < input.points().size(); ++index) {
322 const auto value = input.attributes().getInt(index, name);
323 const bool matches = value && *value >= minValue && *value <= maxValue;
324 if (matches != invert) appendRow(output, input, index);
325 }
326 return output;
327}
328
329PointSet filterPointBoolAttribute(const PointSet& input, const std::string& name, bool value, bool invert) {
331 for (std::size_t index = 0; index < input.points().size(); ++index) {
332 const auto found = input.attributes().getBool(index, name);
333 const bool matches = found && *found == value;
334 if (matches != invert) appendRow(output, input, index);
335 }
336 return output;
337}
338
339Result<PointSet> copyPointAttribute(const PointSet& input, const std::string& source, const std::string& target) {
340 if (source.empty() || target.empty())
342 Diagnostic::error(DiagnosticCode::InvalidArgument, "attribute copy requires source and target", "name"));
344
345 const bool sourceSelector = isPointFloatSelector(source);
346 const bool targetSelector = isPointFloatSelector(target);
347 if (!source.empty() && source.front() == '$' && !sourceSelector)
349 Diagnostic::error(DiagnosticCode::InvalidArgument, "unknown float selector '" + source + "'", "source"));
350 if (!target.empty() && target.front() == '$' && !targetSelector)
352 Diagnostic::error(DiagnosticCode::InvalidArgument, "unknown float selector '" + target + "'", "target"));
353
354 if (!sourceSelector && !targetSelector) {
355 PointSet result = input;
356 auto copied = result.tryCopyAttribute(source, target);
357 if (!copied.ok()) return Result<PointSet>::failure(copied.status());
358 return Result<PointSet>::success(std::move(result));
359 }
360
363 DiagnosticCode::InvalidArgument, "attribute copy with selectors requires float channels", "name"));
364
365 PointSet result = input;
366 for (int index = 0; index < result.getCount(); ++index) {
367 if (isMetadataName(source) && !result.attributes().has(std::size_t(index), source)) continue;
368 const float value =
369 readPointFloatChannel(result, index, source, 0.f).expect("copyPointAttribute source channel");
370 auto written = writePointFloatChannel(result, index, target, value);
371 if (!written.ok()) return Result<PointSet>::failure(written.status());
372 }
373 return Result<PointSet>::success(std::move(result));
374}
375
376Result<PointSet> renamePointAttribute(const PointSet& input, const std::string& from, const std::string& to) {
377 PointSet result = input;
378 auto renamed = result.tryRenameAttribute(from, to);
379 if (!renamed.ok()) return Result<PointSet>::failure(renamed.status());
380 return Result<PointSet>::success(std::move(result));
381}
382
384 PointSet result = input;
385 auto deleted = result.tryDeleteAttribute(name);
386 if (!deleted.ok()) return Result<PointSet>::failure(deleted.status());
387 return Result<PointSet>::success(std::move(result));
388}
389
390Result<PointSet> transferPointAttribute(const PointSet& target, const PointSet& source, const std::string& attribute,
391 const std::string& outputAttribute, const std::string& mode) {
392 const std::string output = outputAttribute.empty() ? attribute : outputAttribute;
393 if (attribute.empty() || output.empty())
395 DiagnosticCode::InvalidArgument, "attribute transfer requires attribute names", "attribute"));
396 if (mode != "index" && mode != "id" && mode != "nearest")
398 DiagnosticCode::InvalidArgument, "attribute transfer mode must be index, id, or nearest", "mode"));
399
400 const bool sourceSelector = isPointFloatSelector(attribute);
401 const bool targetSelector = isPointFloatSelector(output);
402 if (!attribute.empty() && attribute.front() == '$' && !sourceSelector)
404 DiagnosticCode::InvalidArgument, "unknown float selector '" + attribute + "'", "attribute"));
405 if (!output.empty() && output.front() == '$' && !targetSelector)
407 DiagnosticCode::InvalidArgument, "unknown float selector '" + output + "'", "outputAttribute"));
408
409 std::optional<ProcgenAttributeType> metadataType;
410 if (!sourceSelector) {
411 metadataType = source.attributes().typeOf(attribute);
412 if (!metadataType)
414 DiagnosticCode::NotFound, "source attribute '" + attribute + "' does not exist", "attribute"));
415 if (targetSelector && *metadataType != ProcgenAttributeType::Float)
417 DiagnosticCode::TypeMismatch, "only float metadata can transfer into selectors", "attribute"));
418 }
419
420 std::unordered_map<std::uint64_t, int> ids;
421 if (mode == "id") {
422 for (int index = 0; index < source.getCount(); ++index) {
423 const std::uint64_t id = source.getPointId(index);
424 if (id != 0) ids.emplace(id, index);
425 }
426 }
427
428 auto nearestSource = [&](int targetIndex) -> int {
429 const ProcgenPoint& point = target.points()[std::size_t(targetIndex)];
430 int best = -1;
431 float bestD = std::numeric_limits<float>::infinity();
432 for (int index = 0; index < source.getCount(); ++index) {
433 const ProcgenPoint& other = source.points()[std::size_t(index)];
434 const float dx = point.x - other.x;
435 const float dy = point.y - other.y;
436 const float dz = point.z - other.z;
437 const float d = dx * dx + dy * dy + dz * dz;
438 if (d < bestD) {
439 bestD = d;
440 best = index;
441 }
442 }
443 return best;
444 };
445
446 PointSet result = target;
447 for (int targetIndex = 0; targetIndex < result.getCount(); ++targetIndex) {
448 int sourceIndex = -1;
449 if (mode == "index") {
450 if (targetIndex < source.getCount()) sourceIndex = targetIndex;
451 } else if (mode == "id") {
452 const std::uint64_t id = result.getPointId(targetIndex);
453 if (id != 0) {
454 const auto found = ids.find(id);
455 if (found != ids.end()) sourceIndex = found->second;
456 }
457 } else {
458 sourceIndex = nearestSource(targetIndex);
459 }
460 if (sourceIndex < 0) continue;
461
462 if (sourceSelector || targetSelector ||
463 (metadataType && *metadataType == ProcgenAttributeType::Float && isPointFloatChannel(output))) {
464 if (isMetadataName(attribute) && !source.attributes().has(std::size_t(sourceIndex), attribute)) continue;
465 const float value =
466 readPointFloatChannel(source, sourceIndex, attribute, 0.f).expect("transfer float source");
467 auto written = writePointFloatChannel(result, targetIndex, output, value);
468 if (!written.ok()) return Result<PointSet>::failure(written.status());
469 continue;
470 }
471
472 switch (*metadataType) {
474 const auto value = source.attributes().getFloat(std::size_t(sourceIndex), attribute);
475 if (!value) break;
476 auto written = result.trySetFloatAttribute(targetIndex, output, *value);
477 if (!written.ok()) return Result<PointSet>::failure(written.status());
478 break;
479 }
481 const auto value = source.attributes().getInt(std::size_t(sourceIndex), attribute);
482 if (!value) break;
483 auto written = result.trySetIntAttribute(targetIndex, output, *value);
484 if (!written.ok()) return Result<PointSet>::failure(written.status());
485 break;
486 }
488 const auto value = source.attributes().getBool(std::size_t(sourceIndex), attribute);
489 if (!value) break;
490 auto written = result.trySetBoolAttribute(targetIndex, output, *value);
491 if (!written.ok()) return Result<PointSet>::failure(written.status());
492 break;
493 }
495 const auto value = source.attributes().getVector(std::size_t(sourceIndex), attribute);
496 if (!value) break;
497 auto written = result.trySetVectorAttribute(targetIndex, output, value->x, value->y, value->z);
498 if (!written.ok()) return Result<PointSet>::failure(written.status());
499 break;
500 }
502 const auto value = source.attributes().getString(std::size_t(sourceIndex), attribute);
503 if (!value) break;
504 auto written = result.trySetStringAttribute(targetIndex, output, std::string(*value));
505 if (!written.ok()) return Result<PointSet>::failure(written.status());
506 break;
507 }
508 }
509 }
510 return Result<PointSet>::success(std::move(result));
511}
512
513PointSet setPointIntAttribute(const PointSet& input, const std::string& attribute, std::int64_t value) {
514 PointSet result = input;
515 for (int index = 0; index < result.getCount(); ++index)
516 result.trySetIntAttribute(index, attribute, value).expect("setPointIntAttribute schema");
517 return result;
518}
519
520PointSet setPointBoolAttribute(const PointSet& input, const std::string& attribute, bool value) {
521 PointSet result = input;
522 for (int index = 0; index < result.getCount(); ++index)
523 result.trySetBoolAttribute(index, attribute, value).expect("setPointBoolAttribute schema");
524 return result;
525}
526
527PointSet setPointVectorAttribute(const PointSet& input, const std::string& attribute, float x, float y, float z) {
528 PointSet result = input;
529 for (int index = 0; index < result.getCount(); ++index)
530 result.trySetVectorAttribute(index, attribute, x, y, z).expect("setPointVectorAttribute schema");
531 return result;
532}
533
534Result<PointSet> comparePointFloatAttribute(const PointSet& input, const std::string& attribute,
535 const std::string& comparison, float operand,
536 const std::string& outputAttribute, float defaultValue) {
537 if (!isPointFloatChannel(attribute))
539 Diagnostic::error(DiagnosticCode::InvalidArgument, "compare requires a float channel", "attribute"));
540 if (outputAttribute.empty() || !isMetadataName(outputAttribute))
542 DiagnosticCode::InvalidArgument, "compare output must be a metadata bool attribute", "outputAttribute"));
543 if (comparison != "eq" && comparison != "ne" && comparison != "lt" && comparison != "le" && comparison != "gt" &&
544 comparison != "ge")
546 DiagnosticCode::InvalidArgument, "compare comparison must be eq/ne/lt/le/gt/ge", "comparison"));
547
548 PointSet result = input;
549 for (int index = 0; index < result.getCount(); ++index) {
550 const float value =
551 readPointFloatChannel(result, index, attribute, defaultValue).expect("comparePointFloatAttribute");
552 bool matches = false;
553 if (comparison == "eq")
554 matches = value == operand;
555 else if (comparison == "ne")
556 matches = value != operand;
557 else if (comparison == "lt")
558 matches = value < operand;
559 else if (comparison == "le")
560 matches = value <= operand;
561 else if (comparison == "gt")
562 matches = value > operand;
563 else
564 matches = value >= operand;
565 auto written = result.trySetBoolAttribute(index, outputAttribute, matches);
566 if (!written.ok()) return Result<PointSet>::failure(written.status());
567 }
568 return Result<PointSet>::success(std::move(result));
569}
570
571Result<PointSet> selectPointFloatAttribute(const PointSet& input, const std::string& conditionAttribute,
572 const std::string& trueAttribute, const std::string& falseAttribute,
573 const std::string& outputAttribute, float trueDefault,
574 float falseDefault) {
575 if (conditionAttribute.empty() || !isMetadataName(conditionAttribute))
577 DiagnosticCode::InvalidArgument, "select condition must be a metadata bool attribute",
578 "conditionAttribute"));
579 if (!isPointFloatChannel(trueAttribute) || !isPointFloatChannel(falseAttribute) ||
580 !isPointFloatChannel(outputAttribute))
582 Diagnostic::error(DiagnosticCode::InvalidArgument, "select float channels are invalid", "attribute"));
583
584 PointSet result = input;
585 for (int index = 0; index < result.getCount(); ++index) {
586 const bool takeTrue = result.getBoolAttribute(index, conditionAttribute, false);
587 const float value = readPointFloatChannel(result, index, takeTrue ? trueAttribute : falseAttribute,
588 takeTrue ? trueDefault : falseDefault)
589 .expect("selectPointFloatAttribute channel");
590 auto written = writePointFloatChannel(result, index, outputAttribute, value);
591 if (!written.ok()) return Result<PointSet>::failure(written.status());
592 }
593 return Result<PointSet>::success(std::move(result));
594}
595
597 AttributeTable& data = points.mutableDataAttributes();
598 if (data.rowCount() == 0)
599 (void)data.appendRow();
600 else if (data.rowCount() > 1)
601 data.resize(1);
602}
603
604Result<void> setPointDataFloatAttribute(PointSet& points, const std::string& attribute, float value) {
605 if (attribute.empty() || attribute.front() == '$' || attribute.front() == '@')
607 "data attribute name is invalid", "attribute"));
609 return points.mutableDataAttributes().setFloat(0, attribute, value);
610}
611
612Result<void> setPointDataIntAttribute(PointSet& points, const std::string& attribute, std::int64_t value) {
613 if (attribute.empty() || attribute.front() == '$' || attribute.front() == '@')
615 "data attribute name is invalid", "attribute"));
617 return points.mutableDataAttributes().setInt(0, attribute, value);
618}
619
620Result<void> setPointDataStringAttribute(PointSet& points, const std::string& attribute, const std::string& value) {
621 if (attribute.empty() || attribute.front() == '$' || attribute.front() == '@')
623 "data attribute name is invalid", "attribute"));
625 return points.mutableDataAttributes().setString(0, attribute, value);
626}
627
628PointSet partitionPointAttribute(const PointSet& input, const std::string& attribute,
629 const std::string& outputAttribute, const std::string& mode) {
630 PointSet result = input;
631 const std::string out = outputAttribute.empty() ? "partition" : outputAttribute;
632 const bool useHash = mode == "hash";
633 for (int i = 0; i < result.getCount(); ++i) {
634 std::int64_t partition = 0;
635 if (result.hasIntAttribute(i, attribute))
636 partition = result.getIntAttribute(i, attribute, 0);
637 else if (result.hasStringAttribute(i, attribute)) {
638 const std::string value = result.getStringAttribute(i, attribute, {});
639 if (useHash) {
640 std::uint64_t hash = 14695981039346656037ull;
641 for (unsigned char c : value) {
642 hash ^= c;
643 hash *= 1099511628211ull;
644 }
645 partition = std::int64_t(hash & 0x7fffffffull);
646 } else {
647 // Stable dense-ish id from string bytes without allocating a map.
648 std::uint64_t hash = 14695981039346656037ull;
649 for (unsigned char c : value) {
650 hash ^= c;
651 hash *= 1099511628211ull;
652 }
653 partition = std::int64_t(hash & 0xffffull);
654 }
655 } else if (result.hasFloatAttribute(i, attribute)) {
656 partition = std::int64_t(std::llround(result.getFloatAttribute(i, attribute, 0.f)));
657 }
658 result.trySetIntAttribute(i, out, partition).expect("partition write");
659 }
660 return result;
661}
662
663static float hashNoise(uint32_t seed, int index, float x, float z, float frequency) {
664 std::uint32_t h = seed * 747796405u + 2891336453u;
665 h ^= std::uint32_t(index) * 2246822519u;
666 h ^= std::uint32_t(std::int32_t(x * frequency * 1000.f)) * 3266489917u;
667 h ^= std::uint32_t(std::int32_t(z * frequency * 1000.f)) * 668265263u;
668 h ^= h >> 15;
669 h *= 2246822519u;
670 h ^= h >> 13;
671 return float(h & 0x00ffffffu) / float(0x00ffffffu);
672}
673
674PointSet noisePointFloatAttribute(const PointSet& input, const std::string& attribute, uint32_t seed,
675 float frequency, float amplitude, float offset) {
676 PointSet result = input;
677 const std::string out = attribute.empty() ? "noise" : attribute;
678 for (int i = 0; i < result.getCount(); ++i) {
679 const auto& p = result.points()[std::size_t(i)];
680 const float n = hashNoise(seed, i, p.x, p.z, frequency) * amplitude + offset;
681 result.trySetFloatAttribute(i, out, n).expect("noise write");
682 }
683 return result;
684}
685
686PointSet mathPointIntAttribute(const PointSet& input, const std::string& attribute,
687 const std::string& outputAttribute, const std::string& operation, std::int64_t operand,
688 std::int64_t defaultValue) {
689 PointSet result = input;
690 const std::string out = outputAttribute.empty() ? attribute : outputAttribute;
691 for (int i = 0; i < result.getCount(); ++i) {
692 const std::int64_t value = result.getIntAttribute(i, attribute, defaultValue);
693 std::int64_t output = value;
694 if (operation == "add") output = value + operand;
695 else if (operation == "subtract") output = value - operand;
696 else if (operation == "multiply") output = value * operand;
697 else if (operation == "divide") output = operand == 0 ? value : value / operand;
698 else if (operation == "min") output = std::min(value, operand);
699 else if (operation == "max") output = std::max(value, operand);
700 else if (operation == "modulo") output = operand == 0 ? 0 : value % operand;
701 result.trySetIntAttribute(i, out, output).expect("int math write");
702 }
703 return result;
704}
705
706PointSet mathPointVectorAttribute(const PointSet& input, const std::string& attribute,
707 const std::string& outputAttribute, const std::string& operation, float operandX,
708 float operandY, float operandZ, float defaultX, float defaultY, float defaultZ) {
709 PointSet result = input;
710 const std::string out = outputAttribute.empty() ? attribute : outputAttribute;
711 for (int i = 0; i < result.getCount(); ++i) {
712 float x = result.getVectorAttributeX(i, attribute, defaultX);
713 float y = result.getVectorAttributeY(i, attribute, defaultY);
714 float z = result.getVectorAttributeZ(i, attribute, defaultZ);
715 if (operation == "add") {
716 x += operandX; y += operandY; z += operandZ;
717 } else if (operation == "subtract") {
718 x -= operandX; y -= operandY; z -= operandZ;
719 } else if (operation == "multiply" || operation == "scale") {
720 x *= operandX; y *= operandY; z *= operandZ;
721 } else if (operation == "divide") {
722 if (operandX != 0.f) x /= operandX;
723 if (operandY != 0.f) y /= operandY;
724 if (operandZ != 0.f) z /= operandZ;
725 } else if (operation == "set") {
726 x = operandX; y = operandY; z = operandZ;
727 }
728 result.trySetVectorAttribute(i, out, x, y, z).expect("vector math write");
729 }
730 return result;
731}
732
733} // namespace eve::procgen
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 from
float cy
Definition CardTypes.cpp:34
glm::vec4 p[6]
std::array< std::uint8_t, 32 > hash
Definition Evpack.cpp:172
std::vector< std::uint8_t > stored
Definition Evpack.cpp:171
EvpackChunkInput input
Definition Evpack.cpp:170
std::string message
glm::uvec4 ids
float u
Definition Grass.cpp:233
glm::vec3 n
Definition Grass.cpp:63
HexVec3 up
HexVec3 right
std::int32_t c
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
int h
size_t offset
std::string name
std::string path
Definition PlayHost.cpp:110
std::uint32_t seed
Definition PointSet.cpp:807
std::shared_ptr< const std::vector< glm::vec2 > > points
float d
bool found
float dz
float dy
float dx
std::map< Cell, int > best
float size
Definition TreeMesh.cpp:156
uint32_t index
const UnitySourceAsset & source
glm::vec3 point
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
void expect(std::string_view message) const
Require success for a void operation.
Definition Result.h:548
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
Schema-bearing column store aligned with a PointSet's point rows.
std::optional< std::int64_t > getInt(std::size_t row, std::string_view name) const
Read an integer value, or no value for an absent row, name, or type.
bool has(std::size_t row, std::string_view name) const
Return whether a row contains a value in the named column.
std::optional< bool > getBool(std::size_t row, std::string_view name) const
Read a Boolean value, or no value for an absent row, name, or type.
std::optional< float > getFloat(std::size_t row, std::string_view name) const
Read a float value, or no value for an absent row, name, or type.
Script-friendly collection of attributed 3D samples.
Definition PointSet.h:59
bool hasStringAttribute(int index, const std::string &name) const
True when string attribute.
Definition PointSet.cpp:437
bool empty() const
Empty.
Definition PointSet.cpp:49
bool hasIntAttribute(int index, const std::string &name) const
Test whether signed integer metadata exists.
Definition PointSet.cpp:367
float getFloatAttribute(int index, const std::string &name, float fallback) const
Returns the float attribute.
Definition PointSet.cpp:342
Result< void > tryCopyAttribute(const std::string &from, const std::string &to)
Copy one metadata column onto another name on this set.
Definition PointSet.cpp:449
Result< void > trySetStringAttribute(int index, const std::string &name, const std::string &value)
Canonical checked string metadata write.
Definition PointSet.cpp:421
Result< void > tryRenameAttribute(const std::string &from, const std::string &to)
Rename one metadata column on this set.
Definition PointSet.cpp:441
std::string getStringAttribute(int index, const std::string &name, const std::string &fallback) const
Returns the string attribute.
Definition PointSet.cpp:432
float getVectorAttributeY(int index, const std::string &name, float fallback) const
Read the Y component of vector metadata or the caller-provided default.
Definition PointSet.cpp:407
Result< void > trySetBoolAttribute(int index, const std::string &name, bool value)
Canonical checked Boolean metadata write.
Definition PointSet.cpp:371
Result< void > trySetVectorAttribute(int index, const std::string &name, float x, float y, float z)
Canonical checked vector metadata write.
Definition PointSet.cpp:391
bool hasFloatAttribute(int index, const std::string &name) const
True when float attribute.
Definition PointSet.cpp:347
std::uint64_t getPointId(int index) const
Return the stable point identity, or zero when it has not been assigned.
Definition PointSet.cpp:289
float getVectorAttributeZ(int index, const std::string &name, float fallback) const
Read the Z component of vector metadata or the caller-provided default.
Definition PointSet.cpp:412
bool getBoolAttribute(int index, const std::string &name, bool fallback) const
Read boolean metadata or return the caller-provided default when absent.
Definition PointSet.cpp:382
const std::vector< ProcgenPoint > & points() const
Borrow immutable point rows; structural ownership remains with this set.
Definition PointSet.h:212
float getVectorAttributeX(int index, const std::string &name, float fallback) const
Read the X component of vector metadata or the caller-provided default.
Definition PointSet.cpp:402
Result< void > trySetIntAttribute(int index, const std::string &name, std::int64_t value)
Canonical checked signed integer metadata write.
Definition PointSet.cpp:351
std::int64_t getIntAttribute(int index, const std::string &name, std::int64_t fallback) const
Read signed integer metadata or return the caller-provided default when absent.
Definition PointSet.cpp:362
int getCount() const
Returns the count.
Definition PointSet.cpp:48
const AttributeTable & attributes() const noexcept
Borrow the authoritative schema-bearing attribute table.
Definition PointSet.h:216
Result< void > trySetFloatAttribute(int index, const std::string &name, float value)
Canonical checked float metadata write.
Definition PointSet.cpp:331
Result< void > tryDeleteAttribute(const std::string &name)
Delete one metadata column on this set.
Definition PointSet.cpp:445
const char * defaultValue
std::vector< double > forward(const Policy &p, const Observation &o)
Forward.
Definition Learning.h:65
Result< PointSet > deletePointAttribute(const PointSet &input, const std::string &name)
Delete one metadata column.
PointSet mathPointIntAttribute(const PointSet &input, const std::string &attribute, const std::string &outputAttribute, const std::string &operation, std::int64_t operand, std::int64_t defaultValue)
Apply integer math to an int metadata column.
bool isPointFloatChannel(std::string_view name) noexcept
Return whether name is a valid float channel (builtin selector or metadata name).
EVENGINE_API_DOMAINS Result< float > readPointFloatChannel(const PointSet &points, int index, std::string_view name, float defaultValue)
Read a float metadata column or closed $ point-field selector.
PointSet filterPointIntAttribute(const PointSet &input, const std::string &name, std::int64_t minValue, std::int64_t maxValue, bool invert)
Select points whose named int attribute lies in an inclusive range.
Result< PointSet > copyPointAttribute(const PointSet &input, const std::string &source, const std::string &target)
Copy one float channel or typed metadata column onto another name.
EVENGINE_API_DOMAINS Result< void > setPointDataFloatAttribute(PointSet &points, const std::string &attribute, float value)
Write a float into the PointSet @Data domain.
Result< PointSet > selectPointFloatAttribute(const PointSet &input, const std::string &conditionAttribute, const std::string &trueAttribute, const std::string &falseAttribute, const std::string &outputAttribute, float trueDefault, float falseDefault)
Select between two float channels using a bool metadata condition.
EVENGINE_API_DOMAINS PointSet noisePointFloatAttribute(const PointSet &input, const std::string &attribute, uint32_t seed, float frequency, float amplitude, float offset)
Write deterministic float noise into a metadata attribute.
PointSet mathPointFloatAttribute(const PointSet &input, const std::string &attribute, const std::string &outputAttribute, const std::string &operation, float operand, float defaultValue)
Apply one scalar operation to a float metadata attribute or $ selector.
EVENGINE_API_DOMAINS PointSet filterPointFloatAttribute(const PointSet &input, const std::string &name, float minValue, float maxValue, bool invert)
Select points whose named float attribute or $ selector lies in an inclusive range.
PointSet mathPointVectorAttribute(const PointSet &input, const std::string &attribute, const std::string &outputAttribute, const std::string &operation, float operandX, float operandY, float operandZ, float defaultX, float defaultY, float defaultZ)
Apply vector math to a vector metadata column.
PointSet setPointIntAttribute(const PointSet &input, const std::string &attribute, std::int64_t value)
Write a constant int metadata column on every point.
EVENGINE_API_DOMAINS PointSet partitionPointAttribute(const PointSet &input, const std::string &attribute, const std::string &outputAttribute, const std::string &mode)
Assign partition indices from a string/int attribute (mode: value|hash).
void ensurePointSetDataRow(PointSet &points)
Return whether name is a known $-prefixed float point-field selector.
Result< void > setPointDataStringAttribute(PointSet &points, const std::string &attribute, const std::string &value)
Write a string into the PointSet @Data domain.
Result< void > setPointDataIntAttribute(PointSet &points, const std::string &attribute, std::int64_t value)
Write an int into the PointSet @Data domain.
Result< void > writePointFloatChannel(PointSet &points, int index, std::string_view name, float value)
Write a float metadata column or closed $ point-field selector.
Result< PointSet > transferPointAttribute(const PointSet &target, const PointSet &source, const std::string &attribute, const std::string &outputAttribute, const std::string &mode)
Transfer one attribute from source onto target points.
PointSet setPointVectorAttribute(const PointSet &input, const std::string &attribute, float x, float y, float z)
Write a constant vector metadata column on every point.
PointSet setPointBoolAttribute(const PointSet &input, const std::string &attribute, bool value)
Write a constant bool metadata column on every point.
Result< PointSet > renamePointAttribute(const PointSet &input, const std::string &from, const std::string &to)
Rename one metadata column.
Result< PointSet > comparePointFloatAttribute(const PointSet &input, const std::string &attribute, const std::string &comparison, float operand, const std::string &outputAttribute, float defaultValue)
Compare a float channel against an operand and write a bool metadata column.
PointSet filterPointBoolAttribute(const PointSet &input, const std::string &name, bool value, bool invert)
Select points whose named bool attribute equals a value.
bool isPointFloatSelector(std::string_view name) noexcept
True when point float selector.
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
One deterministic sample used by script-first procedural pipelines.
Definition PointSet.h:17