LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
symbolic_geometry_line_plane.cpp
浏览该文件的文档.
6
7#include <algorithm>
8#include <cmath>
9#include <limits>
10#include <stdexcept>
11#include <utility>
12
13namespace LMCAS {
14using namespace geometry_detail;
15
16namespace {
17
19 const LineSymbolic& line, const PlaneSymbolic& plane,
20 ComputationContext& context, const std::string& operation
21) {
22 auto line_valid = validate_line_checked(line, context, operation);
23 if (!line_valid) return line_valid;
24 auto plane_valid = validate_plane_checked(plane, context, operation);
25 if (!plane_valid) return plane_valid;
26 return context.consume_steps(6, operation);
27}
28
30 return error.code == CasErrc::Cancelled ||
32}
33
35 return offset.has_value() && offset.value().is_finite() &&
36 std::isfinite(offset.value().value);
37}
38
39bool direction_products_need_scaling(double normal_scale, double direction_scale) {
40 const double safe_high = std::numeric_limits<double>::max() / 3.0;
41 const double safe_low = std::numeric_limits<double>::min();
42 return normal_scale > safe_high / direction_scale ||
43 normal_scale < safe_low / direction_scale;
44}
45
47 double normal_scale, const Result<double>& point_scale
48) {
49 return point_scale.has_value() && point_scale.value() > 0.0 &&
50 normal_scale >
51 std::numeric_limits<double>::max() / point_scale.value() / 3.0;
52}
53
55 const Result<double>& point_scale, bool numeric_plane_offset
56) {
57 return point_scale.has_value() && numeric_plane_offset &&
58 point_scale.value() > std::numeric_limits<double>::max() / 3.0;
59}
60
62 const std::vector<double>& values, double scale, bool protect_point_dot,
63 std::vector<std::shared_ptr<SymbolicExpr>>& scaled
64) {
65 scaled.reserve(values.size());
66 for (double component : values) {
67 double scaled_component = component / scale;
68 if (protect_point_dot) scaled_component /= 3.0;
69 scaled.push_back(SymbolicExpr::number(scaled_component));
70 }
71}
72
73std::shared_ptr<SymbolicExpr> scale_plane_offset(
74 const std::shared_ptr<SymbolicExpr>& offset,
75 double normal_scale, bool protect_point_dot
76) {
77 auto scaled = SymbolicExpr::divide(
78 offset, SymbolicExpr::number(normal_scale));
79 if (protect_point_dot) {
80 scaled = SymbolicExpr::divide(scaled, SymbolicExpr::number(3.0));
81 }
82 return scaled;
83}
84
85double fused_dot_three(const double (&a)[3], const double (&b)[3]) {
86 return std::fma(a[0], b[0], std::fma(a[1], b[1], a[2] * b[2]));
87}
88
90 const std::vector<double>& normal_values, double normal_scale,
91 const std::vector<double>& direction_values, double direction_scale,
92 const std::vector<double>& point_values, double point_scale,
93 double plane_offset, const std::string& operation
94) {
95 const double plane_location = plane_offset / normal_scale;
96 if (!std::isfinite(plane_location)) {
99 "line-plane intersection coordinate is outside the supported domain",
100 operation);
101 }
102 double spatial_scale = std::max(point_scale, std::abs(plane_location));
103 if (spatial_scale == 0.0) spatial_scale = 1.0;
104
105 double unit_normal[3];
106 double unit_direction[3];
107 double scaled_point[3];
108 for (size_t i = 0; i < 3; ++i) {
109 unit_normal[i] = normal_values[i] / normal_scale;
110 unit_direction[i] = direction_values[i] / direction_scale;
111 scaled_point[i] = point_values[i] / spatial_scale;
112 }
113 const double denominator = fused_dot_three(unit_normal, unit_direction);
114 if (denominator == 0.0) {
117 "line is parallel to plane, so no unique intersection exists",
118 operation);
119 }
120 const double point_dot = fused_dot_three(unit_normal, scaled_point);
121 const double scaled_offset = plane_location / spatial_scale;
122 const double parameter = (scaled_offset - point_dot) / denominator;
123
124 std::vector<std::shared_ptr<SymbolicExpr>> intersection;
125 intersection.reserve(3);
126 for (size_t i = 0; i < 3; ++i) {
127 const double scaled_coordinate = std::fma(
128 parameter, unit_direction[i], scaled_point[i]);
129 const double coordinate = scaled_coordinate * spatial_scale;
130 if (!std::isfinite(coordinate)) {
133 "line-plane intersection coordinate is outside the supported domain",
134 operation);
135 }
136 intersection.push_back(SymbolicExpr::number(coordinate));
137 }
138 return VectorExprListResult::success(std::move(intersection));
139}
140
142 const std::vector<std::shared_ptr<SymbolicExpr>>& normal,
143 const std::vector<std::shared_ptr<SymbolicExpr>>& direction,
144 const std::vector<std::shared_ptr<SymbolicExpr>>& point,
145 const std::shared_ptr<SymbolicExpr>& offset,
146 ComputationContext& context, const std::string& operation
147) try {
148 detail::RewriteBudget transform_budget(context, (context).limits().max_recursion_depth,
149 (context).limits().max_ast_nodes, "transform");
150 auto n_dot_a = vector_dot_checked(normal, point, context);
151 if (!n_dot_a) return VectorExprListResult::failure(n_dot_a.error());
152 auto n_dot_b = vector_dot_checked(normal, direction, context);
153 if (!n_dot_b) return VectorExprListResult::failure(n_dot_b.error());
154 auto denom = detail::make_expression_ptr(detail::normalize_node(detail::node(n_dot_b.value()), transform_budget));
156 denom, context, operation,
157 "line is parallel to plane, so no unique intersection exists",
158 "line-plane denominator nonzero condition cannot be verified");
159 if (!nonzero) return VectorExprListResult::failure(nonzero.error());
160
161 auto t = SymbolicExpr::divide(
162 SymbolicExpr::add(
163 offset,
164 SymbolicExpr::multiply(SymbolicExpr::number(-1), n_dot_a.value())),
165 denom);
166 std::vector<std::shared_ptr<SymbolicExpr>> intersection;
167 intersection.reserve(point.size());
168 for (size_t i = 0; i < point.size(); ++i) {
169 auto coord = SymbolicExpr::add(
170 point[i], SymbolicExpr::multiply(t, direction[i]));
171 auto simplified = simplify_checked(
172 coord, operation,
173 "line-plane intersection coordinate is outside the supported domain", context);
174 if (!simplified) {
175 return VectorExprListResult::failure(simplified.error());
176 }
177 intersection.push_back(std::move(simplified.value()));
178 }
179 return VectorExprListResult::success(std::move(intersection));
180} catch (const CasError& error) {
181 return VectorExprListResult::failure(error);
182} catch (const std::bad_alloc&) {
183 return VectorExprListResult::failure(CasErrc::ResourceLimit, "allocation failed", operation);
184} catch (const std::length_error& error) {
185 return VectorExprListResult::failure(CasErrc::ResourceLimit, error.what(), operation);
186} catch (const std::exception& error) {
187 return VectorExprListResult::failure(CasErrc::InternalInvariant, error.what(), operation);
188}
189
191 const std::vector<std::shared_ptr<SymbolicExpr>>& point,
192 const std::vector<std::shared_ptr<SymbolicExpr>>& normal,
193 const std::shared_ptr<SymbolicExpr>& offset,
194 ComputationContext& context, const std::string& operation
195) {
196 auto n_dot_r = vector_dot_checked(normal, point, context);
197 if (!n_dot_r) return ExpressionResult::failure(n_dot_r.error());
198 auto norm_sq = vector_dot_checked(normal, normal, context);
199 if (!norm_sq) return ExpressionResult::failure(norm_sq.error());
200
201 auto diff = SymbolicExpr::add(
202 n_dot_r.value(),
203 SymbolicExpr::multiply(SymbolicExpr::number(-1), offset));
204 auto norm_n = SymbolicExpr::sqrt(norm_sq.value());
208 std::vector<std::shared_ptr<const SymbolicNode>>{
209 LMCAS::detail::node(diff)}));
210 return simplify_checked(
211 SymbolicExpr::divide(abs_diff, norm_n),
212 operation,
213 "point-plane distance is outside the supported domain", context);
214}
215
216}
217
219 const LineSymbolic& line,
220 const PlaneSymbolic& plane,
222) {
223 const std::string operation = "line_plane_intersection";
224 try {
225 auto valid = validate_intersection_inputs(line, plane, context, operation);
226 if (!valid) {
227 return VectorExprListResult::failure(valid.error());
228 }
229
230 std::vector<double> normal_values;
231 std::vector<double> direction_values;
232 auto normal_scale = numeric_vector_scale_checked(
233 plane.normal, context, operation, &normal_values);
234 if (!normal_scale) {
235 return VectorExprListResult::failure(normal_scale.error());
236 }
237 auto direction_scale = numeric_vector_scale_checked(
238 line.direction, context, operation, &direction_values);
239 if (!direction_scale) {
240 return VectorExprListResult::failure(direction_scale.error());
241 }
242 std::vector<double> point_values;
243 auto point_scale = numeric_vector_scale_checked(
244 line.point, context, operation, &point_values);
245 if (!point_scale && interrupts_optional_numeric_evaluation(point_scale.error())) {
246 return VectorExprListResult::failure(point_scale.error());
247 }
248 auto plane_offset = evaluate_numeric(*plane.d, NumericBindings{}, context);
249 const bool numeric_plane_offset = finite_plane_offset(plane_offset);
250 if (!plane_offset && interrupts_optional_numeric_evaluation(plane_offset.error())) {
251 return VectorExprListResult::failure(plane_offset.error());
252 }
253
254 const bool scale_direction_products = direction_products_need_scaling(
255 normal_scale.value(), direction_scale.value());
256 const bool protect_point_dot =
257 point_dot_needs_protection(normal_scale.value(), point_scale);
258 const std::vector<std::shared_ptr<SymbolicExpr>>* effective_normal = &plane.normal;
259 const std::vector<std::shared_ptr<SymbolicExpr>>* effective_direction = &line.direction;
260 std::vector<std::shared_ptr<SymbolicExpr>> scaled_normal;
261 std::vector<std::shared_ptr<SymbolicExpr>> scaled_direction;
262 std::shared_ptr<SymbolicExpr> effective_d = plane.d;
263 if (scale_direction_products || protect_point_dot) {
264 scale_symbolic_components(
265 normal_values, normal_scale.value(), protect_point_dot, scaled_normal);
266 effective_normal = &scaled_normal;
267 effective_d = scale_plane_offset(plane.d, normal_scale.value(), protect_point_dot);
268 }
269 if (scale_direction_products) {
270 scale_symbolic_components(
271 direction_values, direction_scale.value(), false, scaled_direction);
272 effective_direction = &scaled_direction;
273 }
274
275 if (coordinate_update_needs_protection(point_scale, numeric_plane_offset)) {
276 return protected_coordinate_intersection(
277 normal_values, normal_scale.value(),
278 direction_values, direction_scale.value(),
279 point_values, point_scale.value(), plane_offset.value().value, operation);
280 }
281 return symbolic_line_plane_intersection(
282 *effective_normal, *effective_direction, line.point, effective_d,
283 context, operation);
284 } catch (const std::bad_alloc&) {
286 "vector geometry allocation failed",
287 operation);
288 } catch (const std::exception& e) {
290 e.what(), operation);
291 }
292}
293
301
302
304 const std::vector<std::shared_ptr<SymbolicExpr>>& point,
305 const PlaneSymbolic& plane,
307) {
308 const std::string operation = "point_plane_distance";
309 try {
310 auto plane_valid = validate_plane_checked(plane, context, operation);
311 if (!plane_valid) return ExpressionResult::failure(plane_valid.error());
312 if (point.size() != 3) {
315 "point-plane distance requires a 3-dimensional point",
316 operation);
317 }
318 auto point_valid = validate_symbolic_vector(point, "point", operation);
319 if (!point_valid) return ExpressionResult::failure(point_valid.error());
320 auto step = context.consume_steps(4, operation);
321 if (!step) return ExpressionResult::failure(step.error());
322
323 std::vector<double> normal_values;
324 auto normal_scale = numeric_vector_scale_checked(
325 plane.normal, context, operation, &normal_values);
326 if (!normal_scale) {
327 return ExpressionResult::failure(normal_scale.error());
328 }
329 auto point_scale = numeric_vector_scale_checked(
330 point, context, operation);
331 if (!point_scale && interrupts_optional_numeric_evaluation(point_scale.error())) {
332 return ExpressionResult::failure(point_scale.error());
333 }
334
335 const double safe_low =
336 std::sqrt(std::numeric_limits<double>::min());
337 const double safe_high =
338 std::sqrt(std::numeric_limits<double>::max() / 3.0);
339 const bool protect_point_dot =
340 point_dot_needs_protection(normal_scale.value(), point_scale);
341 const std::vector<std::shared_ptr<SymbolicExpr>>* effective_normal =
342 &plane.normal;
343 std::vector<std::shared_ptr<SymbolicExpr>> scaled_normal;
344 std::shared_ptr<SymbolicExpr> effective_d = plane.d;
345 if (normal_scale.value() < safe_low ||
346 normal_scale.value() > safe_high ||
347 protect_point_dot) {
348 scale_symbolic_components(
349 normal_values, normal_scale.value(), protect_point_dot, scaled_normal);
350 effective_normal = &scaled_normal;
351 effective_d = scale_plane_offset(plane.d, normal_scale.value(), protect_point_dot);
352 }
353
354 return symbolic_point_plane_distance(
355 point, *effective_normal, effective_d, context, operation);
356 } catch (const std::bad_alloc&) {
358 "vector geometry allocation failed",
359 operation);
360 } catch (const std::exception& e) {
362 e.what(), operation);
363 }
364}
365
367 const std::vector<std::shared_ptr<SymbolicExpr>>& point,
368 const PlaneSymbolic& plane
369) {
372}
373
374}
bool has_value() const noexcept
static Result failure(CasErrc code, std::string message, std::string operation={})
static Result success(std::vector< std::shared_ptr< SymbolicExpr > > value)
std::shared_ptr< SymbolicExpr > scale_plane_offset(const std::shared_ptr< SymbolicExpr > &offset, double normal_scale, bool protect_point_dot)
bool point_dot_needs_protection(double normal_scale, const Result< double > &point_scale)
ExpressionResult symbolic_point_plane_distance(const std::vector< std::shared_ptr< SymbolicExpr > > &point, const std::vector< std::shared_ptr< SymbolicExpr > > &normal, const std::shared_ptr< SymbolicExpr > &offset, ComputationContext &context, const std::string &operation)
void scale_symbolic_components(const std::vector< double > &values, double scale, bool protect_point_dot, std::vector< std::shared_ptr< SymbolicExpr > > &scaled)
Result< void > validate_intersection_inputs(const LineSymbolic &line, const PlaneSymbolic &plane, ComputationContext &context, const std::string &operation)
VectorExprListResult protected_coordinate_intersection(const std::vector< double > &normal_values, double normal_scale, const std::vector< double > &direction_values, double direction_scale, const std::vector< double > &point_values, double point_scale, double plane_offset, const std::string &operation)
VectorExprListResult symbolic_line_plane_intersection(const std::vector< std::shared_ptr< SymbolicExpr > > &normal, const std::vector< std::shared_ptr< SymbolicExpr > > &direction, const std::vector< std::shared_ptr< SymbolicExpr > > &point, const std::shared_ptr< SymbolicExpr > &offset, ComputationContext &context, const std::string &operation)
bool coordinate_update_needs_protection(const Result< double > &point_scale, bool numeric_plane_offset)
bool direction_products_need_scaling(double normal_scale, double direction_scale)
std::shared_ptr<::LMCAS::SymbolicExpr > make_expression_ptr(SymbolicNodePtr root)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
std::shared_ptr< const Node > make_node(Args &&... args)
SymbolicNodePtr normalize_node(const SymbolicNodePtr &node, RewriteBudget &budget, const FactsQuery &facts, Domain domain)
Result< double > numeric_vector_scale_checked(const std::vector< std::shared_ptr< SymbolicExpr > > &expressions, ComputationContext &context, const std::string &operation, std::vector< double > *values=nullptr)
Result< void > validate_plane_checked(const PlaneSymbolic &plane, ComputationContext &context, const std::string &operation)
ExpressionResult simplify_checked(const std::shared_ptr< SymbolicExpr > &expr, const std::string &operation, const std::string &message, ComputationContext &context)
Result< void > validate_symbolic_vector(const std::vector< std::shared_ptr< SymbolicExpr > > &vector, const std::string &name, const std::string &operation)
Result< void > validate_line_checked(const LineSymbolic &line, ComputationContext &context, const std::string &operation)
Result< void > checked_nonzero_numeric_or_exact(const std::shared_ptr< SymbolicExpr > &expr, ComputationContext &context, const std::string &operation, const std::string &domain_message, const std::string &inconclusive_message)
const std::string const LimitExprPtr & point
const PlaneSymbolic & plane
ExprResult e()
const std::vector< Polynomial< BigInt > > const BigInt ComputationContext & context
const std::string const LimitExprPtr LimitDirection direction
const MultiPoly & b
Result< std::shared_ptr< SymbolicExpr > > ExpressionResult
Result of an operation that produces a symbolic expression.
std::vector< std::shared_ptr< SymbolicExpr > > point
直线上一点。
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string const std::shared_ptr< SymbolicExpr > & a
VectorExprListResult line_plane_intersection_checked(const LineSymbolic &line, const PlaneSymbolic &plane, ComputationContext &context)
ExpressionResult point_plane_distance_checked(const std::vector< std::shared_ptr< SymbolicExpr > > &point, const PlaneSymbolic &plane, ComputationContext &context)
std::vector< std::shared_ptr< SymbolicExpr > > normal
法向量。
Result< std::vector< std::shared_ptr< SymbolicExpr > > > VectorExprListResult
std::vector< std::shared_ptr< SymbolicExpr > > direction
方向向量。
const ExprPtr & denominator
Result< ApproxReal > evaluate_numeric(const SymbolicExpr &expression, const NumericBindings &bindings={})
__attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) VectorCalculusExprResult curve_integral_scalar_checked(const std __attribute__((visibility("default"))) std const VectorField const std::string & t
std::unordered_map< std::string, double > NumericBindings
ExpressionResult vector_dot_checked(const std::vector< std::shared_ptr< SymbolicExpr > > &a, const std::vector< std::shared_ptr< SymbolicExpr > > &b, ComputationContext &context)
std::shared_ptr< SymbolicExpr > d
常数项。
由一点和方向向量定义的符号直线。
由法向量和常数 d 定义的符号平面 ax + by + cz = d。
内部 AST 定义。