I was messing with variations for my, tentatively, last post in the series. I had a look at the first gnarly curve post (not yet published at the time). And, I realized that I might be short changing myself. Well, at least the potential of the curves/images. I was limiting the number of data points I plotted to a small subset of those available. I was plotting two to at most half the available rows generated for each curve’s dataset. I decided not to limit myself but use a randomly selected value of from 2 to all available rows.

So, I thought I might write another post, sort of out of sequence and cadence, looking at just what that does for the resulting images. My experiments following that change seemed to imply there was definitely value, in many cases, by using additional rows from a curve’s dataset. There is, in any curve’s dataset, one row for each wheel being used to generate the curve.

The last row represents the points of the final curve. Each preceding row represents the point on the circumference of that wheel on which the subsequent wheel is centered. As all the wheels are rotating those points move around the plotting area to a lesser or greater degree based on the wheels’ radii and frequencies of rotation. Every row has the same number of data points.

I am also going to write a new module that will generate subplots of the variations. For a given set of curve parameters, it will show plots using increasing numbers of the available datasets. Or differing line sizes. Or…

Comparing Number of Dataset Rows Used

This will likely take some fooling around to get working correctly. But then, that is half the fun. I won’t bother listing the imports, or the default/global variables. I will start with curves generated by circles. Eventually allowing for the use of other wheel shapes.

While working on the code for that last post I also got away from using a square aspect ratio for some of the plots. Especially those using different wheel shapes. Will likely end up doing that here as well.

To generate this plot type, I am passing in arrays of plot coordinates (one for the x-values and one for the y-values). What I think happens is that matplotlib plots lines between the coordinates of two adjacent rows in the arrays. If there a three datasets it does this twice, once between the first and second. And again between the second and third. At least that’s what I think happens. So to get any kind of gnarly curve we need at least two datasets in our arrays. And for the best results, we need to start with the last dataset and work towards the first. Though you could, if there are enough wheels, likely start from the second last or the third last and proceed accordingly.

The code to generate the first type of chart looks like the following. I am skipping a lot of the setup we’ve seen before.

  # init starting row and column
  rw = 0
  cl = 0
  # limit to max nbr subplots
  for nsb in range(p_rw * p_cl):

    cl = nsb % p_cl
    # and current row
    if nsb > 0:
      if cl == 0:
        rw += 1

    # set up colour cycler for circle colours
    # note, wrote a new function for this particular module
    rcm, cycle = set_clr_map(axs[rw, cl], cc=rcm)
    # tight gradient, not quite as interesting
    # axs[rw, cl].set_prop_cycle('color', cycle)

    axs[rw, cl].axis('off')
    if nsb == 0:
      axs[rw, cl].plot(t_xs[-1], t_ys[-1])
      axs[rw, cl].set_title('Plain Curve')
    else:
      ln_k = nsb + 1
      if ln_k <= n_whl:
        if ln_k < n_whl:
          axs[rw, cl].set_title(f'Last {ln_k} datasets, default line width')
        else:
          axs[rw, cl].set_title(f'All {ln_k} datasets, default line width')
        axs[rw, cl].plot(t_xs[-ln_k:], t_ys[-ln_k:])
plot the gnarly variations for differing numbers of rows in the plot value arrays for a randomly generated curve

Here’s the terminal output for that plot. Note: I am using png images so you should be able to zoom in for a closer look if you wish.

You selected plot type 1 with parameter(s): wheels -> None, shape -> c (Circle).
(gnarly plots with changing numbers of datasets plotted)

wheels (Circle): 5, k_f: 4, cgv: 1, points: 500
widths: [1, 0.672261275819719, 0.402882550940863, 0.2528712475516867, 0.13273353839410237j],
heights: [1, 0.6685601342010199, 0.4318910017785903, 0.37877884438045417j, 0.21976289283452222],
freqs: [1, 9, 13, 9, 13]
default colour map: hot, tight gradient
ln wd: None, ln alpha: 0.75

For this one, I would also like to show you the plots of the individual rows in the arrays. I won’t bother with the code.

