Monday, February 28, 2011

jython + java.lang.Character.UnicodeBlock

In my post on Unicode Blocks in regular expressions, I mentioned there wasn't support for Unicode Blocks in CPython regular expressions.  Unicode Blocks are contiguous (by number) sections of the Unicode tables with some commonality among the characters.  There is another Java language feature that can be helpful for those interested in Unicode Blocks:  the java.lang.Character.UnicodeBlock object.  In jython:

Jython 2.5.1 (Release_2_5_1:6813, Sep 26 2009, 13:47:54)
[Java HotSpot(TM) 64-Bit Server VM (Sun Microsystems Inc.)] on java1.6.0_22
Type "help", "copyright", "credits" or "license" for more information.
>>> from java.lang.Character import UnicodeBlock
>>> UnicodeBlock.of(236)
LATIN_1_SUPPLEMENT
>>> UnicodeBlock.of('a')
BASIC_LATIN
>>> UnicodeBlock.of(738)
SPACING_MODIFIER_LETTERS
>>> UnicodeBlock.of(922)
GREEK

>>> UnicodeBlock.of(0xffee)
HALFWIDTH_AND_FULLWIDTH_FORMS


The "of" method can accept either a character or a Unicode numeric identifier as it's argument.  It provides a shorthand method of finding out roughly where a character is in Unicode and what it might represent.

Thursday, February 24, 2011

More SVG - trace from bitmap with Inkscape and color editing

I'm attempting to convert some of the pysanky eggs I drew with POV-ray to SVG so that I can scale them freely.  Also, once they're converted, I would like to be able to edit their colors in the same way I did with the Python logo in my last post.

To the best of my knowledge, Inkscape (potrace) is the most readily available tool for converting the bitmap images from POV-ray to SVG.  There are a few things to look out for, but this is still really easy:

1) Remove all reflection and shading from the textures (colors) in the POV-ray scene.  Work on getting even lighting for the whole scene.  Generate the scene without antialiasing (jagged edges are fine, and even desired).


2) Follow the directions here for tracing the SVG drawing.

 
Now to use the standard library's xml.etree.ElementTree package to get at the colors.  Before jumping in, I first removed any references to the old bmp image from POV-ray and checked on the three color values in the SVG file.


The three colors in the image are #fefefe (slightly off white), #000000 (black), and #00fd00 (light green).


# svgchangecolor.py


"""
Manipulation of colors in an SVG image.
"""

from xml.etree import ElementTree as ET

def drilldown(nodex, oldcolor, newcolor):
    """
    Walk node of element tree in search
    of oldcolor.

    Replace oldcolor with newcolor where
    oldcolor occurs.
    """
    childrenx = nodex.getchildren()
    if childrenx:
        for childx in childrenx:
            drilldown(childx, oldcolor, newcolor)
    else:
        itemsx = nodex.items()
        for itemx in itemsx:
            if itemx[1].find(oldcolor) > -1:
                oldcolorstr = itemx[1]
                newcolorstr = oldcolorstr.replace(oldcolor, newcolor)
                nodex.set(itemx[0], newcolorstr)

def changecolor(oldcolor, newcolor, filename):
    """
    Replaces oldcolor with newcolor in an
    SVG file named filename.

    All three arguments are strings.

    oldcolor and newcolor are hex strings
    in the format #xxxxxx.
    """
    # the colors are at the lowest level
    # in the XML (SVG) file
    # drill down until they are reached
    svgobj = ET.parse(filename)
    rootx = svgobj.getroot()
    drilldown(rootx, oldcolor, newcolor)
    svgobj.write(filename)


$ python2.5
Python 2.5.4 (r254:67916, Aug  9 2010, 08:57:51)
[GCC 4.2.1 20070719 ] on openbsd4
Type "help", "copyright", "credits" or "license" for more information.
>>> import svgchangecolor as svgcc
>>> svgcc.changecolor('#00fd00', '#ffff00', 'crossdroppullegg.svg')
>>> svgcc.changecolor('#000000', '#00aa00', 'crossdroppullegg.svg')
>>> svgcc.changecolor('#fefefe', '#000000', 'crossdroppullegg.svg')

>>>

There are only three elements with color in the file (the dark green one is doing all the "work" of filling in the pattern).  The script is really overkill in this case.  For a drawing with a number of separate paths with the same color, though, this could be very useful.

Monday, February 21, 2011

SVG, XML, and the Python logo

Legal notice:  the terms of use for the Python logo restrict the alteration or bastardization of the logo, including its colors.  I have contacted the Python Software Foundation and gotten permission to publish this blog entry.


The logo restrictions I violate in this post, for purposes of demonstrating working with svg, are:


   1) changing the color of the logo
   2) removal of the trademark symbol


While people are encouraged to use the logo, they are also encouraged to contact the PSF trademarks committee regarding questions of alteration.  Thank you for your consideration in this matter.

As a means of becoming more familiar with SVG (scalable vector graphics) and XML, I wanted to see if I could change the colors of the Python logo in code.  Although this is neither encouraged nor permitted (see notice above), it has been done on occasion in the past; I wanted to see how.

