载入中...
搜索中...
未找到
PlacementSession.cpp
浏览该文件的文档.
3#include "building/Ghost.h"
6#include "grid/GridConfig.h"
7
8#include <algorithm>
9#include <cmath>
10#include <limits>
11#include <optional>
12
13namespace eve::building {
14
15namespace {
16
17PlacementSystem::SurfacePatch surfacePatchFromGhost(const PlacementWorld &world,
18 const Ghost &ghost) {
19 PlacementSystem::SurfacePatch patch;
20 if (world.getGrid().plane == grid::GridPlane::XZ) {
21 patch.anchor.worldX = ghost.getWorldX();
22 patch.anchor.worldY = ghost.getElevation();
23 patch.anchor.worldZ = ghost.getWorldY();
24 } else {
25 patch.anchor.worldX = ghost.getWorldX();
26 patch.anchor.worldY = ghost.getWorldY();
27 patch.anchor.worldZ = ghost.getElevation();
28 }
29 patch.anchor.surfaceId = ghost.getSurfaceId();
30 patch.anchor.surfaceRevision = static_cast<uint64_t>(ghost.getSurfaceRevision());
31 patch.anchor.normalX = ghost.getSurfaceNormalX();
32 patch.anchor.normalY = ghost.getSurfaceNormalY();
33 patch.anchor.normalZ = ghost.getSurfaceNormalZ();
34 patch.anchor.tangentX = ghost.getSurfaceTangentX();
35 patch.anchor.tangentY = ghost.getSurfaceTangentY();
36 patch.anchor.tangentZ = ghost.getSurfaceTangentZ();
37 patch.samples.resize(static_cast<size_t>(std::max(ghost.getSurfaceSampleCount(), 0)));
38 patch.maxSlopeDegrees = ghost.getSurfaceMaxSlopeDegrees();
39 patch.heightDelta = ghost.getSurfaceHeightDelta();
40 return patch;
41}
42
43std::optional<PlacementSystem::PatternKind> patternKindFromName(const std::string &name) {
44 if (name == "edge_line") return PlacementSystem::PatternKind::EdgeLine;
45 if (name == "edge_path") return PlacementSystem::PatternKind::EdgePath;
46 if (name == "edge_cubic_bezier")
48 if (name == "rectangle_fill") return PlacementSystem::PatternKind::RectangleFill;
49 if (name == "rectangle_outline")
51 if (name == "circle_brush") return PlacementSystem::PatternKind::CircleBrush;
52 return std::nullopt;
53}
54
55std::string patternKindName(PlacementSystem::PatternKind kind) {
56 switch (kind) {
57 case PlacementSystem::PatternKind::EdgeLine: return "edge_line";
58 case PlacementSystem::PatternKind::EdgePath: return "edge_path";
59 case PlacementSystem::PatternKind::EdgeCubicBezier: return "edge_cubic_bezier";
60 case PlacementSystem::PatternKind::RectangleFill: return "rectangle_fill";
61 case PlacementSystem::PatternKind::RectangleOutline: return "rectangle_outline";
62 case PlacementSystem::PatternKind::CircleBrush: return "circle_brush";
63 }
64 return {};
65}
66
67bool edgePattern(PlacementSystem::PatternKind kind) {
71}
72
73} // namespace
74
76
78 if (ghost_) ghost_->destroy();
79 ghost_ = nullptr;
80 delete this;
81}
82
83bool PlacementSession::startPlacement(PlacementWorld *world, const std::string &buildingId) {
84 if (!world || buildingId.empty()) return false;
85 world_ = world;
86 ghost_->setBuildingId(buildingId);
87 active_ = true;
88 mode_ = "place";
89 lastPlacedIds_.clear();
90 edgePathVertices_.clear();
91 edgeCurveControlPoints_.clear();
92 lastEditedId_ = 0;
93 areaPreview_ = {};
94 patternRequest_ = {};
95 patternPreview_ = {};
96 patternActive_ = false;
97 return true;
98}
99
101 active_ = false;
102 world_ = nullptr;
103 edgePathVertices_.clear();
104 edgeCurveControlPoints_.clear();
105 patternRequest_ = {};
106 patternPreview_ = {};
107 patternActive_ = false;
108}
109
111 return ghost_ ? ghost_->getBuildingId() : std::string{};
112}
113
114void PlacementSession::setMode(const std::string &mode) {
115 if (mode == "remove") {
116 mode_ = "remove";
117 } else {
118 mode_ = "place";
119 }
120}
121
123 if (ghost_) ghost_->setRotationDeg(deg);
124}
125
126void PlacementSession::rotateBy(float deltaDeg) {
127 if (ghost_) ghost_->rotateBy(deltaDeg);
128}
129
131 return ghost_ ? ghost_->getRotationDeg() : 0.f;
132}
133
134bool PlacementSession::updateFromWorld(PlacementWorld *world, float worldX, float worldY) {
135 if (!active_ || !ghost_) return false;
136 if (world) world_ = world;
137 if (!world_) return false;
138 ghost_->setFromWorld(world_, worldX, worldY);
139 refreshValidate();
140 return true;
141}
142
144 float worldZ) {
145 if (!active_ || !ghost_) return false;
146 if (world) world_ = world;
147 if (!world_) return false;
148 ghost_->setFromWorld3D(world_, worldX, worldY, worldZ);
149 refreshValidate();
150 return true;
151}
152
154 float x, float y) {
155 if (!active_ || !ghost_) return false;
156 if (world) world_ = world;
157 if (!world_) return false;
158 ghost_->setFromSurface(world_, surface, x, y);
159 refreshValidate();
160 return true;
161}
162
164 const std::string &direction) {
165 if (!active_ || !ghost_) return EdgeUpdateStatus::Rejected;
166 if (world) world_ = world;
167 if (!world_) return EdgeUpdateStatus::Rejected;
168 ghost_->setEdge(world_, cellX, cellY, direction);
169 refreshValidate();
171}
172
174 int vertexY) {
175 if (!active_ || !ghost_) return CornerUpdateStatus::Rejected;
176 if (world) world_ = world;
177 if (!world_) return CornerUpdateStatus::Rejected;
178 ghost_->setCorner(world_, vertexX, vertexY);
179 refreshValidate();
181}
182
184 float worldY, float elevation) {
185 if (!active_ || !ghost_) return FreeUpdateStatus::Rejected;
186 if (world) world_ = world;
187 if (!world_) return FreeUpdateStatus::Rejected;
188 ghost_->setFree(world_, worldX, worldY, elevation);
189 refreshValidate();
191}
192
193bool PlacementSession::isValid() const { return ghost_ ? ghost_->isValid() : false; }
194
195std::string PlacementSession::getReason() const {
196 return ghost_ ? ghost_->getReason() : std::string{};
197}
198
199void PlacementSession::refreshValidate() {
200 if (world_ && ghost_) ghost_->validate(world_);
201}
202
204 if (!active_ || !world_ || !ghost_) return 0;
205 if (mode_ == "remove") {
206 int occ = 0;
207 if (ghost_->getPlacementKind() == "edge") {
208 const std::string direction =
209 ghost_->getEdgeAxis() == "horizontal" ? "north" : "west";
210 const BuildingDefinition *definition = BuildingRegistry::find(ghost_->getBuildingId());
211 const std::string channel = definition ? definition->channel : std::string{};
212 occ = world_->getEdgeOccupant(channel, ghost_->getCellX(), ghost_->getCellY(), direction);
213 } else if (ghost_->getPlacementKind() == "corner") {
214 const BuildingDefinition *definition = BuildingRegistry::find(ghost_->getBuildingId());
215 const std::string channel = definition ? definition->channel : std::string{};
216 occ = world_->getCornerOccupant(channel, ghost_->getCellX(), ghost_->getCellY());
217 } else if (ghost_->getPlacementKind() == "free") {
218 const BuildingDefinition *definition = BuildingRegistry::find(ghost_->getBuildingId());
219 const std::string channel = definition ? definition->channel : std::string{};
220 occ = world_->getFreeOccupant(channel, ghost_->getWorldX(), ghost_->getWorldY());
221 } else {
222 occ = world_->getOccupant(ghost_->getCellX(), ghost_->getCellY());
223 }
224 if (occ <= 0) return 0;
225 return world_->removeBuilding(occ) ? occ : 0;
226 }
227 return world_->placeGhost(ghost_);
228}
229
231 int endVertexX, int endVertexY) {
232 lastPlacedIds_.clear();
233 if (!active_ || !world_ || !ghost_ || mode_ != "place")
237 request.points = {{static_cast<float>(startVertexX), static_cast<float>(startVertexY)},
238 {static_cast<float>(endVertexX), static_cast<float>(endVertexY)}};
239 auto result = PlacementSystem::placePattern(world_, ghost_->getBuildingId(), request);
240 if (!result.ok()) return EdgeLineExecuteStatus::Rejected;
241 lastPlacedIds_ = std::move(result).takeValue().instanceIds;
243}
244
246 edgePathVertices_.clear();
247 if (!active_ || !world_ || !ghost_ || mode_ != "place")
249 const BuildingDefinition *definition = BuildingRegistry::find(ghost_->getBuildingId());
250 if (!definition || definition->placementKind != "edge") return EdgePathUpdateStatus::Rejected;
251 edgePathVertices_.push_back({vertexX, vertexY});
253}
254
256 if (edgePathVertices_.empty()) return EdgePathUpdateStatus::Rejected;
257 const CornerAddress previous = edgePathVertices_.back();
258 if ((previous.x == vertexX) == (previous.y == vertexY))
260 edgePathVertices_.push_back({vertexX, vertexY});
262}
263
265 lastPlacedIds_.clear();
266 if (!active_ || !world_ || !ghost_ || mode_ != "place" || edgePathVertices_.size() < 2)
270 for (const CornerAddress &vertex : edgePathVertices_)
271 request.points.push_back(
272 {static_cast<float>(vertex.x), static_cast<float>(vertex.y)});
273 auto result = PlacementSystem::placePattern(world_, ghost_->getBuildingId(), request);
274 if (!result.ok()) return EdgeLineExecuteStatus::Rejected;
275 lastPlacedIds_ = std::move(result).takeValue().instanceIds;
276 edgePathVertices_.clear();
278}
279
280EdgePathUpdateStatus PlacementSession::beginEdgeCurve(float x, float y) {
281 edgeCurveControlPoints_.clear();
282 if (!active_ || !world_ || !ghost_ || mode_ != "place" || !std::isfinite(x) ||
283 !std::isfinite(y))
284 return EdgePathUpdateStatus::Rejected;
285 const BuildingDefinition *definition = BuildingRegistry::find(ghost_->getBuildingId());
286 if (!definition || definition->placementKind != "edge") return EdgePathUpdateStatus::Rejected;
287 edgeCurveControlPoints_.push_back({x, y});
288 return EdgePathUpdateStatus::Updated;
289}
290
291EdgePathUpdateStatus PlacementSession::appendEdgeCurveControlPoint(float x, float y) {
292 if (edgeCurveControlPoints_.empty() || edgeCurveControlPoints_.size() >= 4 ||
293 !std::isfinite(x) || !std::isfinite(y))
294 return EdgePathUpdateStatus::Rejected;
295 edgeCurveControlPoints_.push_back({x, y});
296 return EdgePathUpdateStatus::Updated;
297}
298
299EdgeLineExecuteStatus PlacementSession::executeEdgeCurve(int subdivisions) {
300 lastPlacedIds_.clear();
301 if (!active_ || !world_ || !ghost_ || mode_ != "place" ||
302 edgeCurveControlPoints_.size() != 4)
303 return EdgeLineExecuteStatus::Rejected;
305 request.kind = PlacementSystem::PatternKind::EdgeCubicBezier;
306 request.points = edgeCurveControlPoints_;
307 request.subdivisions = subdivisions;
308 auto result = PlacementSystem::placePattern(world_, ghost_->getBuildingId(), request);
309 if (!result.ok()) return EdgeLineExecuteStatus::Rejected;
310 lastPlacedIds_ = std::move(result).takeValue().instanceIds;
311 edgeCurveControlPoints_.clear();
312 return EdgeLineExecuteStatus::Placed;
313}
314
315EdgeLineExecuteStatus PlacementSession::executeEdgeCurveOnSurface(
316 int subdivisions, const std::string &surfaceName) {
317 lastPlacedIds_.clear();
318 if (!active_ || !world_ || !ghost_ || mode_ != "place" ||
319 edgeCurveControlPoints_.size() != 4 || surfaceName.empty())
320 return EdgeLineExecuteStatus::Rejected;
322 request.kind = PlacementSystem::PatternKind::EdgeCubicBezier;
323 request.points = edgeCurveControlPoints_;
324 request.subdivisions = subdivisions;
325 request.surfaceName = surfaceName;
326 auto result = PlacementSystem::placePattern(world_, ghost_->getBuildingId(), request);
327 if (!result.ok()) return EdgeLineExecuteStatus::Rejected;
328 lastPlacedIds_ = std::move(result).takeValue().instanceIds;
329 edgeCurveControlPoints_.clear();
330 return EdgeLineExecuteStatus::Placed;
331}
332
333int PlacementSession::getLastPlacedId(int index) const {
334 if (index < 0 || index >= static_cast<int>(lastPlacedIds_.size())) return 0;
335 return lastPlacedIds_[static_cast<size_t>(index)];
336}
337
338PlacementEditExecuteStatus PlacementSession::executeMove(int instanceId) {
339 lastEditedId_ = 0;
340 if (!active_ || !world_ || !ghost_ || mode_ != "place")
341 return PlacementEditExecuteStatus::Rejected;
343 ghost_->getPlacementKind() == "edge"
344 ? PlacementSystem::moveEdgeResult(
345 world_, instanceId, ghost_->getCellX(), ghost_->getCellY(),
346 ghost_->getEdgeAxis() == "horizontal" ? "north" : "west")
347 : ghost_->getPlacementKind() == "corner"
348 ? PlacementSystem::moveCornerResult(world_, instanceId, ghost_->getCellX(),
349 ghost_->getCellY())
350 : ghost_->getPlacementKind() == "free"
351 ? (!ghost_->getSurfaceId().empty()
352 ? PlacementSystem::moveFreeSurfaceResult(
353 world_, instanceId, surfacePatchFromGhost(*world_, *ghost_),
354 ghost_->getRotationDeg())
355 : PlacementSystem::moveFreeResult(
356 world_, instanceId, ghost_->getWorldX(), ghost_->getWorldY(),
357 ghost_->getElevation(), ghost_->getRotationDeg()))
358 : PlacementSystem::moveBuildingResult(world_, instanceId, ghost_->getCellX(),
359 ghost_->getCellY(), ghost_->getRotationDeg());
360 if (!result.ok()) return PlacementEditExecuteStatus::Rejected;
361 lastEditedId_ = std::move(result).takeValue().after.instanceId;
362 return PlacementEditExecuteStatus::Committed;
363}
364
365PlacementEditExecuteStatus PlacementSession::executeReplace(int instanceId) {
366 lastEditedId_ = 0;
367 if (!active_ || !world_ || !ghost_ || mode_ != "place")
368 return PlacementEditExecuteStatus::Rejected;
369 auto result =
370 PlacementSystem::replaceBuildingResult(world_, instanceId, ghost_->getBuildingId());
371 if (!result.ok()) return PlacementEditExecuteStatus::Rejected;
372 lastEditedId_ = std::move(result).takeValue().after.instanceId;
373 return PlacementEditExecuteStatus::Committed;
374}
375
376AreaExecuteStatus PlacementSession::previewRectangle(int minCellX, int minCellY, int maxCellX,
377 int maxCellY) {
378 areaPreview_ = {};
379 if (!active_ || !world_ || !ghost_ || mode_ != "place") return AreaExecuteStatus::Rejected;
381 request.kind = PlacementSystem::PatternKind::RectangleFill;
382 request.points = {{static_cast<float>(minCellX), static_cast<float>(minCellY)},
383 {static_cast<float>(maxCellX), static_cast<float>(maxCellY)}};
384 request.rotationDeg = ghost_->getRotationDeg();
385 auto result = PlacementSystem::previewPattern(world_, ghost_->getBuildingId(), request);
386 if (!result.ok()) return AreaExecuteStatus::Rejected;
387 areaPreview_ = std::move(result).takeValue().area;
388 return areaPreview_.rejectedCount == 0 ? AreaExecuteStatus::Accepted
389 : AreaExecuteStatus::Rejected;
390}
391
392AreaExecuteStatus PlacementSession::previewBrush(int centerCellX, int centerCellY, int radius) {
393 areaPreview_ = {};
394 if (!active_ || !world_ || !ghost_ || mode_ != "place") return AreaExecuteStatus::Rejected;
396 request.kind = PlacementSystem::PatternKind::CircleBrush;
397 request.points = {{static_cast<float>(centerCellX), static_cast<float>(centerCellY)}};
398 request.radius = radius;
399 request.rotationDeg = ghost_->getRotationDeg();
400 auto result = PlacementSystem::previewPattern(world_, ghost_->getBuildingId(), request);
401 if (!result.ok()) return AreaExecuteStatus::Rejected;
402 areaPreview_ = std::move(result).takeValue().area;
403 return areaPreview_.rejectedCount == 0 ? AreaExecuteStatus::Accepted
404 : AreaExecuteStatus::Rejected;
405}
406
407AreaExecuteStatus PlacementSession::executeRectangle(int minCellX, int minCellY, int maxCellX,
408 int maxCellY) {
409 lastPlacedIds_.clear();
410 if (!active_ || !world_ || !ghost_ || mode_ != "place") return AreaExecuteStatus::Rejected;
412 request.kind = PlacementSystem::PatternKind::RectangleFill;
413 request.points = {{static_cast<float>(minCellX), static_cast<float>(minCellY)},
414 {static_cast<float>(maxCellX), static_cast<float>(maxCellY)}};
415 request.rotationDeg = ghost_->getRotationDeg();
416 auto result = PlacementSystem::placePattern(world_, ghost_->getBuildingId(), request);
417 if (!result.ok()) return AreaExecuteStatus::Rejected;
418 auto placement = std::move(result).takeValue();
419 areaPreview_ = std::move(placement.preview.area);
420 lastPlacedIds_ = std::move(placement.instanceIds);
421 return AreaExecuteStatus::Accepted;
422}
423
424AreaExecuteStatus PlacementSession::executeBrush(int centerCellX, int centerCellY, int radius) {
425 lastPlacedIds_.clear();
426 if (!active_ || !world_ || !ghost_ || mode_ != "place") return AreaExecuteStatus::Rejected;
428 request.kind = PlacementSystem::PatternKind::CircleBrush;
429 request.points = {{static_cast<float>(centerCellX), static_cast<float>(centerCellY)}};
430 request.radius = radius;
431 request.rotationDeg = ghost_->getRotationDeg();
432 auto result = PlacementSystem::placePattern(world_, ghost_->getBuildingId(), request);
433 if (!result.ok()) return AreaExecuteStatus::Rejected;
434 auto placement = std::move(result).takeValue();
435 areaPreview_ = std::move(placement.preview.area);
436 lastPlacedIds_ = std::move(placement.instanceIds);
437 return AreaExecuteStatus::Accepted;
438}
439
440int PlacementSession::getAreaPreviewCellX(int index) const {
441 return index >= 0 && index < getAreaPreviewCount() ? areaPreview_.cells[size_t(index)].cellX : 0;
442}
443int PlacementSession::getAreaPreviewCellY(int index) const {
444 return index >= 0 && index < getAreaPreviewCount() ? areaPreview_.cells[size_t(index)].cellY : 0;
445}
446bool PlacementSession::getAreaPreviewAccepted(int index) const {
447 return index >= 0 && index < getAreaPreviewCount() && areaPreview_.cells[size_t(index)].accepted;
448}
449std::string PlacementSession::getAreaPreviewReason(int index) const {
450 return index >= 0 && index < getAreaPreviewCount() ? areaPreview_.cells[size_t(index)].reason
451 : std::string{};
452}
453
454PatternUpdateStatus PlacementSession::beginPattern(const std::string &kind) {
455 const auto parsed = patternKindFromName(kind);
456 if (!active_ || !world_ || !ghost_ || mode_ != "place" || !parsed)
457 return PatternUpdateStatus::Rejected;
458 const BuildingDefinition *definition = BuildingRegistry::find(ghost_->getBuildingId());
459 if (!definition || (edgePattern(*parsed) != (definition->placementKind == "edge")))
460 return PatternUpdateStatus::Rejected;
461 patternRequest_ = {};
462 patternRequest_.kind = *parsed;
463 patternRequest_.rotationDeg = ghost_->getRotationDeg();
464 patternPreview_ = {};
465 patternPreview_.request = patternRequest_;
466 areaPreview_ = {};
467 patternActive_ = true;
468 return PatternUpdateStatus::Updated;
469}
470
471PatternUpdateStatus PlacementSession::appendPatternPoint(float x, float y) {
472 if (!patternActive_ || !std::isfinite(x) || !std::isfinite(y))
473 return PatternUpdateStatus::Rejected;
474 size_t maximum = std::numeric_limits<size_t>::max();
475 if (patternRequest_.kind == PlacementSystem::PatternKind::EdgeLine ||
476 patternRequest_.kind == PlacementSystem::PatternKind::RectangleFill ||
477 patternRequest_.kind == PlacementSystem::PatternKind::RectangleOutline)
478 maximum = 2;
479 else if (patternRequest_.kind == PlacementSystem::PatternKind::EdgeCubicBezier)
480 maximum = 4;
481 else if (patternRequest_.kind == PlacementSystem::PatternKind::CircleBrush)
482 maximum = 1;
483 if (patternRequest_.points.size() >= maximum) return PatternUpdateStatus::Rejected;
484 patternRequest_.points.push_back({x, y});
485 patternPreview_ = {};
486 patternPreview_.request = patternRequest_;
487 return PatternUpdateStatus::Updated;
488}
489
490PatternUpdateStatus PlacementSession::setPatternRadius(int radius) {
491 if (!patternActive_ || radius < 0) return PatternUpdateStatus::Rejected;
492 patternRequest_.radius = radius;
493 return PatternUpdateStatus::Updated;
494}
495
496PatternUpdateStatus PlacementSession::setPatternSubdivisions(int subdivisions) {
497 if (!patternActive_ || subdivisions <= 0) return PatternUpdateStatus::Rejected;
498 patternRequest_.subdivisions = subdivisions;
499 return PatternUpdateStatus::Updated;
500}
501
502PatternUpdateStatus PlacementSession::setPatternSurface(const std::string &surfaceName) {
503 if (!patternActive_ ||
504 patternRequest_.kind != PlacementSystem::PatternKind::EdgeCubicBezier)
505 return PatternUpdateStatus::Rejected;
506 patternRequest_.surfaceName = surfaceName;
507 return PatternUpdateStatus::Updated;
508}
509
510PatternUpdateStatus PlacementSession::previewPattern() {
511 patternPreview_ = {};
512 areaPreview_ = {};
513 if (!patternActive_ || !world_ || !ghost_ || mode_ != "place")
514 return PatternUpdateStatus::Rejected;
515 patternRequest_.rotationDeg = ghost_->getRotationDeg();
516 auto preview = PlacementSystem::previewPattern(world_, ghost_->getBuildingId(),
517 patternRequest_);
518 if (!preview.ok()) return PatternUpdateStatus::Rejected;
519 patternPreview_ = std::move(preview).takeValue();
520 areaPreview_ = patternPreview_.area;
521 if (!edgePattern(patternRequest_.kind) && patternPreview_.area.rejectedCount != 0)
522 return PatternUpdateStatus::Rejected;
523 return PatternUpdateStatus::Updated;
524}
525
526PatternExecuteStatus PlacementSession::executePattern() {
527 lastPlacedIds_.clear();
528 if (!patternActive_ || !world_ || !ghost_ || mode_ != "place")
529 return PatternExecuteStatus::Rejected;
530 patternRequest_.rotationDeg = ghost_->getRotationDeg();
531 auto placed = PlacementSystem::placePattern(world_, ghost_->getBuildingId(),
532 patternRequest_);
533 if (!placed.ok()) return PatternExecuteStatus::Rejected;
534 auto receipt = std::move(placed).takeValue();
535 patternPreview_ = std::move(receipt.preview);
536 areaPreview_ = patternPreview_.area;
537 lastPlacedIds_ = std::move(receipt.instanceIds);
538 return PatternExecuteStatus::Placed;
539}
540
541std::string PlacementSession::getPatternKind() const {
542 return patternActive_ ? patternKindName(patternRequest_.kind) : std::string{};
543}
544
545int PlacementSession::getPatternPreviewX(int index) const {
546 if (index < 0 || index >= getPatternPreviewCount()) return 0;
547 return edgePattern(patternPreview_.request.kind)
548 ? patternPreview_.edge.edges[static_cast<size_t>(index)].x
549 : patternPreview_.area.cells[static_cast<size_t>(index)].cellX;
550}
551
552int PlacementSession::getPatternPreviewY(int index) const {
553 if (index < 0 || index >= getPatternPreviewCount()) return 0;
554 return edgePattern(patternPreview_.request.kind)
555 ? patternPreview_.edge.edges[static_cast<size_t>(index)].y
556 : patternPreview_.area.cells[static_cast<size_t>(index)].cellY;
557}
558
559std::string PlacementSession::getPatternPreviewAxis(int index) const {
560 if (index < 0 || index >= getPatternPreviewCount() ||
561 !edgePattern(patternPreview_.request.kind))
562 return {};
563 return patternPreview_.edge.edges[static_cast<size_t>(index)].axis ==
564 EdgeAxis::Horizontal
565 ? "horizontal"
566 : "vertical";
567}
568
569bool PlacementSession::getPatternPreviewAccepted(int index) const {
570 if (index < 0 || index >= getPatternPreviewCount()) return false;
571 return edgePattern(patternPreview_.request.kind) ||
572 patternPreview_.area.cells[static_cast<size_t>(index)].accepted;
573}
574
575std::string PlacementSession::getPatternPreviewReason(int index) const {
576 if (index < 0 || index >= getPatternPreviewCount() ||
577 edgePattern(patternPreview_.request.kind))
578 return {};
579 return patternPreview_.area.cells[static_cast<size_t>(index)].reason;
580}
581
582} // namespace eve::building
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float maximum[3]
const GltfImportRequest & request
Module-neutral grid topology and sizing (no building/map dependency).
float elevation
TokenKind kind
std::string name
graphics::Canvas * previous
World3D * world
float radius
RoadLaneDirection direction
bool placed
uint32_t index
Move-only operation result carrying either a value or Status.
Definition Result.h:155
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:255
static const BuildingDefinition * find(const std::string &id)
Finds .
EVENGINE_API_WORLD public API.
Definition Ghost.h:16
int getCellY() const
Returns the cell y.
Definition Ghost.h:34
float getWorldY() const
Returns the world y.
Definition Ghost.h:41
void setFree(PlacementWorld *world, float worldX, float worldY, float elevation=0.f)
Set an exact unsnapped world-plane anchor for a free-domain definition.
Definition Ghost.cpp:207
void setFromWorld(PlacementWorld *world, float worldX, float worldY)
按世界吸附模式,从世界坐标刷新格子与世界位姿。
Definition Ghost.cpp:105
void setRotationDeg(float deg)
Sets the rotation deg.
Definition Ghost.cpp:94
int getCellX() const
Returns the cell x.
Definition Ghost.h:32
bool validate(PlacementWorld *world)
对当前姿态做校验,写入 valid_/reason_。
Definition Ghost.cpp:270
void setFromWorld3D(PlacementWorld *world, float worldX, float worldY, float worldZ)
Sets the from world 3 d.
Definition Ghost.cpp:131
std::string getReason() const
Returns the reason.
Definition Ghost.h:69
void setCorner(PlacementWorld *world, int vertexX, int vertexY)
Snap this ghost to one canonical grid vertex.
Definition Ghost.cpp:190
bool isValid() const
True when valid.
Definition Ghost.h:67
std::string getPlacementKind() const
Current placement domain (cell, edge, corner, or free).
Definition Ghost.h:56
float getWorldX() const
Returns the world x.
Definition Ghost.h:39
float getRotationDeg() const
Returns the rotation deg.
Definition Ghost.h:50
std::string getEdgeAxis() const
Canonical edge axis (horizontal/vertical), empty for cell ghosts.
Definition Ghost.cpp:157
void setEdge(PlacementWorld *world, int cellX, int cellY, const std::string &direction)
Snap this ghost to a canonicalized cell edge and its exact midpoint pose.
Definition Ghost.cpp:162
std::string getBuildingId() const
Returns the building id.
Definition Ghost.h:27
void rotateBy(float deltaDeg)
Rotate by.
Definition Ghost.cpp:101
void setFromSurface(PlacementWorld *world, const std::string &surface, float x, float y)
Sets the from surface.
Definition Ghost.cpp:225
void setBuildingId(const std::string &id)
Sets the building id.
Definition Ghost.cpp:13
void destroy()
Destroys .
Definition Ghost.cpp:11
float getRotationDeg() const
Returns the rotation deg.
void stopPlacement()
Stops placement.
void setRotationDeg(float deg)
Sets the rotation deg.
void setMode(const std::string &mode)
Sets the mode.
bool startPlacement(PlacementWorld *world, const std::string &buildingId)
Starts placement.
FreeUpdateStatus updateFree(PlacementWorld *world, float worldX, float worldY, float elevation=0.f)
Refresh a free-object ghost at an exact world-plane position.
bool isValid() const
True when valid.
std::string getReason() const
Returns the reason.
EdgeUpdateStatus updateEdge(PlacementWorld *world, int cellX, int cellY, const std::string &direction)
Refresh an edge-object ghost from a cell-relative cardinal edge.
bool updateFromWorld3D(PlacementWorld *world, float worldX, float worldY, float worldZ)
Updates from world 3 d.
EdgeLineExecuteStatus executeEdgeLine(int startVertexX, int startVertexY, int endVertexX, int endVertexY)
Atomically place an axis-aligned edge line between logical vertices.
std::string getBuildingId() const
Returns the building id.
CornerUpdateStatus updateCorner(PlacementWorld *world, int vertexX, int vertexY)
Refresh a corner-object ghost at a canonical grid vertex.
EdgePathUpdateStatus appendEdgePathVertex(int vertexX, int vertexY)
Append one orthogonally connected logical vertex to the current edge path.
bool updateFromWorld(PlacementWorld *world, float worldX, float worldY)
Updates from world.
EdgeLineExecuteStatus executeEdgePath()
Atomically commit the complete edge path.
void rotateBy(float deltaDeg)
Rotate by.
EdgePathUpdateStatus beginEdgePath(int vertexX, int vertexY)
Start a multi-segment edge path at one logical grid vertex.
bool updateFromSurface(PlacementWorld *world, const std::string &surface, float x, float y)
Updates from surface.
static eve::Result< PatternPlacement > placePattern(PlacementWorld *world, const std::string &buildingId, const PatternRequest &request)
Validate and atomically commit any built-in placement pattern.
PatternKind
Built-in geometry families accepted by the unified placement-pattern protocol.
格子型建筑放置世界(脚本可直接操作)。
int getOccupant(int cellX, int cellY) const
占用查询:格子上建筑实例 / 是否为空。
bool removeBuilding(int instanceId)
Removes building.
int getEdgeOccupant(const std::string &channel, int cellX, int cellY, const std::string &direction) const
Return the occupant of a canonicalized north/east/south/west cell edge.
int placeGhost(Ghost *ghost)
Place ghost.
int getCornerOccupant(const std::string &channel, int vertexX, int vertexY) const
Query a corner-domain occupant at the active floor.
int getFreeOccupant(const std::string &channel, float worldX, float worldY) const
Return the first free-domain object whose circular footprint contains the point.
建筑放置模块入口:定义 / 放置世界 / 鬼影 / 变更事件的脚本绑定点。 设计文档:docs/dev/建筑放置系统设计.md
Definition Building.cpp:7
EdgeLineExecuteStatus
Outcome of committing one continuous edge line.
AreaExecuteStatus
Outcome of refreshing or committing an area tool request.
EdgePathUpdateStatus
Outcome of adding a logical vertex to an edge path preview.
FreeUpdateStatus
Outcome of refreshing an exact free-object placement preview.
PatternUpdateStatus
Outcome of configuring or previewing a unified placement pattern.
CornerUpdateStatus
Outcome of refreshing a corner placement preview.
PlacementEditExecuteStatus
Outcome of committing one move or replace edit.
PatternExecuteStatus
Outcome of atomically committing a unified placement pattern.
EdgeUpdateStatus
Outcome of refreshing an edge placement preview.
建筑模板(进程级注册表中的定义)。
Canonical address of one logical grid vertex used by corner objects.
Owning, renderer-neutral request for one atomic placement pattern.