plot of the rows in the curve plot value arrays for a randomly generated curve

Should be clear from the above images that the plot aspect ratio has not been set to square (i.e. equal). Added axis values to give you some idea of where the lines are coming from.

Then decided to code the curves showing the actual steps in generating the gnarly curve. Two different looks.

a slightly different view of the rows in the curve plot value arrays for a randomly generated curve overlaid with the 'gnarly' curve plot of the rows in the curve plot value arrays for a randomly generated curve overlaid with the 'gnarly' curve

Looks like my impression of what was happening is probably correct. Though I do apologize for the changing aspect ratios. Makes comparison just a touch more difficult than need be.

And, one more example of the compounding effect of using multiple rows for the plot.

plot the gnarly variations for differing numbers of rows in the plot value arrays for a randomly generated curve

Here’s the terminal output for that plot.

You selected plot type 1 with parameter(s): wheels -> 5, shape -> None.
 (gnarly plots with changing numbers of datasets plotted)

wheels (Tetracuspid): 5, k_f: 2, cgv: 1, points: 500
widths: [1, 0.5876852891868274j, 0.46458993144322264, 0.41366597673699845, 0.37445671603914094],
heights: [1, 0.7135324821909219, 0.6502339464232979, 0.5758409152389535, 0.45133085125792016],
freqs: [-1, 7, -3, 9, -1]
default colour map: twilight_shifted, tight gradient
ln wd: None, ln alpha: 0.89

Effect of Different Line Widths

