Search code examples
pythonscipywanddxfbspline

Generating Spline Curves with Wand and Python


I'm attempting to draw the raster representation of spline curves extracted from DXF files. I've extracted the data from the DXF files using the ezdxf library and I'm using the Python Wand library (ImageMagick) to draw the images. Wand has a spline function but what it draws doesn't always match the curves from the DXF files. This is no surprise since the Wand spline function doesn't have inputs for the knots or the degree of the DXF spline curve. The following images show what the DXF shapes look like. The Wand spline function works well with one example but not another.

The first is a spline oval Oval Example.

Oval Example

The second is a spine rectangle Rectangle Example.

Rectangle Example

The data points and knots from the DXF files for these shapes have been extracted and used in the following sample code which creates sample bitmaps. Even though the knots and degree aren't taken into account the oval renders well test_image_oval.bmp.

test_image_oval.bmp

The rectangle is misshaped at all sides and corners test_image_rect.bmp.

test_image_rect.bmp

The control points are plotted in red The knots are included but not used.

from wand.image import Image
from wand.color import Color
from wand.drawing import Drawing

############################################
point_list_oval =   [   (5.0,              1.5,              0),
                        (5.0,              0.6715728753,     0),
                        (3.8807118745,     0.0,              0),
                        (2.5,              0.0,              0),
                        (1.1192881255,     0.0,              0),
                        (0.0,              0.6715728753,     0),
                        (0.0,              1.5,              0),
                        (0.0,              2.3284271247,     0),
                        (1.1192881255,     3.0,              0),
                        (2.5,              3.0,              0),
                        (3.8807118745,     3.0,              0),
                        (5.0,              2.3284271247,     0),
                        (5.0,              1.5,              0)]

knots_oval       = [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0]

############################################
point_list_rect =   [(3.75, 0.0, 0),
                     (3.75,             0.0,              0),
                     (0.25,             0.0,              0),
                     (0.25,             0.0,              0),
                     (0.1125,           0.0,              0),
                     (0.0,              0.1125,           0),
                     (0.0,              0.25,             0),
                     (0.0,              0.25,             0),
                     (0.0,              5.75,             0),
                     (0.0,              5.75,             0),
                     (0.0,              5.8875,           0),
                     (0.1125,           6.0,              0),
                     (0.25,             6.0,              0),
                     (0.25,             6.0,              0),
                     (3.75,             6.0,              0),
                     (3.75,             6.0,              0),
                     (3.8875,           6.0,              0),
                     (4.0,              5.8875,           0),
                     (4.0,              5.75,             0),
                     (4.0,              5.75,             0),
                     (4.0,              0.25,             0),
                     (4.0,              0.25,             0),
                     (4.0,              0.1125,           0),
                     (3.8875,           0.0,              0),
                     (3.75,             0.0,              0)]

knots_rect =  [0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 5.0, 5.0, 5.0, 6.0, 6.0, 6.0, 7.0, 7.0, 7.0, 8.0, 8.0, 8.0, 8.0]


############################################
def draw_spline(img_width, img_height, DPI, point_list, filename):
    POINT_SIZE = 1
    STROKE_WIDTH = 0.5

    img = Image(width=img_width, height=img_height, background=Color('white'))

    # Convert the points to a 96dpi image space, remove the z axis
    converstion_list = []
    for point in point_list:
        point = (int(round(point[0] * DPI)), int(round(point[1] * DPI)))
        converstion_list.append(point)
    point_list = converstion_list

    # Draw the control points
    with Drawing() as draw:
        draw.stroke_width = STROKE_WIDTH
        draw.stroke_color = Color('red')
        draw.fill_color = Color('red')

        print '---------------------'
        print 'image points'
        for point in point_list:
            perim = (point[0] + POINT_SIZE, point[1])
            draw.circle(point, perim)
            print point
        draw(img)

    # draw the bezier path
    with Drawing() as draw:
        draw.stroke_width = STROKE_WIDTH
        draw.stroke_color = Color('green')
        draw.fill_color = Color('transparent')

        draw.bezier(point_list)

        draw(img)

    img.save(filename=filename)

############################################
DPI = 96
draw_spline(577,385, DPI, point_list_oval,'test_image_oval.bmp')
draw_spline(577, 700, DPI, point_list_rect, 'test_image_rect.bmp')

Does anyone have insight on how to use the Wand library to generate a spline curve and take into account the knots and degree along with the data points?

I've also tried using the SciPy library spline function (interpolate.splprep) with limited success. That's an alternative possibility for a solution. Following that path would mean using the SciPy library to interpolate a large set of points and the use the polyline function in Wand to approximate the curve. If anyone is interested I could include that sample code as well but I don't want to muddle the thread too much.

Any insights on Spline and Wand would be greatly appreciated.

Thanks .


Solution

  • The key thing to understand is that bezier is a spline, but not all splines are bezier. So for the DXF, just draw in chunks of four-coordinates, and use the last used point as the first in the next iteration.

    # draw the bezier path
    with Drawing() as draw:
        draw.stroke_width = STROKE_WIDTH
        draw.stroke_color = Color('green')
        draw.fill_color = Color('transparent')
        chunk = []                   # Prototype a temporary list.
        for points in point_list:    # Iterate over all points.
            chunk.append(points)     # Append to temporary list.
            if len(chunk) == 4:      # Ready to draw?
                draw.bezier(chunk)   # Draw spline segment.
                del chunk[0:3]       # Clear first three items, and reuse last point.
        draw(img)
    

    oval rect