1

Code:

    (*Cylinder Point Picking*)
    ppCylinder[p1_List, p2_List, radius_Integer, expNo_Integer] :=
              Module[
                    {pts},
                    (*Set point coordinates *)
                    pts := Point[Table[{radius*Cos[#1], radius*Sin[#1], #2} &[RandomReal[{0, 2 Pi}], RandomReal[{p1[[3]], p2[[3]]}]], {expNo}]];

                    (*Visualize*)
                    Graphics3D[{Cylinder[{p1, p2}, radius], pts}, Boxed -> False]
    ];

    (*Test*)
    ppCylinder[{0, 0, 0}, {0, 0, 1}, 2, 5000] (*Output #1*)
    ppCylinder[{0, 0, 0}, {0, 1, 2}, 2, 5000] (*Output #2*)

Output 1:
Output 1

Output 2:
Output 2

Based on the code I wrote, I am satisfied with the output 1# but not #2. How could I modify the existing code to account for {X && Y} variation when generating a set of random points on a cylinder surface?

e.doroskevic
  • 5,959
  • 1
  • 13
  • 32

2 Answers2

6

You need to transform the generated points (which currently lie on a cylinder that's aligned with the $z$-axis) so that they lie on the new cylinder. You can accomplish this with a TranslationTransform followed by a RotationTransform:

ppCylinder[p1_List, p2_List, radius_Integer, expNo_Integer] :=
  With[{
    (* the height of the cylinder isn't just the difference of the
       z coordinates! *)
    height = Norm[p2 - p1], 
    transform = 
      TranslationTransform[p1] @* RotationTransform[{{0, 0, 1}, p2 - p1}]},
   Module[{pts},
    pts := Point[
     Table[transform@{radius*Cos[#1], radius*Sin[#1], #2} &[
       RandomReal[{0, 2 Pi}], RandomReal[{0, height}]], {expNo}]];
     (*Visualize*)
    Graphics3D[{Cylinder[{p1, p2}, radius], pts}, Boxed -> False]]];

Now ppCylinder[{0, 0, 0}, {0, 0, 1}, 2, 5000] returns:

enter image description here

Pillsy
  • 18,498
  • 2
  • 46
  • 92
  • OP's examples all use the origin as the first point, but if the first point is an argument to the function, you should be able to make it anything. Try ppCylinder[{6, 0, 4}, {0, 1, 2}, 2, 5000] to see an unexpected result. – Jason B. Oct 26 '15 at 12:32
  • 1
    @Jason, that would be on account of transformations not being commutative with respect to composition. – J. M.'s missing motivation Oct 26 '15 at 12:36
  • @J.M., yeah, I thought I had the order right, but no such luck. Fix'd. – Pillsy Oct 26 '15 at 13:24
4

Since Pillsy's already hit on the important issue of doing the proper rotation + translation, I'll just leave this normal distribution-based method for generating random points on a cylinder:

ppCylinder[p1_?VectorQ, p2_?VectorQ, r_?Positive, n_Integer?Positive, opts___] :=
           Module[{h = EuclideanDistance[p1, p2], rt, pts},
                  rt = RotationTransform[{{0, 0, 1}, p2 - p1}]; 
                  pts = Point[Table[Composition[TranslationTransform[p1], rt] @
                  Append[r Normalize[RandomVariate[NormalDistribution[], 2]],
                         RandomReal[h]],
                  {n}]];

                  Graphics3D[{Cylinder[{p1, p2}, r], pts}, opts, Boxed -> False]]

Generating normal variates can be more efficient than generating uniform variates and then applying trigonometric functions on them.

Test:

ppCylinder[{0, 0, 0}, {0, 1, 2}, 2, 1*^4]

points on a cylinder


Here is a faster implementation that eschews the use of Table[] and uses the second argument of RandomReal[]/RandomVariate[] to generate many points at once:

ppCylinder[p1_?VectorQ, p2_?VectorQ, r_?Positive, n_Integer?Positive, opts___] :=
           Module[{h = EuclideanDistance[p1, p2], pts, tr},
                  tr = AffineTransform[{RotationMatrix[{{0, 0, 1}, p2 - p1}], p1}];
                  pts = MapThread[Composition[tr, Append],
                                  {r (Normalize /@
                                      RandomVariate[NormalDistribution[], {n, 2}]),
                                   RandomReal[h, n]}];
                  Graphics3D[{Cylinder[{p1, p2}, r], Point[pts]},
                             opts, Boxed -> False]]

This is ~ 10 times faster than the previous implementation in my tests.

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
  • If the speed of execution matters, this method is much slower than Pillsy's method. If I modify his function as I mention in the comment above, and then execute the resulting function on ppCylinder[{5, 4, 2}, {-7, 1, 2}, 2, 5000] it takes about 0.15 seconds. But the same test on your function takes almost 6 seconds. I don't know what the slowdown step is though. – Jason B. Oct 26 '15 at 13:10
  • That would be because I used a Table[] as opposed to generating the random points all at once. (I wanted the routine to have some resemblance to the OP's code, since he said he was not familiar with the normal distribution approach.) I'll include the faster method in a little while. – J. M.'s missing motivation Oct 26 '15 at 13:14