载入中...
搜索中...
未找到
ArchSpaceTools.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <array>
5#include <cmath>
6#include <span>
7#include <utility>
8
9namespace eve::editor {
10namespace {
11
12constexpr double kEps = 1e-8;
13
14bool intersectFloor(const ArchSpaceViewportRay& ray, double elevation, archspace::Vec2& out) {
15 if (std::abs(ray.direction[1]) <= kEps) return false;
16 const double t = (elevation - ray.origin[1]) / ray.direction[1];
17 if (t < 0.0) return false;
18 out.x = ray.origin[0] + ray.direction[0] * t;
19 out.z = ray.origin[2] + ray.direction[2] * t;
20 return std::isfinite(out.x) && std::isfinite(out.z);
21}
22
23archspace::Vec2 snapOrtho(const archspace::Vec2& start, const archspace::Vec2& end) {
24 if (std::abs(end.x - start.x) >= std::abs(end.z - start.z)) return {end.x, start.z};
25 return {start.x, end.z};
26}
27
28bool projectOntoWall(const archspace::Node& wall, const archspace::Vec2& point, double& t, double& dist) {
29 const double dx = wall.end.x - wall.start.x;
30 const double dz = wall.end.z - wall.start.z;
31 const double len2 = dx * dx + dz * dz;
32 if (len2 <= kEps) return false;
33 t = std::clamp(((point.x - wall.start.x) * dx + (point.z - wall.start.z) * dz) / len2, 0.0, 1.0);
34 const double px = wall.start.x + dx * t;
35 const double pz = wall.start.z + dz * t;
36 dist = std::sqrt((point.x - px) * (point.x - px) + (point.z - pz) * (point.z - pz));
37 return true;
38}
39
40bool commitDomain(editing::IEditAuthority* authority, eve::archspace_editing::ArchSpaceDocumentTarget& target,
41 editing::DomainOperation operation, const ToolDescriptor& descriptor, std::uint64_t sequence,
42 std::string mergeKey, std::optional<editing::TransactionReceipt>& outReceipt) {
43 if (!authority) return false;
44 editing::TransactionSpec transaction;
45 transaction.id = editing::TransactionId(descriptor.id + "." + std::to_string(sequence));
46 transaction.label = descriptor.label;
47 transaction.origin = editing::ActionOrigin::User;
48 transaction.target = target.targetId();
49 transaction.baseRevision = target.revision();
50 transaction.mergeKey = std::move(mergeKey);
51 const std::array<editing::DomainOperation, 1> operations{std::move(operation)};
52 auto plan = authority->preflight(transaction, std::span<const editing::DomainOperation>(operations));
53 if (!plan.ok()) return false;
54 auto receipt = authority->commit(plan.value());
55 if (!receipt.ok()) return false;
56 outReceipt = std::move(receipt.value());
57 return true;
58}
59
60} // namespace
61
63 : viewport_(viewport), authority_(authority) {}
64
66 viewport_ = viewport;
67 drawing_ = false;
68}
69
71 authority_ = authority;
72 drawing_ = false;
73}
74
75void ArchSpaceWallDrawTool::setLevelId(std::string levelId) { levelId_ = std::move(levelId); }
76
77bool ArchSpaceWallDrawTool::hitFloor(const EditorPointerEvent& event, archspace::Vec2& out) const {
78 if (!viewport_) return false;
79 auto ray = viewport_->pointerRay(event);
80 if (!ray.ok()) return false;
81 return intersectFloor(ray.value(), 0.0, out);
82}
83
84ToolResponse ArchSpaceWallDrawTool::begin(EditorContext&, const EditorPointerEvent& event) {
85 if (levelId_.empty() || !viewport_) return ToolResponse::ignored();
87 if (!hitFloor(event, point)) return ToolResponse::ignored();
88 if (!(contiguous_ && haveAnchor_ && !lastWallId_.empty())) {
89 anchor_ = point;
90 haveAnchor_ = true;
91 }
92 cursor_ = point;
93 drawing_ = true;
94 return ToolResponse::capture();
95}
96
97ToolResponse ArchSpaceWallDrawTool::move(const EditorPointerEvent& event) {
98 if (!drawing_) return ToolResponse::ignored();
99 archspace::Vec2 point;
100 if (!hitFloor(event, point)) return ToolResponse::consumed();
101 cursor_ = event.shift ? snapOrtho(anchor_, point) : point;
102 return ToolResponse::consumed();
103}
104
105bool ArchSpaceWallDrawTool::commitWall(eve::archspace_editing::ArchSpaceDocumentTarget& target, archspace::Vec2 start,
106 archspace::Vec2 end) {
107 const double dx = end.x - start.x;
108 const double dz = end.z - start.z;
109 if (std::sqrt(dx * dx + dz * dz) <= 0.05) return false;
110 const std::string wallId = idPrefix_ + "." + std::to_string(++sequence_);
111 auto operation = target.makeCreateWall(levelId_, wallId, wallId, start, end, wallHeight_, wallThickness_);
112 if (!operation.ok()) return false;
113 if (!commitDomain(authority_, target, std::move(operation.value()), descriptor_, sequence_, "archspace.wall.draw",
114 lastReceipt_))
115 return false;
116 lastWallId_ = wallId;
117 if (contiguous_) {
118 anchor_ = end;
119 haveAnchor_ = true;
120 }
121 return true;
122}
123
124ToolResponse ArchSpaceWallDrawTool::finish(EditorContext& context, const EditorPointerEvent& event) {
125 if (!drawing_) return ToolResponse::ignored();
126 (void)move(event);
127 drawing_ = false;
128 auto* target = dynamic_cast<eve::archspace_editing::ArchSpaceDocumentTarget*>(context.target());
129 if (!target || !authority_) return ToolResponse::release();
130 (void)commitWall(*target, anchor_, cursor_);
131 return ToolResponse::release();
132}
133
136 return event.button == 0 ? begin(context, event) : ToolResponse::ignored();
137 if (event.phase == EditorPointerEvent::Phase::Move) return move(event);
138 if (event.phase == EditorPointerEvent::Phase::Up) return finish(context, event);
141 return ToolResponse::release();
142 }
143 return ToolResponse::ignored();
144}
145
148
150 if (!drawing_ || !viewport_) return;
151 auto from = viewport_->projectWorld({anchor_.x, 0.05, anchor_.z});
152 auto to = viewport_->projectWorld({cursor_.x, 0.05, cursor_.z});
153 if (!from.ok() || !to.ok()) return;
154 OverlayStyle style;
155 style.color = 0xff4fc3f7U;
156 style.thickness = 2.f;
157 overlay.line(from.value(), to.value(), style);
158 overlay.circle(from.value(), 5.f, style);
159 overlay.circle(to.value(), 5.f, style);
160}
161
163 inspector.beginGroup("archspace.wall", "Wall Draw");
164 float height = static_cast<float>(wallHeight_);
165 float thickness = static_cast<float>(wallThickness_);
166 if (inspector.scalar("height", "Height", height, 0.5f, 10.f)) wallHeight_ = height;
167 if (inspector.scalar("thickness", "Thickness", thickness, 0.05f, 1.0f)) wallThickness_ = thickness;
168 inspector.boolean("contiguous", "Contiguous", contiguous_);
169 inspector.string("levelId", "Level Id", levelId_);
170 inspector.endGroup();
171}
172
174 editing::IEditAuthority* authority)
175 : viewport_(viewport), authority_(authority) {}
176
178 viewport_ = viewport;
179 preview_.reset();
180}
182 authority_ = authority;
183 preview_.reset();
184}
185
186bool ArchSpaceOpeningPlaceTool::hitFloor(const EditorPointerEvent& event, archspace::Vec2& out) const {
187 if (!viewport_) return false;
188 auto ray = viewport_->pointerRay(event);
189 if (!ray.ok()) return false;
190 return intersectFloor(ray.value(), 0.0, out);
191}
192
193std::optional<ArchSpaceOpeningPlaceTool::Pick> ArchSpaceOpeningPlaceTool::pickWall(
195 std::optional<Pick> best;
196 double bestDist = 1e9;
197 for (const auto& [id, node] : target.document().nodes()) {
198 if (node.kind != archspace::NodeKind::Wall) continue;
199 double t = 0.5;
200 double dist = 0.0;
201 if (!projectOntoWall(node, point, t, dist)) continue;
202 if (dist > std::max(0.75, node.thickness * 2.0)) continue;
203 if (dist >= bestDist) continue;
204 bestDist = dist;
205 best = Pick{
206 id, t, {node.start.x + (node.end.x - node.start.x) * t, node.start.z + (node.end.z - node.start.z) * t}};
207 }
208 return best;
209}
210
212 auto* target = dynamic_cast<eve::archspace_editing::ArchSpaceDocumentTarget*>(context.target());
213 if (!target || !viewport_) return ToolResponse::ignored();
215 if (!hitFloor(event, point)) {
216 preview_.reset();
218 }
219 preview_ = pickWall(*target, point);
221 if (event.phase == EditorPointerEvent::Phase::Down && event.button == 0 && preview_) return ToolResponse::capture();
223 preview_.reset();
224 return ToolResponse::release();
225 }
226 if (event.phase != EditorPointerEvent::Phase::Up || event.button != 0) return ToolResponse::ignored();
227 if (!preview_ || !authority_) return ToolResponse::release();
228 const std::string openingId = idPrefix_ + "." + std::to_string(++sequence_);
229 auto operation = target->makeCreateOpening(preview_->wallId, openingId, kind_, preview_->t, width_, height_, sill_);
230 if (!operation.ok()) return ToolResponse::release();
231 if (commitDomain(authority_, *target, std::move(operation.value()), descriptor_, sequence_,
232 "archspace.opening.place", lastReceipt_))
233 lastOpeningId_ = openingId;
234 preview_.reset();
235 return ToolResponse::release();
236}
237
240
242 if (!preview_ || !viewport_) return;
243 auto center = viewport_->projectWorld({preview_->point.x, height_ * 0.5, preview_->point.z});
244 if (!center.ok()) return;
245 OverlayStyle style;
246 style.color = kind_ == archspace::OpeningKind::Door ? 0xffffb74dU : 0xff81d4faU;
247 style.thickness = 2.f;
248 overlay.circle(center.value(), 8.f, style);
249 overlay.text(center.value(), kind_ == archspace::OpeningKind::Door ? "door" : "window", style);
250}
251
253 inspector.beginGroup("archspace.opening", "Opening Place");
254 bool isDoor = kind_ == archspace::OpeningKind::Door;
255 if (inspector.boolean("door", "Door (else window)", isDoor))
257 float width = static_cast<float>(width_);
258 float height = static_cast<float>(height_);
259 float sill = static_cast<float>(sill_);
260 if (inspector.scalar("width", "Width", width, 0.3f, 3.0f)) width_ = width;
261 if (inspector.scalar("height", "Height", height, 0.3f, 4.0f)) height_ = height;
262 if (inspector.scalar("sill", "Sill", sill, 0.0f, 2.5f)) sill_ = sill;
263 inspector.endGroup();
264}
265
267 : viewport_(viewport), authority_(authority) {}
268
270 viewport_ = viewport;
271 preview_.reset();
272}
274 authority_ = authority;
275 preview_.reset();
276}
277void ArchSpaceItemPlaceTool::setLevelId(std::string levelId) { levelId_ = std::move(levelId); }
278void ArchSpaceItemPlaceTool::setCatalogId(std::string catalogId) { catalogId_ = std::move(catalogId); }
279
280bool ArchSpaceItemPlaceTool::hitFloor(const EditorPointerEvent& event, archspace::Vec2& out) const {
281 if (!viewport_) return false;
282 auto ray = viewport_->pointerRay(event);
283 if (!ray.ok()) return false;
284 return intersectFloor(ray.value(), 0.0, out);
285}
286
288 auto* target = dynamic_cast<eve::archspace_editing::ArchSpaceDocumentTarget*>(context.target());
289 if (!target || levelId_.empty() || !viewport_) return ToolResponse::ignored();
291 if (!hitFloor(event, point)) {
292 preview_.reset();
294 }
295 preview_ = point;
297 if (event.phase == EditorPointerEvent::Phase::Down && event.button == 0) return ToolResponse::capture();
299 preview_.reset();
300 return ToolResponse::release();
301 }
302 if (event.phase != EditorPointerEvent::Phase::Up || event.button != 0) return ToolResponse::ignored();
303 if (!authority_) return ToolResponse::release();
304 const std::string itemId = idPrefix_ + "." + std::to_string(++sequence_);
305 auto operation =
306 target->makePlaceItem(levelId_, itemId, catalogId_, archspace::Vec3{point.x, 0.0, point.z}, yawDegrees_);
307 if (!operation.ok()) return ToolResponse::release();
308 if (commitDomain(authority_, *target, std::move(operation.value()), descriptor_, sequence_, "archspace.item.place",
309 lastReceipt_))
310 lastItemId_ = itemId;
311 preview_.reset();
312 return ToolResponse::release();
313}
314
317
319 if (!preview_ || !viewport_) return;
320 auto center = viewport_->projectWorld({preview_->x, 0.05, preview_->z});
321 if (!center.ok()) return;
322 OverlayStyle style;
323 style.color = 0xffaed581U;
324 style.thickness = 2.f;
325 overlay.circle(center.value(), 7.f, style);
326 overlay.text(center.value(), catalogId_, style);
327}
328
330 inspector.beginGroup("archspace.item", "Item Place");
331 inspector.string("levelId", "Level Id", levelId_);
332 inspector.string("catalogId", "Catalog Id", catalogId_);
333 float yaw = static_cast<float>(yawDegrees_);
334 if (inspector.scalar("yaw", "Yaw Degrees", yaw, -180.f, 180.f)) yawDegrees_ = yaw;
335 inspector.endGroup();
336}
337
338} // namespace eve::editor
LogicalId target
ActionParameterOperation operation
Duration start
LogicalId itemId
std::string descriptor
std::string from
float thickness
Definition CaveMesh.cpp:83
float pz
float elevation
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
std::vector< Colorf > px
std::uint32_t height
std::uint32_t width
std::vector< BvhNode > nodes
std::unique_ptr< gpgpu::Sequence > sequence
Definition OnnxGpgpu.cpp:43
std::string id
Definition PlayHost.cpp:108
float begin
float t
const RoadNode * node
float dz
float dx
std::map< Cell, int > best
const VegetationPresetContext & context
glm::vec3 point
Revisioned ArchSpace document target for Inspector, tools and automation.
Final validation and commit boundary for editor mutations.
void cancel(EditorContext &context) override
Cancel.
void setLevelId(std::string levelId)
Sets the level id.
void setViewportAdapter(IArchSpaceViewportAdapter *viewport)
Sets the viewport adapter.
void drawOverlay(EditorContext &context, IEditorOverlay &overlay) override
Draws overlay.
void setCatalogId(std::string catalogId)
Sets the catalog id.
void setAuthority(editing::IEditAuthority *authority)
Sets the authority.
void deactivate(EditorContext &context) override
Deactivate.
void inspect(EditorContext &context, IEditorInspector &inspector) override
Inspect.
ToolResponse pointerEvent(EditorContext &context, const EditorPointerEvent &event) override
Pointer event.
ArchSpaceItemPlaceTool(IArchSpaceViewportAdapter *viewport, editing::IEditAuthority *authority)
Arch space item place tool.
ToolResponse pointerEvent(EditorContext &context, const EditorPointerEvent &event) override
Pointer event.
void cancel(EditorContext &context) override
Cancel.
ArchSpaceOpeningPlaceTool(IArchSpaceViewportAdapter *viewport, editing::IEditAuthority *authority)
Arch space opening place tool.
void inspect(EditorContext &context, IEditorInspector &inspector) override
Inspect.
void deactivate(EditorContext &context) override
Deactivate.
void setAuthority(editing::IEditAuthority *authority)
Sets the authority.
void drawOverlay(EditorContext &context, IEditorOverlay &overlay) override
Draws overlay.
void setViewportAdapter(IArchSpaceViewportAdapter *viewport)
Sets the viewport adapter.
void setLevelId(std::string levelId)
Sets the level id.
void setViewportAdapter(IArchSpaceViewportAdapter *viewport)
Sets the viewport adapter.
void inspect(EditorContext &context, IEditorInspector &inspector) override
Inspect.
ToolResponse pointerEvent(EditorContext &context, const EditorPointerEvent &event) override
Pointer event.
void deactivate(EditorContext &context) override
Deactivate.
void cancel(EditorContext &context) override
Cancel.
ArchSpaceWallDrawTool(IArchSpaceViewportAdapter *viewport, editing::IEditAuthority *authority)
Arch space wall draw tool.
void setAuthority(editing::IEditAuthority *authority)
Sets the authority.
void drawOverlay(EditorContext &context, IEditorOverlay &overlay) override
Draws overlay.
Context supplied by EditorSession to tools.
Definition EditorTool.h:71
Host-owned conversion boundary between pointer coordinates and the ArchSpace viewport.
virtual editing::Result< OverlayPoint > projectWorld(const std::array< double, 3 > &world) const =0
Project a world position into the coordinate space accepted by IEditorOverlay.
virtual editing::Result< ArchSpaceViewportRay > pointerRay(const EditorPointerEvent &event) const =0
Build a world ray for one normalized pointer event.
UI-independent property editor used by tools to expose settings.
virtual void endGroup()=0
End the current logical group.
virtual void beginGroup(const std::string &id, const std::string &label)=0
Begin a logical group of properties.
virtual bool boolean(const std::string &id, const std::string &label, bool &value)=0
Present an editable boolean property.
virtual bool scalar(const std::string &id, const std::string &label, float &value, float minimum, float maximum)=0
Present an editable scalar property.
virtual bool string(const std::string &id, const std::string &label, std::string &value)=0
Present an editable text property.
Sink implemented by a 2D or 3D viewport to render tool feedback.
virtual void text(const OverlayPoint &position, const std::string &value, const OverlayStyle &style)=0
Draw a short text annotation.
virtual void circle(const OverlayPoint &center, float radius, const OverlayStyle &style)=0
Draw a circle whose orientation is supplied by the viewport.
virtual void line(const OverlayPoint &from, const OverlayPoint &to, const OverlayStyle &style)=0
Draw a line between two viewport-space points.
StrongId< TransactionIdTag > TransactionId
Definition EditingIds.h:62
Metres on the level XZ plane.
Metres in world/level space.
Pointer input normalized by an editor viewport adapter.
Definition EditorTool.h:20
Renderer-independent appearance for an editor overlay primitive.
Result returned by a tool after processing input.
Definition EditorTool.h:50
static ToolResponse ignored()
Ignored.
Definition EditorTool.h:56
static ToolResponse capture()
Capture.
Definition EditorTool.h:60
static ToolResponse release()
Release.
Definition EditorTool.h:62
static ToolResponse consumed()
Consumed.
Definition EditorTool.h:58