Since SVG is a subset of XML, I'm going to use an XML tool from the standard library (xml.etree.ElementTree) to work with the SVG image.

First, I downloaded the logo from python.org. I'm using the Java based tool Batik/Squiggle to view the SVG images:


To simplify the SVG file I removed the gray parts (text, trademark character, and shadow).  This I did by hand by looking for any gray colors of the hex form #xyxyxy in the file.

Also, I changed the name of the file I was referencing inside the SVG file to pythonlogochanged.svg.  This is important, otherwise the SVG file will continue to read whatever file is named inside it.


There are now six main elements to the file:
    1) the two shapes in the logo
    2) the two gradients for color variation across each shape
    3) the two sets of colors for start and stop in the gradation (two yellows and two blues)

It's number 3) that we're interested in.

I started out trying to use minidom from the xml standard library, but found ElementTree to be better suited to the task.  This tutorial from Effbot was helpful.

$ python2.5
Python 2.5.4 (r254:67916, Aug  9 2010, 08:57:51)
[GCC 4.2.1 20070719 ] on openbsd4
Type "help", "copyright", "credits" or "license" for more information.
>>> # open file
...
>>> svgobj = ET.parse('pythonlogochanged.svg')
>>> # get toplevel element
...
>>> rootx = svgobj.getroot()

Now it is time to drill down into the element tree.  I walked through this myself previously, so you'll have to trust that I know where the colors are in the tree.

>>> elementsx = rootx.getchildren()
>>> defsx = elementsx[2]
>>> elementsx = defsx.getchildren()
>>> yellowelement = elementsx[0]
>>> blueelement = elementsx[1]

First we'll look at the yellow color definitions.

>>> yellowchildren = yellowelement.getchildren()
>>> yellowchildren[0].items()
[('style', 'stop-color:#ffd43b;stop-opacity:1'), ('id', 'stop4673'), ('offset', '0')]
>>> yellowchildren[1].items()
[('style', 'stop-color:#ffe873;stop-opacity:1'), ('id', 'stop4675'), ('offset', '1')]

The yellow colors across the gradient are #ffd43b and #ffe873.
Now we'll look at the blue colors.

>>> bluechildren = blueelement.getchildren()
>>> bluechildren[0].items()
[('style', 'stop-color:#5a9fd4;stop-opacity:1'), ('id', 'stop4691'), ('offset', '0')]
>>> bluechildren[1].items()
[('style', 'stop-color:#306998;stop-opacity:1'), ('id', 'stop4693'), ('offset', '1')]

#5a9fd4 and #306998 are the blue colors across the gradient.

Let's try taking the color gradients out first without changing the blue and yellow colors.

>>> bluechildren[1].set('style', 'stop-color:#5a9fd4:stop-opacity:1')
>>> yellowchildren[1].set('style', 'stop-color:#ffd43b;stop-opacity:1')

Now the result needs to be written to disk.

>>> svgobj.write('pythonlogochanged.svg')


Still blue and yellow, but significantly different without the gradient.

Now let's make a black silhouette.

>>> bluechildren[0].set('style', 'stop-color:#000000:stop-opacity:1')
>>> bluechildren[1].set('style', 'stop-color:#000000:stop-opacity:1')
>>> yellowchildren[0].set('style', 'stop-color:#000000;stop-opacity:1')
>>> yellowchildren[1].set('style', 'stop-color:#000000;stop-opacity:1')
>>> svgobj.write('pythonlogochanged.svg')


The hardest part of this excercise was finding the appropriate parts of the SVG file to edit.  ElementTree makes it easy from there.


Wednesday, February 16, 2011

Simple Polygon Offset

I was working on another POV-ray pysanky egg design and had to work with offsetting polygons.  Stack Overflow had a brief description of how this could be done.  I chose to go with the simplest solution: drawing offset lines and connecting their intersection points.

Update:  ΤΖΩΤΖΙΟΥ was kind enough to introduce me to the math.atan2 function, which correctly identifies the quadrant the angle theta falls in, and rewrote my verbose function(s) concisely:

def calcoffsetpoint(pt1, pt2, offset):
    theta = math.atan2(pt2[1] - pt1[1],
                       pt2[0] - pt1[0])
    theta += math.pi/2.0
    return (pt1[0] - math.cos(theta) * offset,
            pt1[1] - math.sin(theta) * offset)
getoffsetintercept gets the b in y = mx + b needed to calculate the new point:

def getoffsetintercept(pt1, pt2, m, offset):
    """
    From points pt1 and pt2 defining a line
    in the Cartesian plane, the slope of the
    line m, and an offset distance,
    calculates the y intercept of
    the new line offset from the original.
    """
    x, y = calcoffsetpoint(pt1, pt2, offset)
    return y - m * x

The function that gets a single point along the polygon:

def getpt(pt1, pt2, pt3, offset):
    """
    Gets intersection point of the two
    lines defined by pt1, pt2, and pt3;
    offset is the distance to offset
    the point from the polygon.
    """
    # get first offset intercept
    m = (pt2[1] - pt1[1])/(pt2[0] - pt1[0])
    boffset = getoffsetintercept(pt1, pt2, m, offset)
    # get second offset intercept
    mprime = (pt3[1] - pt2[1])/(pt3[0] - pt2[0])
    boffsetprime = getoffsetintercept(pt2, pt3, mprime, offset)
    # get intersection of two offset lines
    newx = (boffsetprime - boffset)/(m - mprime)
    newy = m * newx + boffset
    return newx, newy

Lastly, the function that works the polygon offset:

def offsetpolygon(polyx, offset):
    """
    Offsets a clockwise list of coordinates
    polyx distance offset to the inside of
    the polygon.
    Returns list of offset points.
    """
    polyy = []
    # need three points at a time
    for counter in range(0, len(polyx) - 3):
        # get first offset intercept
        pt = getpt(polyx[counter],
                   polyx[counter + 1],
                   polyx[counter + 2],
                   offset)
        # append new point to polyy
        polyy.append(pt)
    # last three points
    pt = getpt(polyx[-3], polyx[-2], polyx[-1], offset)
    polyy.append(pt)
    pt = getpt(polyx[-2], polyx[-1], polyx[0], offset)
    polyy.append(pt)
    pt = getpt(polyx[-1], polyx[0], polyx[1], offset)
    polyy.append(pt)
    return polyy

Shown below is the shape I was trying to offset.


As the "Simple" in the entry's title suggests, this is a best case scenario:

    1) no degenerate polygons inside or outside
    2) no zero or infinite slopes

Actually, I should have some zero slopes, but I cheated and changed the coordinates.  The differences won't be seen by the naked eye, but are big enough for the computer to handle them.

Here is the egg I was working on; it's a Hutsul design from the Sixty Score of Easter Eggs book:


Thanks for having a look.

Tuesday, February 8, 2011

jython copy versus clone

We're a Java shop at work.  We do a lot of passing of Java objects back and forth through the jython API to our application.

The other day I had the need to copy a Java object.  It was a flat structure and my instinct was to use the object's clone method, which I did.  This got me thinking about the use of clone versus the copy module in jython and I gave it a look:

Jython 2.5.1 (Release_2_5_1:6813, Sep 26 2009, 13:47:54)
[Java HotSpot(TM) 64-Bit Server VM (Sun Microsystems Inc.)] on java1.6.0_22
Type "help", "copyright", "credits" or "license" for more information.
>>> from java.util import ArrayList
>>> a = ArrayList()
>>> a
[]
>>> a.add(1)
True
>>> a.add([2, 3])
True
>>> import copy
>>> b = a.clone()
>>> b
[1, [2, 3]]
>>> b[1]
[2, 3]
>>> b[1][1]
3
>>> b[1][1] = 22
>>> b
[1, [2, 22]]
>>> a
[1, [2, 22]]
>>> b = copy.deepcopy(a)
>>> b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
SystemError: Automatic proxy initialization should only occur on proxy classes


OK, clone works similar to copy.copy (shallow copy).  Not sure what's going on with deepcopy, but it doesn't appear to be available in the case of an ArrayList object.

Update:  as noted in the comments, the copy behavior on Java objects is a known bug and is scheduled to be fixed in jython version 2.5.2.  In the meantime, clone() would appear to be a decent option.

Let's test the shallow copy capability of clone():

>>> a = ArrayList()
>>> a.add(1)
True
>>> a.add(2)
True
>>> a.add(3)
True
>>> a
[1, 2, 3]
>>> b = a.clone()
>>> b
[1, 2, 3]
>>> b[2] = 22
>>> b
[1, 2, 22]
>>> a
[1, 2, 3]


Perfect - it works just like copy.copy() would.

Saturday, February 5, 2011

More POV-ray and Pysanky

Continuing with the series of pysanky eggs I've been working on with Python and POV-ray - another week, another egg.  This one is a Lemko design.  The dots, I believe, represent stars.

Getting the crown and base was relatively easy, as it just required the correct positioning of a single stroke, then rotating it:

def makecrown(baseobj):
    crownstrokes = [baseobj]
    theta = STEP
    while theta < 360.0:
        crownstrokes.append(pov.Object(baseobj,
                            rotate = (0.0, theta, 0.0)))
        theta += STEP
    return pov.Union(*crownstrokes)

In this case, step = 360.0/24.0.

Likewise, I have a function for the dots that made them easier to place:

def movedot(basedot, xtheta, ytheta):
    dot = pov.Object(basedot, rotate = (xtheta, ytheta, 0.0))
    return pov.Object(dot, translate = (0.0, 1.0, 0.0))

Ultimately, I'd like to get enough egg designs coded to put together some sort of scene in POV-ray dedicated to the eggs.  The following two scenes show what I have so far:

If I can identify a few more attractive, computationally friendly designs, I should be able to come up with enough eggs for a decent POV-ray egg scene.  In the meantime, thanks for having a look.