Well, turns out I still have some bugs in the code for my repeat route. I was trying to repeat a cycling line width using colour between (clw_btw) style spirograph image. But, it just wasn’t coming out correctly.

The image I wanted to repeat was the following. Something about it appealed to me. Though not to sure about the background. But one thing at a time.

spirograph image I was trying to repeat
Spirograph image to be repeated

And this is what I got when I tried to repeat.

the failed repeat of the spirograph image
Failed repeat of spirograph image

Not horrible. But, one of the things I liked about the original was those thin sections of colour. So, what is happening.

Let’s compare the cycling line width data for the original and the repeat.

# Original
Cycling Line Width
    Use percentage increment: False
    Number cycles: 8
    Half cycle points: 128
    Multiplier increment: 0.0003401358293962072
    Number of maximums: 8
    Datapoints per colour section: 4

# Failed Repeat
Cycling Line Width
    Use percentage increment: False
    Number cycles: 8
    Half cycle points: 64
    Multiplier increment: 0.0006802718909397331
    Number of maximums: 8
    Datapoints per colour section: 8

The repeat doesn’t have as many colour sections (64 vs 128) and they are larger (8 vs 4 datapoints). I am specifying those values in the repeat data dictionary. But they are not being used.

When I look at the function being used to draw the image, I see the following.

def cycle_lw_btw(axt, rxs, rys, u_pcnt=True, n_cyc=8, dppc=12, mx_grw=.25, no_plot=False):
  # rxs, rys 1D arrays
  n_dots = len(rxs)
  f_cyc = n_dots // n_cyc
  h_cyc = f_cyc // 2

