载入中...
搜索中...
未找到
BuildingEdgeCurveTool.cpp
浏览该文件的文档.
2
4
5#include <algorithm>
6#include <charconv>
7#include <cmath>
8#include <span>
9
10namespace eve::editor {
11namespace {
12
13editing::Result<void> toolFailure(editing::Status status, const char* rule,
14 std::string message) {
15 return editing::failed<void>(status, editing::RuleId(rule), std::move(message));
16}
17
18OverlayStyle styleFor(const editing::GizmoPrimitive& primitive) {
19 const auto channel = [&](size_t index) {
20 return static_cast<unsigned int>(std::clamp(primitive.color[index], 0.0, 1.0) *
21 255.0 + 0.5);
22 };
23 const unsigned int color = (channel(3) << 24U) | (channel(0) << 16U) |
24 (channel(1) << 8U) | channel(2);
25 return {color, primitive.dashed ? 1.f : 2.f, false};
26}
27
28} // namespace
29
31 editing::IEditAuthority* authority)
32 : viewport_(viewport), authority_(authority) {}
33
35 if (drag_) drag_->cancelDrag();
36 drag_.reset();
37 viewport_ = viewport;
38}
39
41 if (drag_) drag_->cancelDrag();
42 drag_.reset();
43 authority_ = authority;
44}
45
48 if (isDragging())
49 return toolFailure(editing::Status::Conflict,
50 "editor.building.curve-tool-gesture-active",
51 "Cannot replace curve selection during an active drag");
52 if (selection.buildingId.empty() || selection.memberInstanceId <= 0 ||
53 selection.controlPoints.size() != 4 || selection.subdivisions <= 0)
54 return toolFailure(editing::Status::Rejected,
55 "editor.building.curve-tool-selection-invalid",
56 "Curve selection requires a building, member, four controls and positive subdivisions");
57 selection_ = std::move(selection);
58 preview_.reset();
59 lastReceipt_.reset();
60 diagnostics_.clear();
61 lastStatus_ = editing::Status::Applied;
62 return editing::applied<void>();
63}
64
66 if (drag_) drag_->cancelDrag();
67 drag_.reset();
68 selection_.reset();
69 preview_.reset();
70 diagnostics_.clear();
71 lastStatus_ = editing::Status::NotFound;
72}
73
74bool BuildingEdgeCurveTool::isDragging() const { return drag_ && drag_->isDragging(); }
75
76void BuildingEdgeCurveTool::recordFailure(
78 const std::vector<editing::Diagnostic>& diagnostics) {
79 lastStatus_ = status;
80 diagnostics_ = diagnostics;
81}
82
83void BuildingEdgeCurveTool::recordPreview(
85 lastStatus_ = preview.status;
86 diagnostics_ = preview.diagnostics;
87 preview_ = std::move(preview);
88}
89
90ToolResponse BuildingEdgeCurveTool::begin(EditorContext& context,
91 const EditorPointerEvent& event) {
92 auto* target = dynamic_cast<BuildingPlacementTarget*>(context.target());
93 if (!target || !selection_ || !viewport_) return ToolResponse::ignored();
94 auto ray = viewport_->pointerRay(event);
95 if (!ray.ok()) {
96 recordFailure(ray.code(), ray.diagnostics());
97 return ToolResponse::ignored();
98 }
99 drag_ = std::make_unique<building_editing::BuildingEdgeCurveDragSession>(
100 target, selection_->buildingId, selection_->memberInstanceId,
101 selection_->controlPoints, selection_->subdivisions, selection_->surfaceName);
102 const BuildingViewportRay& value = ray.value();
103 auto begun = drag_->beginDrag(value.origin[0], value.origin[1], value.origin[2],
104 value.direction[0], value.direction[1],
105 value.direction[2]);
106 if (!begun.ok()) {
107 recordFailure(begun.code(), begun.diagnostics());
108 drag_.reset();
109 return ToolResponse::ignored();
110 }
111 recordPreview(std::move(begun.value()));
112 return ToolResponse::capture();
113}
114
115ToolResponse BuildingEdgeCurveTool::move(const EditorPointerEvent& event) {
116 if (!isDragging()) return ToolResponse::ignored();
117 if (!viewport_) {
118 auto failure = editing::failed<BuildingViewportRay>(
119 editing::Status::Failed,
120 editing::RuleId("editor.building.curve-tool-viewport-missing"),
121 "Curve tool viewport adapter is unavailable");
122 recordFailure(failure.code(), failure.diagnostics());
123 return ToolResponse::consumed();
124 }
125 auto ray = viewport_->pointerRay(event);
126 if (!ray.ok()) {
127 recordFailure(ray.code(), ray.diagnostics());
128 return ToolResponse::consumed();
129 }
130 const BuildingViewportRay& value = ray.value();
131 auto moved = drag_->updateDrag(value.origin[0], value.origin[1], value.origin[2],
132 value.direction[0], value.direction[1],
133 value.direction[2]);
134 if (!moved.ok()) {
135 recordFailure(moved.code(), moved.diagnostics());
136 if (!drag_->isDragging()) {
137 drag_.reset();
138 return ToolResponse::release();
139 }
140 return ToolResponse::consumed();
141 }
142 recordPreview(std::move(moved.value()));
143 return ToolResponse::consumed();
144}
145
146ToolResponse BuildingEdgeCurveTool::finish(EditorContext& context,
147 const EditorPointerEvent& event) {
148 if (!isDragging()) return ToolResponse::ignored();
149 (void)move(event);
150 if (!isDragging()) return ToolResponse::release();
151 auto operation = drag_->finishDrag();
152 drag_.reset();
153 if (!operation.ok()) {
154 recordFailure(operation.code(), operation.diagnostics());
155 return ToolResponse::release();
156 }
157 auto* target = dynamic_cast<BuildingPlacementTarget*>(context.target());
158 if (!target || !authority_) {
159 auto failure = toolFailure(editing::Status::Failed,
160 "editor.building.curve-tool-authority-missing",
161 "Curve commit requires its selected target and authority");
162 recordFailure(failure.code(), failure.diagnostics());
163 return ToolResponse::release();
164 }
165 editing::TransactionSpec transaction;
166 transaction.id = editing::TransactionId(
167 descriptor_.id + "." + std::to_string(++transactionSequence_));
168 transaction.label = descriptor_.label;
169 transaction.origin = editing::ActionOrigin::User;
170 transaction.target = target->targetId();
171 transaction.baseRevision = target->revision();
172 transaction.mergeKey = "building.edge-curve.handle";
173 const std::array<editing::DomainOperation, 1> operations{std::move(operation.value())};
174 auto plan = authority_->preflight(transaction, std::span<const editing::DomainOperation>(operations));
175 if (!plan.ok()) {
176 recordFailure(plan.code(), plan.diagnostics());
177 return ToolResponse::release();
178 }
179 auto receipt = authority_->commit(plan.value());
180 if (!receipt.ok()) {
181 recordFailure(receipt.code(), receipt.diagnostics());
182 return ToolResponse::release();
183 }
184 lastStatus_ = editing::Status::Applied;
185 diagnostics_ = receipt.value().diagnostics;
186 lastReceipt_ = std::move(receipt.value());
187 if (selection_ && preview_) {
188 selection_->controlPoints = preview_->curve.controlPoints;
189 if (!lastReceipt_->affectedObjects.empty()) {
190 const std::string& value = lastReceipt_->affectedObjects.front().object.value();
191 int member = 0;
192 const auto parsed = std::from_chars(value.data(), value.data() + value.size(), member);
193 if (parsed.ec == std::errc{} && parsed.ptr == value.data() + value.size() &&
194 member > 0)
195 selection_->memberInstanceId = member;
196 }
197 }
198 return ToolResponse::release();
199}
200
202 const EditorPointerEvent& event) {
204 return event.button == 0 ? begin(context, event) : ToolResponse::ignored();
205 if (event.phase == EditorPointerEvent::Phase::Move) return move(event);
206 if (event.phase == EditorPointerEvent::Phase::Up) return finish(context, event);
207 if (!isDragging()) return ToolResponse::ignored();
209 return ToolResponse::release();
210}
211
213
215 if (drag_) drag_->cancelDrag();
216 drag_.reset();
217}
218
220 if (!preview_ || !viewport_) return;
221 for (const editing::GizmoPrimitive& primitive : preview_->gizmo.primitives) {
222 const OverlayStyle style = styleFor(primitive);
223 if (primitive.kind == "sphere") {
224 auto center = viewport_->projectWorld(primitive.position);
225 if (center.ok()) overlay.circle(center.value(), 7.f, style);
226 continue;
227 }
228 if (primitive.kind != "line") continue;
229 std::array<double, 3> from = primitive.position;
230 std::array<double, 3> to = primitive.position;
231 for (size_t axis = 0; axis < 3; ++axis) {
232 const double half = primitive.direction[axis] * primitive.length * 0.5;
233 from[axis] -= half;
234 to[axis] += half;
235 }
236 auto projectedFrom = viewport_->projectWorld(from);
237 auto projectedTo = viewport_->projectWorld(to);
238 if (projectedFrom.ok() && projectedTo.ok())
239 overlay.line(projectedFrom.value(), projectedTo.value(), style);
240 }
241}
242
243} // namespace eve::editor
LogicalId target
ActionParameterOperation operation
double value
std::string from
std::string message
wgpu::PopErrorScopeStatus status
HexCoordinates to
Cell the unit walks towards on this segment.
Definition HexUnits.cpp:64
float begin
float(ui::Theme::* member)[4]
uint32_t index
const VegetationPresetContext & context
Move-only operation result carrying either a value or Status.
Definition Result.h:155
Final validation and commit boundary for editor mutations.
virtual Result< AuthorityPlan > preflight(const TransactionSpec &transaction, std::span< const DomainOperation > operations)=0
Validate a transaction without mutating its target.
virtual Result< TransactionReceipt > commit(const AuthorityPlan &plan)=0
Atomically apply an authority-approved plan when possible.
ToolResponse pointerEvent(EditorContext &context, const EditorPointerEvent &event) override
Pointer event.
void drawOverlay(EditorContext &context, IEditorOverlay &overlay) override
Draws overlay.
void setAuthority(editing::IEditAuthority *authority)
Replace the transaction authority and cancel any active gesture.
const std::vector< editing::Diagnostic > & diagnostics() const
Diagnostics from the latest preview, adapter or authority operation.
bool isDragging() const
Whether a handle currently owns pointer-drag state.
editing::Result< void > setSelection(BuildingEdgeCurveToolSelection selection)
Select one authoritative curve group member for handle editing.
void clearSelection()
Clear selection and any uncommitted gesture.
void setViewportAdapter(IBuildingViewportAdapter *viewport)
Replace the host viewport adapter and cancel any active gesture.
BuildingEdgeCurveTool(IBuildingViewportAdapter *viewport, editing::IEditAuthority *authority)
Building edge curve tool.
void cancel(EditorContext &context) override
Cancel.
const std::optional< building_editing::BuildingEdgeCurveDragPreview > & preview() const
Last valid transient preview.
void deactivate(EditorContext &context) override
Deactivate.
Context supplied by EditorSession to tools.
Definition EditorTool.h:71
Host-owned conversion boundary between pointer coordinates and a 2D/3D 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< BuildingViewportRay > pointerRay(const EditorPointerEvent &event) const =0
Build a world ray for one normalized pointer event.
Sink implemented by a 2D or 3D viewport to render tool feedback.
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
StrongId< RuleIdTag > RuleId
Definition EditingIds.h:60
eve::StatusCode Status
eve::building_editing::BuildingPlacementTarget BuildingPlacementTarget
StatusCode
Stable outcome category for an operation.
Definition Status.h:27
Immutable result of one curve-handle pointer update.
Renderer-neutral wire primitive for authoring viewport overlays.
Immutable selection/configuration consumed by BuildingEdgeCurveTool.
std::vector< building_editing::BuildingEdgeCurvePoint > controlPoints
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
glm::vec4 color