Okay, decided to add the extra information to the image display page. It adds a fair bit of seldom used information. But it is simply text in <li> tags, so not too bad. And, if it helps me get repeat images exactly as I originally saw and liked them, all for the good.

Of course it meant I pretty much had to modify every route. I did skip the animation related routes. I will just show the changes for one route.

I also had to modify the global variables file, g_vars.py, and the get_image_dtl() function in the app library module, sp_app_lib.py.

Refactor the Image Information

Okay, let’s get this done.

Global Vars

Added one new variable, abg. I am quite horrible at variable names. That one, at least to me, means array background. Here’s the pertinent section of the file with the addition.

# colour background variables
bg_cmnm = None  # nmae of background colour map
bg_cmap = None  # colour map to use for background
bg_lpha = 0.58  # default alpha value for background
use_bb2 = False # use 2nd version blotchy background
bb2_dim = 4     # dimension for colour array
use_rand_bg = True  # select bg colour map from list available for current curve colour map
abg = None

Generate and Save Data in Routes

The next thing I did was refactor the routes to if necessary generate the data and save it to the appropriate global variable. And, in fact the curve’s colour cycle was already being saved to a global variable. So, I only had to deal with the array for the background pattern. And that was rather simple (if repeated all too many times).

I changed bax2, _ = sal.set_bg(ax) in each route to bax2, g.abg = sal.set_bg(ax). Bingo!

Update Image Detail Dictionary

The reason I made the above modifications is so that I could let get_image_dtl() look after adding the additional information to the dictionary sent to the image display template. Also a simple change.

I changed the following:

def get_image_dtl(pg_ttl):
  c_d = {
    'pttl': pg_ttl, 'ln_sz': g.ln_w, 'cmap': g.rcm, 'dobg': g.use_bb2, 'bgcm': g.bg_cmnm,
    'fig_sz': g.fig_sz, 'dpi': g.i_dpi
  }

  return c_d

to the following:

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

  return c_d

I mean, how easy can it get.

Refactor Image Display Template

Had to add the code to display the information, but again rather straightforward and fairly simple. The following was added to the bottom of the content block.

  <h3>Colour Arrays</h3>
  
  <ul class="dots">
    <li>Background array: {{ i_data['abg'] }}</li>
    <li>Colour cycle: {{ i_data['c_cyc'] }}</li>
  </ul>

{% endblock %}

Result

And for a random cycling line width spirograph, the following was displayed at the bottom of the page presenting the image.

Colour Arrays

  • Background array: [ [0.72150447 0.63785385 0.64074449 0.45465285] [0.3204368 0.91695756 0.37960593 0.44851666] [0.14313813 0.21237489 0.45522572 0.23737202] [0.83414924 0.42462584 0.18825301 0.76050846]]
  • Colour cycle: [(0.001462, 0.000466, 0.013866, 1.0), (0.018815, 0.016026, 0.084584, 1.0), (0.056615, 0.04216, 0.167446, 1.0), (0.102815, 0.06301, 0.257854, 1.0), (0.159018, 0.068354, 0.352688, 1.0), (0.232077, 0.059889, 0.437695, 1.0), (0.29774, 0.066117, 0.478243, 1.0), (0.359898, 0.087831, 0.496778, 1.0), (0.420791, 0.11292, 0.505215, 1.0), (0.488088, 0.139186, 0.508011, 1.0), (0.550287, 0.161158, 0.505719, 1.0), (0.613617, 0.181811, 0.498536, 1.0),
    More/Less(0.677786, 0.202203, 0.485819, 1.0), (0.748378, 0.226377, 0.464794, 1.0), (0.810855, 0.252861, 0.439305, 1.0), (0.868793, 0.287728, 0.409303, 1.0), (0.917689, 0.3355, 0.379915, 1.0), (0.955849, 0.4044, 0.360619, 1.0), (0.97669, 0.476226, 0.364466, 1.0), (0.988533, 0.550446, 0.388365, 1.0), (0.994738, 0.62435, 0.427397, 1.0), (0.997254, 0.704611, 0.482635, 1.0), (0.996727, 0.776795, 0.541039, 1.0), (0.994222, 0.84854, 0.605696, 1.0), (0.99057, 0.920049, 0.675675, 1.0), (0.987053, 0.991438, 0.749504, 1.0), (0.993545, 0.862859, 0.619299, 1.0), (0.996162, 0.798348, 0.55982, 1.0), (0.997341, 0.733545, 0.505167, 1.0), (0.99658, 0.668256, 0.456192, 1.0), (0.993326, 0.602275, 0.41439, 1.0), (0.9867, 0.535582, 0.38221, 1.0), (0.973381, 0.46152, 0.361965, 1.0), (0.953099, 0.397563, 0.361438, 1.0), (0.921884, 0.341098, 0.377376, 1.0), (0.879464, 0.296125, 0.403118, 1.0), (0.828886, 0.262229, 0.430644, 1.0), (0.773695, 0.236249, 0.455289, 1.0), (0.716387, 0.214982, 0.47529, 1.0), (0.652056, 0.193986, 0.491611, 1.0), (0.594508, 0.175701, 0.501241, 1.0), (0.537755, 0.156894, 0.506551, 1.0), (0.481929, 0.136891, 0.507989, 1.0), (0.426877, 0.115395, 0.505714, 1.0), (0.372116, 0.092816, 0.499053, 1.0), (0.316654, 0.07169, 0.48538, 1.0), (0.25222, 0.059415, 0.453248, 1.0), (0.19146, 0.064818, 0.396152, 1.0), (0.135053, 0.068391, 0.315, 1.0), (0.088155, 0.058133, 0.229922, 1.0), (0.048062, 0.036607, 0.150327, 1.0)]