Again a quick presentation of the basic code without any of the setup and other related code.

  # init starting row and column
  rw = 0
  cl = 0
  # limit to max nbr subplots
  for nsb in range(p_rw * p_cl):

    cl = nsb % p_cl
    # and current row
    if nsb > 0:
      if cl == 0:
        rw += 1

    # set up colour cycler for circle colours
    rcm, cycle = set_clr_map(axs[rw, cl], cc=rcm)

    axs[rw, cl].axis('off')
    if nsb == 0:
      axs[rw, cl].plot(t_xs[-1], t_ys[-1])
      axs[rw, cl].set_title('Plain Curve')
    else:
      ln_w = [None, 1, 2, 4, 6, 8, 10, 14, 18][nsb]
      lnw = 'Default' if ln_w is None else ln_w
      axs[rw, cl].set_title(f'All datasets, line width: {lnw}')
      for ln in range(n_whl//2, n_whl):
        axs[rw, cl].plot(t_xs[-ln:], t_ys[-ln:], lw=ln_w, alpha=alph)

And, a sample plot.

plot the gnarly variations for differing line sizes for a randomly generated curve

Trouble! Do you see what happens with the last line or two when plotted. There is nothing covering them so they stick out like a sore thumb. You will not believe how much time I wasted trying various potential solutions. Eventually I got a couple of ideas that likely work better than most.

Can’t say I am done but here’s the solution I have temporarily settled on. It seems to do the job and is simple. I drop the last line from the full plot. Then I redraw (plot) the first 25 lines which partially covers up the last line or two, from the first plot. Doesn’t always work as the implied rotation of the curve sometimes just shows the full size of a line segment. But in general does look better. I am not bothering with a multi-plot for various line sizes. Because, in fact, things now look pretty much the same regardless of line size at the size of the multi-plots.

But, did add code to randomly change the number of plotting points. Also, to select line sizes between 2 and 18. And, to randomly drop one or two of the rows from the end of the curve dataset. Have spent a goodly amount of time just repeatedly generating plots. A few of them will be shown after the basic code.

      r_xs = []
      r_ys = []
      m_xs = []
      m_ys = []

      skip = 0
      if n_whl < 5:
        skip = np.random.choice([0, 1])
      else:
        skip = np.random.choice([0, 1, 2])
      for i, crv in enumerate(t_xs):
        if i < n_whl - skip:
          r_xs.append(crv[:-1])
          r_ys.append(t_ys[i][:-1])
          m_xs.append(crv[:25])
          m_ys.append(t_ys[i][:25])
      print(f"dropping {skip} datasets (r_xs: {len(r_xs)})")
      lwr = np.random.randint(2, 19)
      lwr2 = lwr / 2
      print(f"plot 5 line width now: {lwr}")
      ax.plot(r_xs, r_ys, lw=lwr, alpha=alph)
      ax.plot(m_xs, m_ys, lw=lwr, alpha=alph)

This dropped the last dataset.

gnarly plot for a randomly generated curve using 'fix' to eliminate unwanted large line(s)
  You selected plot type 7
  (single gnarly plot, original dataset, with changing line size and extended radii array).

  wheels (Square): 4, k_f: 2, cgv: 1, points: 500
  widths: [1, 0.7027757813543477j, 0.60996163140743j, 0.5927608920689169],
  heights: [1, 0.7377083554809298j, 0.3930426166591305j, 0.2219691819721672],
  freqs: [3, -5, 7, -5]
  default colour map: viridis, full gradient
  ln wd: None, ln alpha: 0.64
  plot 5 now using 1000 data points
  dropping 1 datasets (r_xs: 3)
  plot 5 line width now: 13

This dropped the last two datasets.

gnarly plot for a randomly generated curve using 'fix' to eliminate unwanted large line(s)
  You selected plot type 7
  (single gnarly plot, original dataset, with changing line size and extended radii array).

  wheels (Square): 7, k_f: 6, cgv: 3, points: 500
  widths: [1, 0.6478525801468158j, 0.422468298703937j, 0.30019827557342466, 0.19587056011824142j, 0.125j, 0.125],
  heights: [1, 0.6928101320234437j, 0.5979100616857167, 0.34165633543134494j, 0.19158944607520792, 0.17177959687271313, 0.17133528409197124],
  freqs: [-3, -15, 9, -3, -3, 21, -15]
  default colour map: default, full gradient
  ln wd: None, ln alpha: 0.9
  plot 5 now using 1500 data points
  dropping 2 datasets (r_xs: 5)
  plot 5 line width now: 2

In this one you can see how the perceived angle of rotation can still cause some largish lines on the curve image.

gnarly plot for a randomly generated curve using 'fix' to eliminate unwanted large line(s)
  You selected plot type 7
  (single gnarly plot, original dataset, with changing line size and extended radii array).

  wheels (Circle): 3, k_f: 3, cgv: 2, points: 500
  widths: [1, 0.7099981384668245, 0.4388124387621105],
  heights: [1, 0.5730850306035137, 0.5212872042551904],
  freqs: [2, 8, -1]
  default colour map: hot, full gradient
  ln wd: None, ln alpha: 0.89
  plot 5 now using 1500 data points
  dropping 0 datasets (r_xs: 3)
  plot 5 line width now: 11
gnarly plot for a randomly generated curve using 'fix' to eliminate unwanted large line(s)
  You selected plot type 7
  (single gnarly plot, original dataset, with changing line size and extended radii array).

  wheels (Tetracuspid): 6, k_f: 4, cgv: 3, points: 500
  widths: [1, 0.6193065942488816, 0.37697938351347626j, 0.3501304392804537j, 0.19980660262160393j, 0.17326589512051513],
  heights: [1, 0.5149242816382915j, 0.3382984276125193, 0.337931395540526j, 0.2096927914680031, 0.2031050885766257],
  freqs: [3, -5, -13, -1, -5, -1]
  default colour map: viridis, full gradient
  ln wd: None, ln alpha: 0.85
  plot 5 now using 500 data points
  dropping 0 datasets (r_xs: 3)
  plot 5 line width now: 11
gnarly plot for a randomly generated curve using 'fix' to eliminate unwanted large line(s)
  You selected plot type 7
  (single gnarly plot, original dataset, with changing line size and extended radii array).

  wheels (Circle): 8, k_f: 4, cgv: 3, points: 500
  widths: [1, 0.5655949713598107, 0.5352181487301252, 0.4838580482033818, 0.31076571813701587, 0.1622188925169619, 0.1593135332820691, 0.125j],
  heights: [1, 0.6035563897061702j, 0.5668257889719073j, 0.3746396012442885, 0.24258246796750188, 0.125j, 0.125, 0.125],
  freqs: [7, 3, -9, -9, -13, -9, 19, -9]
  default colour map: twilight_shifted, full gradient
  ln wd: None, ln alpha: 0.72
  plot 5 now using 500 data points
  dropping 0 datasets (r_xs: 3)
  plot 5 line width now: 9

Dropping from Start or End of Dataset

I tentatively thought this post was done. But, got thinking about something.

In the above curves, I was dropping rows from the end of the curve’s dataset. In most of my more recent work (draft posts yet to be seen), I was dropping rows from the beginning of the dataset. I figured I should look at that a little more closely. So, new plot type added to the current module.

  re_xs = []
  re_ys = []
  me_xs = []
  me_ys = []
  rs_xs = []
  rs_ys = []
  ms_xs = []
  ms_ys = []

  skip = 0
  if n_whl < 4:
    skip = np.random.choice([1])
  elif n_whl > 6:
    skip = np.random.choice([1, 2, 3])
  else:
    skip = np.random.choice([1, 2])

  # r_xs = t_xs[::-1]
  # r_ys = t_ys[::-1]
  # r_xs = t_xs
  # r_ys = t_ys
  for i, crv in enumerate(t_xs):
    # drop from end
    if i < n_whl - skip:
      re_xs.append(crv[:-1])
      re_ys.append(t_ys[i][:-1])
      me_xs.append(crv[:25])
      me_ys.append(t_ys[i][:25])
    # drop from start
    if i >= skip:
      rs_xs.append(crv[:-1])
      rs_ys.append(t_ys[i][:-1])
      ms_xs.append(crv[:25])
      ms_ys.append(t_ys[i][:25])
    
  print(f"dropping {skip} datasets (r_xs: {len(re_xs)})")

  lwr = np.random.randint(2, 19)
  lwr2 = lwr / 2
  print(f"plot 10 line width now: {lwr}")

  # make sure plot fits
  x_mn = min(t_xs[-1]) - 0.2
  x_mx = max(t_xs[-1]) + 0.2
  y_mn = min(t_ys[-1]) - 0.2
  y_mx = max(t_ys[-1]) + 0.2

  # and let's plot upto 8 subplots
  # init starting row and column
  rw = 0
  cl = 0
  # limit to max nbr subplots
  for nsb in range(p_rw * p_cl):

    cl = nsb % p_cl
    # and current row
    if nsb > 0:
      if cl == 0:
        rw += 1

    # set up colour cycler for circle colours
    rcm, cycle = set_clr_map(axs[rw, cl], n_vals=lc_frq, cc=rcm)
    # rcm, cycle = splt.set_clr_map_tight(axs[rw, cl], n_vals=lc_frq, cc=rcm)
    # tight gradient, not quite as interesting
    # axs[rw, cl].set_prop_cycle('color', cycle)

    axs[rw, cl].axis('off')
    axs[rw, cl].set_xlim(x_mn, x_mx)  
    axs[rw, cl].set_ylim(y_mn, y_mx)
    # set the aspect ratio to square
    axs[rw, cl].set_aspect('equal')

    # axs[rw, cl].set_title(sb_ttl)
    if nsb == 0:
      axs[rw, cl].set_title(f'{skip} {"curves" if skip > 1 else "curve"} dropped from end of dataset\nline width: {lwr}')
      axs[rw, cl].plot(re_xs, re_ys, lw=lwr, alpha=alph)
      axs[rw, cl].plot(me_xs, me_ys, lw=lwr, alpha=alph)
    else:
      axs[rw, cl].set_title(f'{skip} {"curves" if skip > 1 else "curve"} dropped from start of dataset\nline width: {lwr}')
      axs[rw, cl].plot(rs_xs, rs_ys, lw=lwr, alpha=alph)
      axs[rw, cl].plot(ms_xs, ms_ys, lw=lwr, alpha=alph)

And, here’s a few examples. Just love how easy it is to generate these things once the script is written. While working on this section, I likely produced 50 images to look at in 10-15 minutes. So much fun.

plot showing gnarly curves for same dataset but dropping rows from opposite ends of the dataset
  You selected plot type 10
  (comparing gnarly plots with datasets dropped from front versus end).

  wheels (Circle): 5, k_f: 3, cgv: 1, points: 500
  widths: [1, 0.5091570692831524j, 0.4099497006295479, 0.36522170619888894, 0.3190237294979745],
  heights: [1, 0.6158895568713917, 0.4666066789480788j, 0.41494464823227284j, 0.372706027060366],
  freqs: [-2, 13, -2, 10, -5]
  default colour map: viridis, full gradient
  ln wd: None, ln alpha: 0.66
  dropping 2 datasets (r_xs: 3)
  plot 10 line width now: 8
plot showing gnarly curves for same dataset but dropping rows from opposite ends of the dataset
  You selected plot type 10
  (comparing gnarly plots with datasets dropped from front versus end).

  wheels (Ellipse): 3, k_f: 3, cgv: 1, points: 500
  widths: [1, 0.7202629694801501, 0.5264818854962413],
  heights: [1, 0.7322250049399075, 0.4290171363613821j],
  freqs: [4, 1, 13]
  default colour map: twilight_shifted, full gradient
  ln wd: None, ln alpha: 0.72
  dropping 1 datasets (r_xs: 2)
  plot 10 line width now: 9

So far looks like dropping from the front leaves more detail in the image. Perhaps even adds a bit of subtlety. Though the edges of the curve do seem to get more ragged in this case. However that could be a consequence of the line width used for the plot.

Also, usually the more curves you drop, the bigger the difference in the two images. Though not always. And, the wheel shapes, dimensions and frequencies would, given a specific number of dropped rows, likely have a significant impact on the outcome.

plot showing gnarly curves for same dataset but dropping rows from opposite ends of the dataset
  You selected plot type 10
  (comparing gnarly plots with datasets dropped from front versus end).

  wheels (Square): 3, k_f: 2, cgv: 1, points: 500
  widths: [1, 0.6213873224989493j, 0.6045999960114662],
  heights: [1, 0.7374760365427575j, 0.7233595900180926j],
  freqs: [1, 9, -7]
  default colour map: GnBu, full gradient
  ln wd: None, ln alpha: 0.81
  dropping 1 datasets (r_xs: 2)
  plot 10 line width now: 14

That said, dropping too many lines from the front can remove a considerable amount of interest from the resulting image.

plot showing gnarly curves for same dataset but dropping rows from opposite ends of the dataset
  You selected plot type 10 with parameter(s): wheels -> 7, shape -> None.
  (comparing gnarly plots with datasets dropped from front versus end)

  wheels (Tetracuspid): 7, k_f: 3, cgv: 1, points: 500
  widths: [1, 0.5055206284127902, 0.46137645295077684j, 0.23647147818313027j, 0.1578651958931276, 0.125j, 0.125],
  heights: [1, 0.6796868274404586, 0.37080523158004963, 0.2793182345113025j, 0.1621423548462832j, 0.1605531817391008, 0.125],
  freqs: [-2, -5, -8, 13, 1, 1, 10]
  default colour map: hot, full gradient
  ln wd: None, ln alpha: 0.62
  dropping 3 datasets (r_xs: 4)
  plot 10 line width now: 3

Done M’thinks

There is probably any number of other things I could perhaps look at or compare choices. But, this post is plently long enough. All output images are very much dependent on the wheel shape, the wheel dimensions and frequencies, the chosen k-fold symmetry and the specific degree of that symmetry. Not too mention line widths, alpha values and even line types. Expect that the number of points used to generate the curve also has some impact in certain, maybe all, cases. Lots of things to consider and/or control. A little beyond where my thinking has been of late.

I have added an unlisted post presenting the code module used for this post. Do note, the code is horribly sloppy and has some bugs that I have not yet bothered to resolve. And, I now also have to think about changing the code module for my, tentatively, final post based on what has been discovered working on this post.

Have fun coding. May your code be bug free.