... ...

  pp_clr = int(dppc * (n_dots // 512))
  for i in range(0, n_dots, pp_clr):
    c_end = i + pp_clr

I am passing the argument 4 in the dppc parameter. But it is never being used. Nor, it turns out, are my values for full and half cycle. Instead the function calculates its own values based on the number of data points and the number of cycles. And then it generates a completely different value for the number of datapoints per colour segment, pp_clr. Not what I want when generating a repeat image.

But, even though I had the data in the repeat image data dictionary, I was not putting it anywhere. So, I replaced the current line setting g.ddp_clr in the set_rpt_data() function in the main module with the following.

  g.dpp_clr = int(r.d_rpt[r.do_nbr]['clwpp'])
  g.lc_frq, g.hf_frq = r.d_rpt[r.do_nbr]['c_frq'], r.d_rpt[r.do_nbr]['h_frq']

Then, I made some modifications to the cycle_lw_btw() function.

def cycle_lw_btw(axt, rxs, rys, u_pcnt=True, n_cyc=8, dppc=12, mx_grw=.25, no_plot=False):
  # rxs, rys 1D arrays
  n_dots = len(rxs)
  if not g.do_rpt:
    f_cyc = n_dots // n_cyc
    h_cyc = f_cyc // 2
  else:
    f_cyc, h_cyc = g.lc_frq, g.hf_frq

... ...

  if not g.do_rpt:
    pp_clr = int(dppc * (n_dots // 512))
  else:
    pp_clr = int(dppc)
  for i in range(0, n_dots, pp_clr):
    c_end = i + pp_clr

That almost got the job done. In the end I had to increase the number of data points from 1024 to 2048. You may recall I had for some reason previously hardcoded that value to 2048 for this image type. Something that was covered/corrected in a previous post.

In the end I changed the background colour map and its opacity (alpha parameter). That gave me this result.

a successful repeat of the spirograph image with changed background
Successful repeat of the spirograph image

Not Done: clw_btw_t

When I tried to reproduce a clw_btw with transforms, I once again did not get the image I expected. The image I was attempting to repeat (albeit with a lighter background) follows.

the cycling line width image with transforms to be repeated
Image to be repeated

So, I started by applying the changes to cycle_lw_btw() above to the two functions used by the repeat route to generate this kind of image.

def clw_btw_make(rxs, rys, u_pcnt=False, n_cyc=8, dppc=12, mx_grw=.25):
  # rxs, rys 1D arrays
  n_dots = len(rxs)
  if not g.do_rpt:
    f_cyc = n_dots // n_cyc
    h_cyc = f_cyc // 2
  else:
    f_cyc, h_cyc = g.lc_frq, g.hf_frq
def clw_btw_plot(axt, rxs, rys1, rys2, dppc=12, u_ndx=None):
  # rxs, rys* 1D arrays
  n_dots = len(rxs)

... ...

  if not g.do_rpt:
    pp_clr = int(dppc * (n_dots // 512))
  else:
    pp_clr = int(dppc)
  for i in range(0, n_dots, pp_clr):
    c_end = i + pp_clr

But, no success. Not that it looks horribly worse. But, it did not repeat correctly. Why not?

failed repeat attempt of cycling line width with transforms
Failed repeat of cycling line width with transforms above

When I compare the data for the original and repeat images all the values are the same (excluding the background colour change and the number of data points).

I decided to trying generating just the base image, no transforms to see if it looked more or less as expected. Each time I ran the route I got a different image. Printing out some of the curve data, the base y values kept changing. I finally started digging further into the code.

I haven’t mentioned it but the underlying wheel shape is an ellipse. And, when I finally got to looking at the data generation function for ellipse shaped wheels I found the following.

def get_ellipse(wd, ht, rs, frq):
  e_wds, e_hts = [], []
  c_pts = get_unit_circ(rs, frq)
  # make sure ellipse is eccentric
  mult = round((1.75 - 1.25) * np.random.random_sample() + 1.25, 4)
  if wd == ht:
    if np.random.choice(['w', 'h']) == 'w':
      wd *= mult
    else:
      ht *= mult
  else:
    if wd > ht:
      wd *= mult
    else:
      ht *= mult
  e_wds.append(wd)
  e_hts.append(ht)
  cx = np.real(c_pts) * wd / 2
  cy = np.imag(c_pts) * ht / 2
  return (cx, cy)

And, here’s the original and modified width and height values for one run.

'wds': [1, 0.5330789734681889, 0.35089465188067426j],
'hts': [1, 0.6438726877572059, 0.4873409707171726j],

e_wds: [1.4271, 0.5330789734681889, 0.35089465188067426]
e_hts: [1, 1.0609090276175481, 0.7110304762763549]

So not much hope in recreating this image. I don’t have the correct widths and heights for each of the three ellipses.

So, I am going to add code to print out the modified widths and heights on the display page if there are any ellipses in an image. And I did check, only images with ellipses are affected by this creative bit of code.

Refactored ‘get_curve_dtl()’ in the sp_app_lib module to add those values to the curve details dictionary.

# in sp_app_lib module
def get_curve_dtl():
  if len(g.su) == 1:
    shape = f"{splt.shp_nm[g.su].lower()} ({g.su})"
  else:
    shape = g.su

  c_d = {
    'n_whl': g.n_whl, 'shape': shape, 'k_f': g.k_f, 'cgv': g.cgv,
    'freqs': g.freqs, 'wds': g.wds, 'hts': g.hts, 'agn': g.u_agn,
    'npts': g.t_pts, 'e_wds': splt.e_wds, 'e_hts': splt.e_hts
  }

  return c_d

And, in the disp_iamge.html template I add the following if block (a line above and below for context).

        <li>Heights: {{ c_data['hts'] }}</li>
        {% if 'e' in c_data['shape'] %}
        <li>Modified Widths: {{ c_data['e_wds'] }}</li>
        <li>Modified Heights: {{ c_data['e_hts'] }}</li>
        {% endif %}
        <li>Data points: {{ c_data['npts'] }}</li>

I regenerated the image multiple times until I got something close to the original. I replaced the original widths and heights with the modified values in the repeat data dictionary.

    # 'wds': [1, 0.5330789734681889, 0.35089465188067426j],
    # 'hts': [1, 0.6438726877572059, 0.4873409707171726j],
    'wds': [1, 0.5330789734681889, 0.35089465188067426],
    'hts': [1.6536, 0.8869346273855511, 0.7825233966805639],

I then added an import of the global variables module to my old spiro_plotlib module. And refactored get_ellipse().

import g_vars as g

... ...

def get_ellipse(wd, ht, rs, frq):
  c_pts = get_unit_circ(rs, frq)
  # make sure ellipse is eccentric
  if not g.do_rpt:
    mult = round((1.75 - 1.25) * np.random.random_sample() + 1.25, 4)
    if wd == ht:
      if np.random.choice(['w', 'h']) == 'w':
        wd *= mult
      else:
        ht *= mult
    else:
      if wd > ht:
        wd *= mult
      else:
        ht *= mult
  e_wds.append(wd)
  e_hts.append(ht)
  cx = np.real(c_pts) * wd / 2
  cy = np.imag(c_pts) * ht / 2
  return (cx, cy)

And, now I get the same image every time I generate the repeat.

an almost successful repeat attempt of cycling line width with transforms
Almost a repeat of cycling line width with transforms above

Done

Wow, a much longer and more time consuming post than I expected. But, it is always nice to find solutions to one’s problems (especially the self-created ones).

Until next time, may your solutions come a lot easier than mine typically do.