I would like to find the surface normal for a point on a 3D filled shape in Mathematica.
I know how to calculate the normal of a parametric surface using the cross product but this method will not work for a shape like Cone[]
or Ball[]
.
- Is there some sort of
RegionNormal
option? There is an option to findVertexNormals
here, but this is something to with shading and seems unhelpful. - Is there a method I can use to convert the region into a parametric expression and use the normal cross product method?
The plan is to take an arbitrary line and find the angle of intersection between the line and the surface of the shape.
Answer
(* put inequality into u ≤ 0 form, return just u *)
standardize[a_ <= b_] := a - b;
standardize[a_ >= b_] := b - a;
regnormal[reg_, {x_, y_, z_}] := Module[{impl},
impl = LogicalExpand@ Simplify[RegionMember[reg, {x, y, z}], {x, y, z} ∈ Reals];
If[Head@impl === Or,
impl = List @@ impl,
impl = List@impl];
impl = Replace[impl, {Verbatim[And][a___] :> {a}, e_ :> {e}}, 1];
Piecewise[
Flatten[
Function[{component},
Table[{
D[standardize[component[[i]]], {{x, y, z}}],
Simplify[
(And @@ Drop[component, {i}] /. {LessEqual -> Less, GreaterEqual -> Greater}) &&
(component[[i]] /. {LessEqual -> Equal, GreaterEqual -> Equal}),
TransformationFunctions -> {Automatic,
Reduce[#, {}, Reals] &}]
}, {i, Length@component}]
] /@ impl,
1],
Indeterminate]
];
Examples:
regnormal[Cone[{{0, 0, 0}, {1, 1, 1}}, 1/2], {x, y, z}]
regnormal[Ball[{1, 2, 3}, 4], {x, y, z}]
regnormal[RegionUnion[Ball[], Cone[{{0, 0, 0}, {1, 1, 1}}, 1/2]], {x, y, z}]
regnormal[Cylinder[{{1, 1, 1}, {2, 3, 1}}], {x, y, z}]
It assumes that the RegionMember
expression can be computed (which is not always the case) and that it will be a union (via Or
) of intersections (via And
). It also assumes that the RegionMember
expression includes the boundary. Thus, it is probably not very robust, but it handles the OP's examples.
Also, if this is used in numerical applications, which seems to be the case for the OP, one should worry about the exact conditions in the Piecewise
expressions returned. It's unlikely the numerical calculations will be accurate enough to satisfy Equal
. So either change the conditions or possible change Internal`$EqualTolerance
:
Block[{Internal`$EqualTolerance = Log10[2.^28]}, (* ~single-precision FP equality *)
]
Comments
Post a Comment