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
RegionNormaloption? There is an option to findVertexNormalshere, 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