Carrying on from last time, let’s see if I can sort the colour pattern and the curve and background alpha values.

More Refactoring

The alpha value for the curve is generally fixed at 0.6. But it can change. The alpha value for the background is randomly generated everytime an image is produced. And, that was happening when I repeated the image. So, I was not actually getting the same image each time.

For the colouring of the curve itself, a start value into the colour cycle is also randomly generated for each redraw of the saved image. Hence the colour changes in the image itself.

Of course I now need to save some new image parameters. The colour cycle start index, the colour cycle alpha value and the background alpha value. And, use those where appropriate.

More Data to Save and Use

I added c_ndx = 1 to the appropriate section of the g_vars module. There were already global variables for the alpha values: alph and bg_lpha. (Not very consistent with my variable names, am I?)

Then added some more dictionary elements to the output of get_image_dtl().

def get_image_dtl(pg_ttl):
  c_d = {
    'pttl': pg_ttl, 'ln_sz': g.ln_w, 'cmap': g.rcm, 'c_lph': g.alph, 'c_ndx': g.c_ndx,
    'dobg': g.use_bb2, 'bgcm': g.bg_cmnm, 'bg_lph': g.bg_lpha,
    'fig_sz': g.fig_sz, 'dpi': g.i_dpi, 'c_cyc': g.cycle, 'abg': g.abg, 
  }

  return c_d

And, updated set_rpt_data() to take care of the three additional bits of data.

def set_rpt_data():
  # set repeat global
  g.do_rpt = True
  # intiialize and generate curve data
  # 'k' fold symmetry
  g.k_f = r.d_rpt[r.do_nbr]['k_f']
  # congruency vs k_f
  g.cgv = r.d_rpt[r.do_nbr]['cgv']
  # widths of none circular shapes or diameter of circle
  g.wds = r.d_rpt[r.do_nbr]['wds']
  # heights of none circular shapes
  g.hts = r.d_rpt[r.do_nbr]['hts']
  # get rid of the imaginary unit if present
  g.r_wds = [max(np.real(rd), np.imag(rd)) for rd in g.wds]
  g.r_hts = [max(np.real(rd), np.imag(rd)) for rd in g.hts]
  # get the actually frequencies and congruency
  g.freqs = r.d_rpt[r.do_nbr]['freqs']
  # deal with the new repeat data values
  g.cycle = r.d_rpt[r.do_nbr]['c_cyc']
  g.alph = r.d_rpt[r.do_nbr]['c_lpha']
  g.c_ndx = r.d_rpt[r.do_nbr]['c_ndx']
  g.abg = r.d_rpt[r.do_nbr]['abg']
  g.bg_lpha = r.d_rpt[r.do_nbr]['bg_lpha']

  if g.DEBUG:
    print(f"\tinit_curve(): data points = {g.t_pts}")

  splt.set_spiro(g.freqs, g.wds, g.hts, nbr_t=g.t_pts)

And, finally updated the image display template, disp_image.html, to show that data.

  <ul class="dots">
      {% if i_data['c_lph'] %}
      <li>Colour map: {{ i_data['cmap'] }} ({{ i_data['c_lph'] }}, {{ i_data['c_ndx'] }})</li>
      {% else %}
      <li>Colour map: {{ i_data['cmap'] }}</li>
      {% endif %}
      {% if i_data['dobg'] %}
      {% if i_data['bg_lph'] %}
        <li>BG colour map: {{ i_data['bgcm'] }} ({{ i_data['bg_lph'] }})</li>
      {% else %}
      <li>BG colour map: {{ i_data['bgcm'] }}</li>
      {% endif %}
      {% endif %}
      <li>Line width (if used): {{ i_data['ln_sz'] }}</li>
      <li>Image size: {{ i_data['fig_sz'] }}</li>
      <li>Image DPI: {{ i_data['dpi'] }}</li>
    </ul>

Which now looks like the following.

Drawing Parameters

  • Colour map: cubehelix (0.6, 16)
  • BG colour map: ocean (0.16)
  • Line width (if used): 1
  • Image size: 8
  • Image DPI: 72

Image Section Colour

You may recall, that for any images using changing colours, I randomly select an index into the colour cycle and a ‘jump’ size. In the plotting loop, the first colour used is the one at the index c_ndx. Then I move through the colour cycle jumping c_jmp colours for each loop. Looping to the start of the cycle as necessary using the modulus operator. The code in the loop is after the dotted line.

  c_len = len(g.cycle)
  c_ndx = rng.integers(0, c_len-2)
  g.c_ndx = c_ndx
  c_jmp = int(c_len * 0.15)
  if c_jmp % 2 == 0:
    c_jmp += 1
... ...
    c_ndx = (c_ndx + c_jmp) % c_len

So, we need to control that c_ndx value when running in a repeat image scenario. This happens in a number of functions. I won’t bother listing them all. Definitely not DRY code I have been producing. If I ever really refactor am going to have to work on removing the immense amount of repeated code blocks.

The above was wherever necessary changed to the following. Making sure to save any generated start index to the appropriate global variable.

  c_len = len(g.cycle)
  if not g.do_rpt:
    c_ndx = rng.integers(0, c_len-2)
    g.c_ndx = c_ndx
  else:
    c_ndx = g.c_ndx
  c_jmp = c_len // 40
  c_jmp = int(c_len * 0.15)
  if c_jmp % 2 == 0:
    c_jmp += 1

Alpha Values

The alpha value for the background is handled in proc_curve_form(). I put that code into an if block that would only execute if a repeat was not being drawn.

  if not g.do_rpt:
      if "tab" in g.bg_cmnm or g.bg_cmnm == 'default':
        g.bg_lpha = rng.integers(5, 15) / 100
      else:
        g.bg_lpha = rng.integers(15, 33) / 100

The curve alpha value is going to be an issue. In some cases I use a local variable whose value is never saved to the global. In other cases, I use the global value which is apparently a default. For now, I will deal with the two specific cases. Eventually as I progress, I will deal with them all.

In the case of the cycling width image type, I changed the line l_alph = 1 to the following.

  if not g.do_rpt:
    l_alph = 1
    g.alph = 1
  else:
    l_alph = g.alph

For the function, btw_n_apart(), I was using a hardcoded 1 in the call to the fill_between(). E.G. alpha=1. So, I added the above if block and modified the parameter value in all the calls, alpha=l_alph.

Test the Refactoring

generate a new cycling width style spirograph for test
The original cycling line width style spirograph image.

And, when I ran the repeat route, I got the following.

attempt to repeat the test image
Attempt to repeat the above spirograph image.

That repeat is very close to the original. Not an exact duplicate, but certainly getting there.

Done

There is obviously something I am missing. Expect it is the multiplier I use to get the maximum line width. Though there may be something else I don’t currently recall affecting the duplication attempt.

Will give it some thought. Maybe comeback and add another section to this post. Or just start another one. Not a lot of commentary on my part. But a lot of scrolling to get through the post. Smaller, rather than bigger, may be best.

Until next time, may your bugs make you smile.