In some cases, that colour cycle array could get even bigger.

Big Test

Clearly, I now need to refactor my code to use that information if available. Then see if I do get exactly the same image when I use my repeat route. Well, after also generating and saving the enhanced data for a new image in the global variables file.

Just saved a spirograph image of the cycling line width style. When I started building the data dictionary, I realized that my current route code was specifically for mosaic style spirogrpah images. And, then looking at all the possibilities I realized the route code was going to get a touch crazy sorting the image types and the related functions. And in some cases loops for drawing various elements. E.G. rotational transforms.

The function parameters also vary between the various drawing functions. There are curve/image related variables being calculated that I may not yet have in the repetition data dictionary. Beginning to look like a nightmare. Will take a bit of time to think about things before I go crazy generating spaghetti code.

Get Started

I don’t currently see an easy way to avoid a lot of if blocks in the route function to cover the various options. So, I am going to try a cycling line width image, add an if block to cover the two current cases: mosaic or cycling line width using colour between.

Global Variables versus Image Data

I decided that I didn’t want to clutter the global variables file with a bunch of image data. Just doesn’t belong there. So I took out that data and moved it to a new module, repeats.py. Well I did leave do_rpt = False in the g_vars module. As I use it’s current setting in a few different functions/modules. I also converted d_rpt to a list, with the data dictionary of individual images as it’s elements (thinking ahead?). Then, I use another variable in the repeats module, do_nbr to index the list to get the image to repeat.

The function set_rpt_data() now looks like the following. Also showing the new import.

import repeats as r

... ...

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.abg = r.d_rpt[r.do_nbr]['abg']

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

In the route function, I removed the line g.rcm, g.cycle = sal.set_clr_map(ax, g.mx_s, g.rcm). Those two values are now being set using the repeat image data. Added a couple of if blocks to deal with drawing the images and generating the data to send to the display page template. And, of course changed all the references for g.d_rpt[item] to r.d_rpt[r.do_nbr][item]. Wasn’t going to show the whole function, but there were enough changes that I figured I should do so.

