Couple nights ago I woke up in the middle of the night and somehow got thinking about plotting spirographs with cycling line widths. Or at least a simulation of a cycling line width. Scatter plots came to mind. These plots often use circles to represent data points. What if I used a scatter plot for a spirograph curve. Cycling the size of the circle used for each point in the plot. Might be a bit more time consuming to generate the plot, but it might better look like what I had been aiming for many posts ago.

See: Spirograph VIII: Moving Line Thickness. (My choice of wording didn’t improve until a while after I wrote that title.)

I didn’t want to mess with generating an array of marker sizes to pass to the scatter() function. (Though I may return to that idea, as it would likely be faster than what I am doing.) So, I decided to take slices of the plot data and plot each slice with a given size of marker. I am using circles for the markers. (Another thing I might play with down the road.)

The number of slices had to match the number points in the cycle size. And, I would have to increase the marker size for the first half of the cycle and reduce it for the second. There should likely be a maximum size for the largest marker. Though at first glance there seems to be no easy way to sort that out.

I started by looking at what a few different marker sizes would look like at the scale of the current spirograph plots. I picked a couple of numbers and used them to get things going. I started coding for the basic curve. That is, one random shape for all wheels. I also decided to allow for a larger number of wheels, up to 12 from the previous 8.

Random Shape for All Wheels

There were a variety of adjustments in various parts of my mega-plotting module, but I am only going to show you the essential bits of code. Let’s start with plots using a single wheel shape. In this case, the data I use (t_xs, t_ys, lc_frq, hf_frq) has been generated earlier in the module. Lot’s of previous posts with that code or the information to allow you to code it yourself.

    elif do_plt == 15:
      # cycling line width curve simulation using scatter plots

      if t_tl:
        m_ttl = get_plt_ttl(shp=splt.shp_nm[su], ld=r_skp, df=drp_f, lw=ln_w)
        fig.suptitle(m_ttl)

      sz_mult = 80  # incremental marker size
      print(f"plot 15: sz_mult: {sz_mult}, max size: {sz_mult * hf_frq}")
      # first half of cycles, marker size increases
      for i in range (hf_frq):
        if i == 0:
          m_sz = sz_mult
        else:
          m_sz = i * sz_mult
        ax.scatter(t_xs[-1][i::lc_frq], t_ys[-1][i::lc_frq], s=m_sz)
      # second half of cycles, marker size decreases
      m_sz = hf_frq * sz_mult
      for i in range(hf_frq, lc_frq):
        ax.scatter(t_xs[-1][i::lc_frq], t_ys[-1][i::lc_frq], s=m_sz)
        m_sz -= sz_mult

And, here’s a look at an example generated by the code.

image using scatter plot to simulate cycling line width for curve

Personally I think the maximum simulated line width is a bit too large. I generated a number of images and, in general, for large numbers of plot points though the images were somewhat interesting, I still felt the simulated line width is too large. The more plotting points, the larger the cycle. The larger the cycle, the greater the maximum marker size. So I am going to reduce the multiplier vaLue and try again.

I set it at 60, but for larger numbers of plotting points (i.e. large line width cycles), I think it still got too big. So, decided to set the value based on the cycle size.

      sz_mult = 3000 / lc_frq

And an example of the change at 2000 plot points (takes a bit of time to generate and save). I think that makes for a decent maximum line thickness. Though perhaps artistically there might be something to be said for the thicker lines in the image above.

image using scatter plot to simulate cycling line width for curve with limit on maximum line thickness

Also, kind of like the fact that at the really small marker sizes the marker occaisionally shows as a series of almost or completely separated circles. Gives the image a different feel.

Random Shape for Each Wheel

I have recently found myself liking the gnarly images with a random shape for each wheel more than those with the same shape for each wheel. So, let’s give that a try.

Pretty much a duplicate of the code above, but I need to make sure I generate the necessary curve data. Not generally done earlier in the module for curves with a random shape for each wheel. Should probably fix that.

    elif do_plt == 16:
      # cycling line width curve simulation using scatter plots, random shape aach wheel

      if not (t_ua or t_hc or t_sv):
        t_xs, t_ys, t_su = splt.mk_rnd_curve(rds)
      else:
        t_xs, t_ys, t_su = splt.mk_rnd_curve(rds, su=t_su)
      print(f"{do_plt}: {t_su}")

      # make plot fits without going over the title
      ax = set_plot_bnds(ax, t_xs, t_ys, x_adj=b_adj)

      if t_tl:
        m_ttl = get_plt_ttl(shp=t_su, lw=ln_w)
        fig.suptitle(m_ttl)

      # set up data for plot
      sz_mult = 3000 / lc_frq
      print(f"plot {do_plt}: sz_mult: {sz_mult}, max size: {sz_mult * hf_frq}")
      # first half of cycles, marker size increases
      for i in range (0, hf_frq):
        if i == 0:
          m_sz = sz_mult
        else:
          m_sz = i * sz_mult
        ax.scatter(t_xs[-1][i::lc_frq], t_ys[-1][i::lc_frq], s=m_sz, alpha=alph)
      # second half of cycles, marker size decreases
      m_sz = hf_frq * sz_mult
      for i in range(hf_frq, lc_frq):
        ax.scatter(t_xs[-1][i::lc_frq], t_ys[-1][i::lc_frq], s=m_sz, alpha=alph)
        m_sz -= sz_mult

