Saturday, January 29, 2011

POV-ray -another Pysanky Egg

This is a continuation on a series of POV-ray posts all focused on the Pysanky decoration of egg shapes.  An attempt to represent the drop pull method of wax application to the egg is the subject of this post.  To represent this I've borrowed some work on flower shapes from a previous post and added a simple methodology for extending the flower in a simple V shape lengthwise:

def cycleequation(step, start, stop, trigfunc, factorr, factortheta, startend):
    """
    Returns list of x, y coordinates that correspond
    to the equation of the form

    factorr * r * r = (trigfunc(factortheta * theta)) ** 2
 
    theta will be incremented by step.
    start is the first value of theta.
    stop is the last value of theta.

    Shape starts and ends at startend, preferably a
    coordinate along the POV-ray z axis in the
    direction negative of the other points.
    """
    coords = [startend]
    theta = start
    while theta < stop:
        coords.append(getcoords(theta, factorr, factortheta, trigfunc))
        theta += step
    coords.append(getcoords(stop, factorr, factortheta, trigfunc))
    coords.append(startend)
    return coords

The head of the shape is formed by the equation 

r ** 2 = (math.cos(2.0 * theta)) ** 2


from math.pi/4.0 + 0.2 to 3 * math.pi/4.0 - 0.2

For a long drop pull shape, a start and end point of (0.0, -15.0) yields a shape like the one pictured below:



The head and the lengthwise extension are slightly slope discordant.  I did this intentionally to try to get the effect of the real life dropping of hot wax on the egg and pulling away from the drop to make the V shape.


With a parameter of (0.0, -5.0) for the start-stop point, a shorter, stubby drop is formed:






Through a series of scaling and rotation operations, I was able to get the drop shapes positioned to decorate my egg:







It takes 41 different strokes to make this egg (the way I approached it).  I fell into the temptation briefly of naming the strokes 'stroke1', 'stroke2', etc.  This is bad.  Descriptive names are good.  Even with a deceptively simple design like this, it's impossible to figure out what's going on and mirror the strokes that can be mirrored without descriptive names like 'northeastcenter', 'northwestcenter', etc.

Once I get a chance to rework the code, I can shorten the names and eliminate repetitive code.  This pattern recognition/code refactoring exercise should keep me busy for a bit.  In the meantime, thanks for having a look.

Saturday, January 15, 2011

jython and java.util.Properties

