I have the following list of points in 3D and would like to find the points which are located on the lower edge of the object and on (or closest distance to) the X-Y or Y-Z plane of the symmetry. (Please see yellow points in fig 1). What I did was generating a point which has the X average with Y and Z min and then finding a point of the list which has the closest distance to the generated point. However, it was not correct as the generated point was not a member of the list point so the closest point was sometimes in the front and sometimes on the back of the object for different lists of points.
1 Answer
$\begingroup$
$\endgroup$
3
This is a little involved but I hope you can follow this step-by-step:
(* load the data *)
Evaluate[ToExpression@Import["data3d.txt"]];
(* get the centroid and the KLDecomposition *)
kld = KarhunenLoeveDecomposition[Transpose[Data3D]];
(* get the transformed points, new basis, and new centroid *)
transformed = Transpose[kld[[1]]];
basis = kld[[2]];
trcentr = Mean[transformed];
(* offset to {0,0,0} *)
transformed = (# - trcentr) & /@ transformed;
(* create some planes *)
xyplane = InfinitePlane[{0, 0, 0}, {{1, 0, 0}, {0, 1, 0}}];
xzplane = InfinitePlane[{0, 0, 0}, {{1, 0, 0}, {0, 0, 1}}];
yzplane = InfinitePlane[{0, 0, 0}, {{0, 1, 0}, {0, 0, 1}}];
(* find the points near to each plane, and of those points find the one lowest down *)
yzclosepoints = Select[transformed, RegionDistance[yzplane, #] < 0.5 &];
yzsmallestz = First@MinimalBy[yzclosepoints, Last];
xzclosepoints = Select[transformed, RegionDistance[xzplane, #] < 0.5 &];
xzsmallestz = First@MinimalBy[xzclosepoints, Last];
(* show the transformed points, the planes, the points near the planes *)
Show[
ListPointPlot3D[transformed, PlotRange -> Full, BoxRatios -> 1, PlotStyle -> Black],
Graphics3D[{Opacity[.1], Red, xyplane, Green, xzplane, Blue, yzplane}],
Graphics3D[{
Blue, Point[yzclosepoints],
Darker@Green, Point[xzclosepoints],
Orange, PointSize[Large],
Point[yzsmallestz], Point[xzsmallestz]}]
]
We now have the desired points but we need to transform them back into the original space:
invkld = Inverse[basis];
untransform[point_] := invkld.(point + trcentr)
(* show the two points in the original space *)
Show[ListPointPlot3D[Data3D],
Graphics3D[{Red, PointSize[Large],
Point[untransform[yzsmallestz]],
Point[untransform[xzsmallestz]]
}]]
-
$\begingroup$ Thank you very much flinty! You are awesome. $\endgroup$ Commented Jul 3, 2020 at 23:58
-
$\begingroup$ I have a question and appreciate your response! I tried it on my whole object. Actually, I could not upload whole points because of the limitation on the Pastebin capacity. To get the side point, I added the XY plane to your script. It shows the correct list of side points; However, It does not give the first point of the list. (i.e the edge point locates a bit higher than the bottom edge). It is weird as your script works well for the points on the Yz plane. (i.e. point on the bake) $\endgroup$ Commented Jul 4, 2020 at 1:11
-
1$\begingroup$ Try adjusting the
< 0.5
where I do theSelect
to something a bit larger. It's possible it's ignoring too many points. $\endgroup$– flintyCommented Jul 4, 2020 at 1:28
xyplane = HalfPlane[......]
), you could get some of the closest points to that plane within some acceptable distance e.gxyclosepoints = Select[Data3D, RegionDistance[#,xyplane] < 5 &]
Then of these points find the one with the smallest coordinateMinimalBy[xyclosepoints, #[[3]]&]
$\endgroup$KarhunenLoeveDecomposition
you can get the data in a nicer frame and then transform back later. Also I meant InfinitePlane not HalfPlane, and my RegionDistance arguments were the wrong way around. $\endgroup$