Okay, had a rest, ready to go once again.

Image Completion

We will start with the third function I discussed last time in my conceptual view of how I would refactor the routes. That’s:

  • adding the copyright notice
  • adding the background colours
  • resetting the number of plotting points as appropriate
  • creating and returning the base64 version of the image

That’s quite a bit for one function. And not likely to be thought of as dealing with one concern. But small steps. Can always split some of that up as necessary.

New Function

I will call this one fini_image(). It will take at least two parameters, one for the figure and one for the plot axis. And this point it will only return the base64 for the spirograph image. Which is used to display the spirograph via the display page Flask template. Things may change but that looks like a good start. I will once again start by showing the pertinent code from the clw and mosaic routes.

def fini_image(fig, ax):
  ## clw route
  ax.autoscale()
  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)
  ... ...
  # set number of datapoints back to default in case another image type is selected
  g.t_pts = int(g.u_pts)
  g.rds = np.linspace(0, 2*np.pi, g.t_pts)

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

  ## mosaic route
  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)

  return data

Well, except for an extra call to autoscale() and resetting the plotting point value, pretty much identical. I have been calling autoscale() all over the place. Am going to reduce the number of calls and see what happens. And, here’s the function we will test.

def fini_image(fig, ax):
  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])
  # decided to add border around image
  ax.patch.set_edgecolor('black')  
  ax.patch.set_linewidth(2)  

  bax2, g.abg = sal.set_bg(ax)
  # having scaling issues with some images, can't figure it out
  # so using both these methods for now
  ax.autoscale()
  ax.autoscale_view()

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

  # if necessary reverse any change in plotting points by one of the routes
  if int(g.u_pts) != g.t_pts:
    g.t_pts = int(g.u_pts)
    g.rds = np.linspace(0, 2*np.pi, g.t_pts)

  return data

And refactoring the routes, things will now look like the following.

# pertinent part of clw_btw route
  elif request.method == 'POST':
    t_xs, t_ys, *_ = get_curve_data('clw_btw', request.form, 2048)
    setup_image(ax)
    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)
    data = fini_image(fig, ax)

# pertinent part of mosaic route
  elif request.method == 'POST':
    r_xs, r_ys, *_ = get_curve_data('clw_btw', request.form, g.t_pts)
    setup_image(ax)
    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)
    data = fini_image(fig, ax)

# pertinent part of repeat route
  if request.method == 'GET' or request.method == 'POST':
    set_rpt_data()
    r_xs, r_ys, m_xs, m_ys, m2_xs, m2_ys = get_curve_data(r.d_rpt[r.do_nbr]['i_typ'], r.d_rpt[r.do_nbr], g.t_pts)
    setup_image(ax)
    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, r_xs[-1], r_ys[-1], u_pcnt=g.use_pct, n_cyc=g.n_lwcyc, dppc=g.dpp_clr)
    data = fini_image(fig, ax)

And a quick test says all three routes work just fine.

Refactor Gnarly Route

Okay let’s see if those new functions can be used to refactor the gnarly route, sp_gnarly(). Here’s the refactored code for the route’s POST block. And, by the way it works just fine. And, yes the colour cycle and background arrays are also displayed on the page.

  elif request.method == 'POST':
    r_xs, r_ys, m_xs, m_ys, m2_xs, m2_ys = get_curve_data('clw_btw', request.form, g.t_pts)
    setup_image(ax)

    ax.plot(r_xs, r_ys, lw=g.ln_w, alpha=g.alph, zorder=g.pz_ord)
    ax.plot(m_xs, m_ys, lw=g.ln_w, alpha=g.alph, zorder=g.pz_ord)
    ax.plot(m2_xs, m2_ys, lw=g.ln_w, alpha=g.alph, zorder=g.pz_ord)
    ax.autoscale()

    data = fini_image(fig, ax)

    d_end = "top" if g.drp_f else "bottom"
    c_data = sal.get_curve_dtl()
    i_data = sal.get_image_dtl(pg_ttl)
    p_data = {
              'd_end': d_end, 'd_nbr': g.r_skp
             }

    return render_template('disp_image.html', sp_img=data, type='gnarly', c_data=c_data, i_data=i_data, p_data=p_data)

Refactor Other Routes

Okay I am going to try and refactor all the non-animation routes. Or at least as much as possible. Those with transforms and animation will likely have some code left for the next step in this refactoring effort.

This may take awhile. Perhaps you should go for a coffee or a short walk outside. After a night’s rest, I finished refactoring, at least to some extenet, all the routes.

Before refactoring, the main module was around 46 KB. After refactoring is is approximately 36 KB. Roughly a 20% reduction in size. Pretty good for a first attempt. And there are a lot of commented out code sections from previous work still in there. Will remove those in the near future.

I also tested each route before and after refactoring. They all still work.

I won’t bother showing any of the newly refactored routes. Between this and the previous post I expect one could figure out what was done in each one.

Select Which Image to Repeat

Before I call it quits for this post, one small thing to cover.

I have numerous images stored in the list in the repeats module. I wanted to be able to easily select which one to use when I executed the repeat route. Figured a querystring would do the job.

You may recall from the section Global Variables versus Image Data in Repeat Images, Part II that I have a variable, do_nbr which is used to determine which image to reproduce when the repeat route is called. I propose to use a querystring of ?rpt_nbr=# in the repeat url to allow me to specify which of the available images to reproduce. If absent, it will use whatever the current value is. If present, code in the route will set the variable to the new value if valid. Note, that I generate a variable in the repeats module called rpt_max which is the current length of the repeat image list. And, that did not take all that much code.

I added the following at the top of route’s code. Should likely check that int(rpt_nbr) will work, but for now…

  if request.method == 'GET' or request.method == 'POST':
    rpt_nbr = request.args.get('rpt_nbr', None)
    if rpt_nbr:
      tst_nbr = int(rpt_nbr)
      if tst_nbr > 0 and tst_nbr < r.rpt_max:
        r.do_nbr = tst_nbr
      else:
        r.do_nbr = 1

    pg_ttl = f"Repeat: {r.d_rpt[r.do_nbr]['i_typ']}"
... ...

And the URL below, which in my case is valid, reproduces the currently last image in the list in the repeats module:

http://localhost:5000/spirograph/repeat?rpt_nbr=6

Unfortunately, the 4 earliest spirographs in the list do not produce an image. My refactoring did not account for them missing some values. So errors are generated. But that is for another day.

Done

I think that’s it for this one. I will look at fixing the problem of images, in the repeat images dictionary, with missing values (which will likely mean a fair amount of refactoring of existing code). Or, I may look at getting the repeat route to work for all the non-animation image types (possibly less work). Or I may look at seeing if it is possible to put the route code used in generating transforms into one or two new functions. Don’t you just love choices, eh!

Until next time, may your choices not be overwhelming.