@app.route('/spirograph/repeat', methods=['GET', 'POST'])
def sp_repeat():
  pg_ttl = f"Repeat: {r.d_rpt[r.do_nbr]['i_typ']}"
  if request.method == 'GET' or request.method == 'POST':
    set_rpt_data()
    sal.cnt_btw = 0

    sal.proc_curve_form(r.d_rpt[r.do_nbr], r.d_rpt[r.do_nbr]['i_typ'])
    t_xs, t_ys, t_shp = sal.init_curve()

    r_xs, r_ys, m_xs, m_ys, m2_xs, m2_ys = sal.get_gnarly_dset(t_xs, t_ys, g.r_skp, g.drp_f, g.t_sy)

    ax.clear()
    ax.set_xlim(-0.1, 0.1)
    ax.set_ylim(-0.1, 0.1)
    ax.autoscale()
    if g.u_fgsz and g.fig_sz > 8:
      ax.margins(g.sz2mrg[g.u_fgsz])
    if g.u_again:
      sal.cnt_btw = 1
    else:
      sal.cnt_btw = 0

    if r.d_rpt[r.do_nbr]['i_typ'] == 'mosaic':
      sal.btw_n_apart(ax, r_xs, r_ys, dx=g.bw_dx, ol=g.bw_o, r=g.bw_r, fix=None, mlt=g.bw_m, sect=g.bw_s)
    elif r.d_rpt[r.do_nbr]['i_typ'] == 'clw_btw':
      h_cyc, mn_mlt, mx_mlt, m_inc, n_max, pp_clr = sal.cycle_lw_btw(ax, t_xs[-1], t_ys[-1], u_pcnt=g.use_pct, n_cyc=g.n_lwcyc, dppc=g.dpp_clr)

    ax.text(0.5, 0.01, "© koach (barkncats) 2022", ha="center", transform=ax.transAxes,
            fontsize=10, alpha=.3, c=g.cycle[len(g.cycle)//2])
    ax.autoscale()

    bax2, g.abg = sal.set_bg(ax)

    # convert image to base64 for embedding in html page
    data = sal.fig_2_base64(fig)

    c_data = sal.get_curve_dtl()
    i_data = sal.get_image_dtl(pg_ttl)

    if r.d_rpt[r.do_nbr]['i_typ'] == 'mosaic':
      p_data = {'bdx': g.bw_dx, 'bol': g.bw_o, 'bcs': g.bw_s,
                # 'bmlt': g.bw_m, 'mlts': sal.c_mlts
                'bmlt': g.u_rmlt != 'false', 'mlts': sal.c_mlts
              }
    elif r.d_rpt[r.do_nbr]['i_typ'] == 'clw_btw':
      p_data = {'mx_mlt': mx_mlt, 'mn_mlt': mn_mlt, 'm_inc': m_inc,
                'hf_frq': h_cyc, 'n_max': n_max, 'pp_clr': pp_clr,
                'u_pct': g.use_pct, 'n_lwc': g.n_lwcyc
                }

    g.do_rpt = False

    return render_template('disp_image.html', sp_img=data, type=r.d_rpt[r.do_nbr]['i_typ'], c_data=c_data, i_data = i_data, p_data=p_data)

A Few Changes in the App Library Module

In sp_app_lib.py I added the import for the repeats module.

And in the function, btw_n_apart(), I modified if not g.u_agn: to if not (g.u_agn and g.do_rpt):. (Beginning to wonder if I shouldn’t just have used g.u_agn instead of adding g.do_rpt. But, they actually work differently, so probably okay I did so.)

I also had to add an if block to make sure set_bg() used the specified background array, via the abg=g.abg parameter.

... ...

    if not g.do_rpt:
      baxt, abg = blotch_bg_2(axb, extent=(0, 1, 0, 1), dim=g.bb2_dim, cmap=g.bg_cmap, alpha=g.bg_lpha, transform=axb.transAxes, zorder=1, aspect='auto')
    else:
      baxt, abg = blotch_bg_2(axb, extent=(0, 1, 0, 1), abg=g.abg, dim=g.bb2_dim, cmap=g.bg_cmap, alpha=g.bg_lpha, transform=axb.transAxes, zorder=1, aspect='auto')

... ...

Test With New Image

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..

Lots of similarity. But, also lots of differences.

And, for some reference, here’s the data from the display page as added to repeats.py. I am showing all the variables in the file, but not the data for the image in the last post.

do_rpt = True
do_nbr = 3
d_rpt = []

... ...

d_rpt.append(
  {
    'i_typ': 'clw_btw',
    'shape': 'c',
    'wheels': '4',
    'k_f': 3,
    'cgv': 2,
    'freqs': [2, -7, -4, 14],
    'wds': [1, 0.5657651146125591j, 0.4636419043932005, 0.4251551432695591],
    'hts': [1, 0.5224678027865723j, 0.3913564985539285, 0.309642962585756],
    'ln_sz': '7',
    'cmap': 'jet',
    'c_nbr': '16',
    'c_cyc': [(0.0, 0.0, 0.5, 1.0), (0.0, 0.0, 0.67825311942959, 1.0), (0.0, 0.0, 0.85650623885918, 1.0), (0.0, 0.0, 1.0, 1.0), (0.0, 0.12745098039215685, 1.0, 1.0), (0.0, 0.3, 1.0, 1.0), (0.0, 0.4568627450980391, 1.0, 1.0), (0.0, 0.6137254901960785, 1.0, 1.0), (0.0, 0.7705882352941177, 1.0, 1.0), (0.03478810879190385, 0.9431372549019608, 0.9329538266919671, 1.0), (0.16129032258064513, 1.0, 0.8064516129032259, 1.0), (0.2877925363693864, 1.0, 0.6799493991144845, 1.0), (0.4142947501581275, 1.0, 0.5534471853257434, 1.0), (0.5534471853257431, 1.0, 0.4142947501581278, 1.0), (0.6799493991144844, 1.0, 0.2877925363693865, 1.0), (0.8064516129032256, 1.0, 0.16129032258064513, 1.0), (0.932953826691967, 1.0, 0.03478810879190386, 1.0), (1.0, 0.843137254901961, 0.0, 1.0), (1.0, 0.6978939724037765, 0.0, 1.0), (1.0, 0.5526506899055921, 0.0, 1.0), (1.0, 0.40740740740740755, 0.0, 1.0), (1.0, 0.24763979665940472, 0.0, 1.0), (1.0, 0.1023965141612202, 0.0, 1.0), (0.8565062388591802, 0.0, 0.0, 1.0), (0.6782531194295901, 0.0, 0.0, 1.0), (0.5, 0.0, 0.0, 1.0), (0.8208556149732622, 0.0, 0.0, 1.0), (0.9812834224598939, 0.058823529411765274, 0.0, 1.0), (1.0, 0.18954248366013093, 0.0, 1.0), (1.0, 0.3202614379084969, 0.0, 1.0), (1.0, 0.4509803921568629, 0.0, 1.0), (1.0, 0.5816993464052289, 0.0, 1.0), (1.0, 0.7269426289034134, 0.0, 1.0), (1.0, 0.8576615831517794, 0.0, 1.0), (0.9456040480708408, 0.9883805374001459, 0.022137887413030133, 1.0), (0.831752055660974, 1.0, 0.1359898798228969, 1.0), (0.7179000632511068, 1.0, 0.2498418722327641, 1.0), (0.6040480708412397, 1.0, 0.3636938646426312, 1.0), (0.4901960784313725, 1.0, 0.4775458570524984, 1.0), (0.3636938646426312, 1.0, 0.6040480708412397, 1.0), (0.24984187223276405, 1.0, 0.717900063251107, 1.0), (0.13598987982289687, 1.0, 0.8317520556609741, 1.0), (0.022137887413029723, 0.9274509803921569, 0.9456040480708413, 1.0), (0.0, 0.7862745098039213, 1.0, 1.0), (0.0, 0.6450980392156863, 1.0, 1.0), (0.0, 0.503921568627451, 1.0, 1.0), (0.0, 0.34705882352941175, 1.0, 1.0), (0.0, 0.20588235294117635, 1.0, 1.0), (0.0, 0.06470588235294118, 1.0, 1.0), (0.0, 0.0, 0.981283422459893, 1.0), (0.0, 0.0, 0.820855614973262, 1.0)],
    'c_lpha': 0.6,
    'c_ndx': 1,
    'do_bg': 'true',
    'c_bg': 'rainbow',
    'abg': [[0.91046052, 0.02509128, 0.59490719, 0.37365929], [0.08651942, 0.48278835, 0.54195799, 0.91269637], [0.0502208, 0.78281916, 0.31809638, 0.58101069], [0.07049545, 0.18987813, 0.76442259, 0.98590682]],
    'bg_lpha': 0.29,
    'npts': 2048,
    'sz': 8,
    'dpi': 72,
    'how': 'between adjacent datarows with overlap',
    'clw_pct': 'false',
    'clwc': '8',
    'clwpp': '12',
    'ntr': 'r',
    'shp_mlt': 'false',
    'u_agn': 'false',
    'torb': 'top',
    'drows': '1',
    'r_mlt': 'false',
    'do_drpt': 'false',
    'ani_step': 8,
  }
)

rpt_max = len(d_rpt)

Done

After some thought, I sort of figured I knew what was causing the differences in colour of the spirograph curve sections and the alpha values for the image and background. But, that will have to wait for the next post.

And, I would also like to add a querystring to the repeat route’s GET request. As I expect I will have a few images in the repeats module at any given time, it would be nice if I could select which one to display without modifying the do_rpt variable manually each time.

Until our next encounter, may your time at the keyboard be peaceful and satisfying. Mine was today.