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

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

    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?

  • $\begingroup$ I left a comment showing a faster method for generating points on the surface of a cylinder in your previous question. Did you see it? $\endgroup$ – J. M. will be back soon Oct 26 '15 at 12:14
  • $\begingroup$ Notice in your code that you only ever used the z-coordinate of p1 and p2 to generate the points. $\endgroup$ – Jason B. Oct 26 '15 at 12:27
  • $\begingroup$ @J.M. Thank you for your comment. Yes, I did see it. The reason why I haven't used it is due to my inability to fully comprehend the implementation. Therefore I decided to stick to what I had implemented initially. But it has a flow. Therefore I have started a new question on the topic. $\endgroup$ – e.doroskevic Oct 26 '15 at 12:40
  • 1
    $\begingroup$ In that case, you'll want to see this related topic. $\endgroup$ – J. M. will be back soon Oct 26 '15 at 12:42
  • $\begingroup$ I also realised just now that I have the same type of an issue with my Sphere Point Picking model -.-, which I hope I can fix using similar approaches described below $\endgroup$ – e.doroskevic Oct 26 '15 at 15:33

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] :=
    (* 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}]},
    pts := Point[
     Table[transform@{radius*Cos[#1], radius*Sin[#1], #2} &[
       RandomReal[{0, 2 Pi}], RandomReal[{0, height}]], {expNo}]];
    Graphics3D[{Cylinder[{p1, p2}, radius], pts}, Boxed -> False]]];

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

enter image description here

  • $\begingroup$ 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. $\endgroup$ – Jason B. Oct 26 '15 at 12:32
  • 1
    $\begingroup$ @Jason, that would be on account of transformations not being commutative with respect to composition. $\endgroup$ – J. M. will be back soon Oct 26 '15 at 12:36
  • $\begingroup$ @J.M., yeah, I thought I had the order right, but no such luck. Fix'd. $\endgroup$ – Pillsy Oct 26 '15 at 13:24

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]],

                  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.


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.

  • $\begingroup$ 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. $\endgroup$ – Jason B. Oct 26 '15 at 13:10
  • $\begingroup$ 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. $\endgroup$ – J. M. will be back soon Oct 26 '15 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.