algorithmmathpascal

Plotting a polar rose — how to use "n" and "d" to achieve different shapes?


In code form, the calculation of the coordinates of the polar rose point in the Cartesian system can be represented as follows:

X := OriginX + A * Cos(K * Theta) * Cos(Theta);
Y := OriginY + A * Cos(K * Theta) * Sin(Theta);
Parameter Meaning
OriginX the X coordinate of the rose's center point
OriginY the Y coordinate of the rose's center point
A target rose radius
K angular frequency

The above code works prefectly. For example, setting K as 5, I get a flower with five petals. For other positive integer values of K I also get correct flowers, consistent with the first row of those given on Wikipedia:

enter image description here
Image credit: Jason Davies via Wikimedia Commons.

Now I would like my code to be able to generate roses from other rows. I found the article Polar rose garden and there is a image with various roses, where k is integer or real:

https://blog.k2h.se/post/2020-01-03-polar-rose-garden_files/figure-html/unnamed-chunk-2-1.png

For example, for k = 1 / 3, the rose should look like this:

Also https://blog.k2h.se/post/2020-01-03-polar-rose-garden_files/figure-html/unnamed-chunk-2-1.png look for caption 1/3.

but if I use the following code:

var
  Theta:   Single;
  OriginX: Integer;
  OriginY: Integer;
  Size:    Integer;
  X:       Integer;
  Y:       Integer;
  K:       Single;
begin
  OriginX := 120;
  OriginY := 120;
  Size    := 120;
  K       := 1 / 3;
  Theta   := 0.0;

  while Theta < Pi * 2 do
  begin
    X := Round(OriginX + Size * Cos(K * Theta) * Cos(Theta));
    Y := Round(OriginY + Size * Cos(K * Theta) * Sin(Theta));

    Canvas.Pixels[X, Y] := clGray;

    Theta += 0.001;
  end;
end;

the rose looks like this:

enter image description here

Could anyone advise why my graph is broken?

PS: I'm not a mathematician, so C/Pascal code would be perfect.


Solution

  • We can't just blindly run from 0 to 2PI, because that doesn't guarantee a full period of the graph. After all, we're not working with cos(theta) and sin(theta), we're working with an additional factor cos(n/d * theta), so we'll need to at the very least multiply by d in order to get back to full periods, but n is a bit sneaky here.

    If we turn to the Wikipedia page for rose curves, specifically the section for when k is rational, we learn that:

    In the case when both n and d are odd, the positive and negative half-cycles of the sinusoid are coincident. The graph of these roses are completed in any continuous interval of polar angles that is long.

    When n is even and d is odd, or visa versa, the rose will be completely graphed in a continuous polar angle interval 2dπ long

    So a modulo-based check should do:

    <script type="module" src="https://cdnjs.cloudflare.com/ajax/libs/graphics-element/1.11.1/graphics-element.js" integrity="sha512-fz6xdkeMImCEkyHV+HPpg7C9cxoqc0w9RdSzm9ufj2wjFQFz71iJvyazoM9tXRGBzN40GuNY2e5fmimRiBFoJw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/graphics-element/1.11.1/graphics-element.css" integrity="sha512-sD4sJLVEBRXXbNNaSs/vwyt6vgq+jIiAhpg1MtqNdmhOt3k6K7V4wH5p5IxNvRXAoP51rAHyig0f4egsM+WdCQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    <graphics-element id="graphics" width="200px" height="160px" title="let's draw some roses">
      <graphics-source>
        const r = 70;
        let k = 1;
    
        function setup() {
          addSlider(`n`, {min:1, max:7, step:1, value:1 });
          addSlider(`d`, {min:1, max:9, step:1, value:3 });
        }
        
        function draw() {
          k = n / d;
          clear();
          noFill();
          translate(width/2, height/2);
    
          // what's our  "loop limit"?
          const oddN = (n % 2) === 1;
          const oddD = (d % 2) === 1;
          const limit = d * ((oddN && oddD) ? PI : TAU);
    
          // let's draw!
          start();
          for (let theta = 0, f, x, y; theta <= limit; theta += 0.001) {
            f = r * cos(k * theta);
            x = f * cos(theta);
            y = f * sin(theta);
            vertex(x, y);
         }
         end();
        }
      </graphics-source>
    </graphics-element>

    The last time I've used Pascal was probably 40 years ago, so you'll have to "port" that solution yourself, but that should be pretty easy =)