jack: (Default)
After rather too much fiddling, I have a simple Django website on Heroku that uses the Pylogram cartoons. So anyone can create a simple two-figure one-panel cartoon.

Create a new cartoon: http://toons.herokuapp.com/
Example: http://toons.herokuapp.com/toons/fxyne

Obviously, I would like to fix the alignment and spacing of the text and add word wrap, and add some layout so the page has something other than a couple of text boxes at the top.

And obviously, this level of example is not very useful, but I like the idea because the dynamic generation of the cartoon means that anything else you can specify (extra panels, captions, hats, furniture, etc) can immediately be used by anyone. The biggest hurdle is an interface for people to specify it.

Some of that basically works but isn't in the website example yet, and I'm away for a week, and due for a break from coding, so I may not do so soon.
jack: (Default)
Cartoons, now with added captions!

The wonky edges of the panels I think are because I used floats
instead of Fractions to see if it was any faster; I think that was a
mistake and I need to switch back.

Source for the cartoon:
https://github.com/CartesianDaemon/pylogram/blob/master/example_cartoonstrip.py
jack: (Default)
This isn't actually part of pylogram, it works on any arrays, but is useful for expressing things in Pylogram. Assuming that Point is an object with .x and .y members and Array acts like a list of Points, it lets you say:
import pylogram

# Graph of y=2x+1
pts = Array(10,Point)
each(pts).y = 100-each(pts).x * 2 + 1
each(pts).x = prev(pts).x + 1
every(pts).col = rgb(0,0,0)

display(pts)

A few caveats:

  • I'm not sure if this is cool or horrible
  • In this example Point and Array are pylogram objects, so you can specify y in terms of x before specifying x.
  • It also works if pts was a normal list of structs, except that it's normal assignemnt so you have to assign x before using x to calculate y
  • However, there's a bug in the "each(pts).x = prev(pts).x" case where prev(pts).x returns a copy of the list rather than the actual values, so after you update pts[1] in terms of pts[0], you don't get that value used to update pts[2]
  • If you want to assign an element of the array, instead of a member of an element, you need the "each(int_arr).val = each(int_arr).val+1" syntax
  • Colours don't actually work yet, I just wanted an example of setting every element to the same value.
  • I was originally going to make this a property of array, which means you might be able to use slice notation: "arr[each].y = arr[each].x" and you wouldn't have to worry about assigning to elements of the array. But having it in separate functions means they work on any iterable container too.
jack: (Default)
I implemented what I wanted arrays for, in order to be able to draw simple stick figures, where you can specify the number of panels and the number and size of figures per panel and it auto-generates it.

Image of example output:
https://github.com/CartesianDaemon/pylogram/blob/master/cartoonstrip.png
Example image )

Source for example:
https://github.com/CartesianDaemon/pylogram/blob/master/example_cartoonstrip.py

It also involved tidying up the constrain solver so instead of solving everything at the end, it normalises the constraints as they're applied, which makes everything a lot quicker.

It is incredibly gratifying to look at my code and say it really is getting cleaner as I work on it more :) The new constraint classes are a lot simpler, once a lot of plumbing code was obsoleted and removed.

It's still slow drawing multiple panels, though. There are a lot of equations for three panels plus nine characters, but almost all of them are trivial, like "a=b" or "a=b+2". I'm not sure, is it likely to be better to optimise it a bit in python (eg. keeping a list of which equations use a variable and only reduce those) or convert the guts to C++ (which should be possible, only the interface uses a lot of
dynamic stuff, a most of the rest ought to be a fairly direct translation)?
jack: (Default)
I've also added arrays, so you can do:
# Warning, untested
fib = Arr(10,Var)
for a,b,c in fib.adj_objs(3):
 c = a + b
fib[0] = fib[1] = 1
assert fib == (1, 1, 2, 3, 5, 8, 13, 21, 34, 55)

graph = Arr(10,Point)
for x,(pt,f) in enumerate(zip(graph,fib)):
 pt.x = 10 * x
 pt.y = 150 - 2 * f
But I really wanted to give an alternative to the "for ... in" syntax, something like:
 fib_pts = Arr(10,Point)
 fib.each() = fib.prev() + fib.prev(2)
or maybe:
 fib[pyl.idx] = fib[pyl.idx-1] + fib[pyl.idx-2]
jack: (Default)
That took more fiddling than I'd hoped to get all the modulo operators in the right place, but exactly the same system works for modulo equations:
from pylogram import constrain, default_vars as vars

constrain(   vars.a + 5*vars.b == 22 , mod=17 )
constrain( 2*vars.a +   vars.b == -5 , mod=17 )

print( " >> a , b =", vars.a, ",", vars.b )

#  >> a , b = 8, 13

assert (  8 + 5*13)%17 , (22)%17
assert (2*8 +   13)%17 , (-6)%17
jack: (Default)
Once I had the dry-run draw code, it was fairly easy to add actual drawing, to turn:
    from tkinter import *

    master = Tk()
    canvas = Canvas(master, width=300, height=300)
    canvas.pack()

    big_lollypop = Lollypop()
    big_lollypop.mid = Point(150,150)
    big_lollypop.height = 150
    big_lollypop.draw(canvas)

    mainloop()
into:
Read more... )
jack: (Default)
I'd meant to do something else this weekend, but I spend a lot of time on a different project. I reimplemented a lot of Mark Dominus' linogram in Python.

I haven't quite decided on the right syntax yet, you can either say constrain( equation_i_want_to_be_true ) or you can use Obj objects like Point, Line, Circle, etc where normal assignment syntax expresses a new constraint.

Behind the scenes it caches each constraint you express, and solves them as a big set of linear equations that it tries to keep in a solved state, dividing them into "variables it knows the answer to" which you can read at any time, and "variables yet to be determined". It rejects new constraints that produce contradictions.

An example of use to solve a set of linear equations would be:
    from pylogram import constrain, default_vars as vars
    constrain(   vars.a + vars.b   == 2  )
    constrain( 2*vars.a + vars.b/3 == -7 )
    print( "a , b =", vars.a, ",", vars.b )

Which gives output:
    >> -23/5 , 33/5

Read more... )