(*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$ Oct 26, 2015 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, 2015 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$ Oct 26, 2015 at 12:40
  • 1
    $\begingroup$ In that case, you'll want to see this related topic. $\endgroup$ Oct 26, 2015 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$ Oct 26, 2015 at 15:33

2 Answers 2


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