The other day my boss gave me an assignment to create a static properties file with stringified numeric mappings of file lookups.  Turns out this is really easy in jython (if you're used to Python instead of Java).  Hardly any Java is required:

$ /usr/local/jdk-1.7.0/bin/java -jar jython.jar
Jython 2.5.1 (Release_2_5_1:6813, Sep 26 2009, 13:47:54)
[OpenJDK Client VM (Sun Microsystems Inc.)] on java1.7.0-internal
Type "help", "copyright", "credits" or "license" for more information.
>>> from java.util import Properties
>>> filemappings = {'1':'numberone.py',
...                 '2':'numbertwo.py',
...                 '3':'numberthree.py'}
>>> props = Properties()
>>> for key in filemappings:
...     props.setProperty(key, filemappings[key])
...
>>> fle = open('blah', 'w')
>>> props.store(fle, 'put comment here')
>>> fle.close()
>>> fle = open('blah', 'r')
>>> props = Properties()
>>> props.load(fle)
>>> props.getProperty('1')
u'numberone.py'
>>> for prop in props:
...     print prop, props.getProperty(prop)
...
3 numberthree.py
2 numbertwo.py
1 numberone.py
>>> fle.close()
>>>





The file itself looks like this:



#put comment here
#Sat Jan 15 16:24:57 MST 2011
3=numberthree.py
2=numbertwo.py
1=numberone.py


The value strings are stored in a manner that has them returned as Jython Unicode strings.
My case is the simplest one, in that the properties file is static.  To update it dynamically and store it for the next use (for example, to maintain state), you would need a writable file object.

Sunday, January 9, 2011

Prisms and Trigonometric Shapes - POV-ray

I was looking for a way to put a four petal flower shape on my Python-POV-ray generated egg.  As it turns out, this shape is a common geometric one called Pascal's Rose.

For expediency rather than efficiency's sake, I opted to generate the shape using individual x, y points (actually x, z in POV-ray):


def getcoords(theta, factorr, factortheta, trigfunc):
    """
    Returns two tuple of x and y values for
    the equation in the form

    factorr * r * r = trigfunc(factortheta * theta)
    """
    r = math.sqrt((trigfunc(factortheta * theta)) ** 2 / factorr)
    x = r * math.cos(theta)
    y = r * math.sin(theta)
    if abs(x) < ZEROLIMIT:
        x = 0.0
    if abs(y) < ZEROLIMIT:
        y = 0.0
    print x, y, theta
    return x, y

def cycleequation(step, start, stop, trigfunc, factorr, factortheta):
    """
    Returns list of x, y coordinates that correspond
    to the equation of the form

    factorr * r * r = trigfunc(factortheta * theta)

    theta will be incremented by step.
    start is the first value of theta.
    stop is the last value of theta.
    """
    coords = []
    theta = start
    while theta < stop:
        coords.append(getcoords(theta, factorr, factortheta, trigfunc))
        theta += step
    coords.append(getcoords(stop, factorr, factortheta, trigfunc))
    return coords

math.cos creates Pascal's Rose for this equation.  I had to add a Prism object to the POV-ray recipe I am using.

class Prism(Item):
  def __init__(self, base, top, numpts, *opts, **kwargs):
    Item.__init__(self,"prism",(base, top, numpts),opts,**kwargs)

For me it was easiest to just translate the points of the prism into POV-ray SDL language prior to feeding them to Python:

def translatetopovray(pts):
    """
    Returns string in povray format of list
    of points pts.
    """
    stringpoints = []
    for pt in pts:
        stringpoints.append('<%.8f, %.8f>' % pt)
    return string.join(stringpoints, ',')

This allowed for generation of a prism:

This is just a rough representation of what I wanted.  The scene I was creating required a two colored flower, the outside as gold and the inside as orange.  Further, the outside edge needed to appear to be of constant width, as one would achieve by applying wax to an egg with the pysanky drawing tool, the kistka.

To get this effect I used a rough offset algorithm - get the centerpoint of the line segment on the outside edge of the "flower", offset a set distance along the normal to the outside line segment, then connect all the inner points.

def gettan(pt1, pt2):
    """
    Returns the tangent of the directional
    angle formed by line segment pt1-pt2.
    """
    return (pt2[1] - pt1[1])/(pt2[0] - pt1[0])

def getmidpoint(pt1, pt2):
    """
    Get the midpoint of the line segment
    pt1-pt2.
    """
    x = pt1[0] + (pt2[0] - pt1[0])/2.0
    y = pt1[1] + (pt2[1] - pt1[1])/2.0
    return x, y

def calcoffsetpoint(pt1, pt2, offset):
    """
    Get point offset distance offset
    from the midpoint of line segment
    pt1-pt2 to the left.
    """
    midpoint = getmidpoint(pt1, pt2)
    # get normal
    # quadrant I
    if pt2[0] >= pt1[0] and pt2[1] >= pt1[1]:
        theta = math.atan(gettan(pt1, pt2)) + math.pi/2.0
        x = midpoint[0] + math.cos(theta) * offset
        y = midpoint[1] + math.sin(theta) * offset
    # quadrant II
    elif pt2[0] <= pt1[0] and pt2[1] >= pt1[1]:
        theta = math.atan(gettan(pt1, pt2)) + math.pi/2.0
        x = midpoint[0] - math.cos(theta) * offset
        y = midpoint[1] - math.sin(theta) * offset
    # quadrant III
    elif pt2[0] <= pt1[0] and pt2[1] <= pt1[1]:
        theta = 3.0 * math.pi/2.0 + math.atan(gettan(pt1, pt2))
        x = midpoint[0] + math.cos(theta) * offset
        y = midpoint[1] + math.sin(theta) * offset
    # quadrant IV
    elif pt2[0] >= pt1[0] and pt2[1] <= pt1[1]:
        theta = 3.0 * math.pi/2.0 + math.atan(gettan(pt1, pt2))
        x = midpoint[0] - math.cos(theta) * offset
        y = midpoint[1] - math.sin(theta) * offset
    else:
        print pt1, pt2
        print 'error - theta undefined'
        theta = 0.0
    return x, y

def getmiddle(step, start, stop, trigfunc, factorr, factortheta, offset):
    """
    Return list of points for middle of 'petal'.
    """
    startend = (0.0, 0.0)
    pts = [startend]
    # need to offset by fixed distance along
    # normal to each line segment
    # need to get pts along original flower
    # but not near 0.0, 0.0 where they will
    # overlap
    outsidepts = cycleequation(step, start, stop,
        trigfunc, factorr, factortheta)
    counter = 0
    maxcounter = len(outsidepts)
    while (counter + 1) < (maxcounter - 1):
        pts.append(calcoffsetpoint(outsidepts[counter],
            outsidepts[counter + 1], offset))
        counter += 1
    pts.append(startend)
    return pts

My analytical geometry is a bit rough, but this works well enough for what I was trying to accomplish.

Through some intersecting and positioning, I was able to get the effect I wanted.  This egg design, according to the Sixty Score of Easter Eggs book by Elyjiw, is from the Odessa region of the Ukraine.


Thanks for having a look.