An, an example. This one had 3 wheels: circle, circle, equilateral triangle.

image using scatter plot to simulate cycling line width for curve with limit on maximum line thickness and random shape for each wheel

And, another. This one had 7 wheels: square, square, tetracuspid, tetracuspid, tetracuspid, tetracuspid, square. I would say the image reflects that selection of shapes.

image using scatter plot to simulate cycling line width for curve with limit on maximum line thickness and random shape for each wheel

Tough to say which of the above I like best. Not to everyone’s taste I’m sure. But certainly interesting. And, I do prefer the ones with a “coloured” background rather than a white or grey background. Also sorry about the copyright at the bottom. Was setting things up for getting some of these plots printed and framed. We think a set of three small framed images would look great on the bathroom wall.

Cycle Size

While working on the code for this post, I got wondering how cycle size would affect the final image. So as a test I decided to go with a large cycle size and have a look at a few curves. I decided to set the cycle size to the number of plotting points divided by a number (randomly selected) between 4 and 8 inclusive.

    if not (sv_plot):
      if do_plt in [15, 16]:
        c_div = np.random.randint(4,9)
        lc_frq = max(20, t_pts // c_div)
      else:
        lc_frq = max(20, t_pts // 20)

And some examples. The first one uses 8 width cycles for the plot. It used 3 circles to generate the curve. I am including it, because it is quite easy to see the 8 cycles. Not the case for most of the images generated.

image using scatter plot to simulate cycling line width with fewer, longer cycles

The next one used 5 cycles and 11 wheels with the following shapes: [’t’, ‘c’, ’e’, ’s’, ‘q’, ’t’, ’s’, ’t’, ’s’, ‘r’, ‘r’]. A little harder to sort the cycles, but possible.

image using scatter plot to simulate cycling line width with fewer, longer cycles

This one also used 5 cycles. But only 3 wheels of shape: [’s’, ’e’, ‘c’] (square, ellipse, circle).

image using scatter plot to simulate cycling line width with fewer, longer cycles

I do like the longer cycles. So that stays. But, I am beginning to think that the maximum marker size needs some playing with. Seemed to me that it was a little small in the images above. And that perhaps on occasion, a larger value would be appropriate.

Maximum Marker Size

So need to look at using a range of maximum sizes as well. Though I am not sure how to select that size. Ideally larger sizes for less complex images. But I have no idea how to determine image complexity. Let’s just start with a random selection from some range or set of maximum sizes.

The very first image above had a maximum marker size of 16000. The ones in the previous section maxed out at 1500. Big difference. So, after some playing around, I decided to allow for maximum marker sizes between 1500 and 105000 inclusive, at steps of 1000. And see what we get.

Because of the larger line thickness, I had to make some adjustments to my plot boundaries as some of the plots were overrunning the current limits.

      if not t_sv:
        # mx_sz = np.random.choice(list(range(1500, 5001, 500)))
        mx_sz = np.random.choice(list(range(1500, 10501, 1000)))
        sz_mult = mx_sz // hf_frq

And, here’s the same curve at different maximum marker sizes. And, of course, differing colour schemes and differing cycle sizes.

The first one has a maximum marker size of 1500, cycle size: 142.

image using scatter plot to simulate cycling line width with fewer, longer cycles and random maximum marker sizes

And this one at 3500 and cycle of 125. I haven’t yet figure out why the aspect ratio of the images changes. It is something to do with the background colourmap, but I don’t know what or why!

image using scatter plot to simulate cycling line width with fewer, longer cycles and random maximum marker sizes

This one has maximum marker size of 6500 and cycle of 142.

image using scatter plot to simulate cycling line width with fewer, longer cycles and random maximum marker sizes

And, max marker 7500, cycle 200.

image using scatter plot to simulate cycling line width with fewer, longer cycles and random maximum marker sizes

Max marker 10500, cycle 266.

image using scatter plot to simulate cycling line width with fewer, longer cycles and random maximum marker sizes

You know, I think random maximum marker sizes works for me. And the range I’ve settled on looks pretty good.

I do not believe how much fun this has been. Keep hitting myself on the head to remind me that this is not what I can expect in my future coding experience.

The originals were rather large for posting on a web site. So did a bulk convert on the above images. Which does not seem to have completely removed the borders. Software or me??

Time for a Break

I think that’s it for this one. Can’t completely ignore my other responsibilities. And, we are definitely and finally into gardening season. Lot’s to be done. Just got a couple dozen or so tomato starters and 8 or so pepper starter plants from our friend Lynn. He has, the last couple of years, been rather keen on starting various veggies and herbs from seed. Most industrious. And most generous with his results.

Though there will be another post. Few more things I’d like to try (or should that be checkout) with respect to this approach to producing images with a simulated cycling line width. For instance differing marker shapes. Starting the plots at different points in the line width cycle. And, whatever else comes to mind between then and now.

Until then, don’t forget to make note of your midnight ramblings. They just might work.

Resources