]> Some of my projects - openlase.git/commitdiff
New Python bindings via Cython
authorHector Martin <hector@marcansoft.com>
Sun, 20 Mar 2011 07:42:48 +0000 (08:42 +0100)
committerHector Martin <hector@marcansoft.com>
Sun, 20 Mar 2011 07:42:48 +0000 (08:42 +0100)
CMakeLists.txt
examples/27c3_slides/CMakeLists.txt
examples/harp2.py [new file with mode: 0644]
examples/showtext.py [new file with mode: 0644]
examples/simple.py [new file with mode: 0644]
libol/CMakeLists.txt
python/CMakeLists.txt [new file with mode: 0644]
python/pylase.pyx [new file with mode: 0644]

index c8f5f11e3381d16e6a997b854dbaf8c0ab79724e..4f864c38f01852032423b67ab86104529d867732 100644 (file)
@@ -30,6 +30,7 @@ endif()
 
 find_package(Threads REQUIRED)
 find_package(JACK REQUIRED)
+find_package(PythonInterp REQUIRED)
 find_package(Qt4)
 find_package(FFmpeg)
 find_package(OpenGL)
@@ -46,4 +47,5 @@ add_definitions(-Wall)
 add_subdirectory (libol)
 add_subdirectory (output)
 add_subdirectory (tools)
+add_subdirectory (python)
 add_subdirectory (examples)
index 636d60e6e9fd2ca096778e7c0d970132e3f34054..5140fd034c4aa104c706f1ea9a76493f91fafae5 100644 (file)
@@ -35,7 +35,7 @@ if(CURSES_FOUND AND FFMPEG_FOUND)
       DEPENDS ${CMAKE_SOURCE_DIR}/tools/svg2ild.py
       MAIN_DEPENDENCY ${NAME}.svg
       DEPENDS ${NAME}.svg ${NAME}.cfg
-      COMMAND python ${CMAKE_SOURCE_DIR}/tools/svg2ild.py -q ${ARGN} -cfg ${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.cfg ${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.svg ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.ild)
+      COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/svg2ild.py -q ${ARGN} -cfg ${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.cfg ${CMAKE_CURRENT_SOURCE_DIR}/${NAME}.svg ${CMAKE_CURRENT_BINARY_DIR}/${NAME}.ild)
   endfunction()
 
   svg2ild(openlase-logo -noopt)
diff --git a/examples/harp2.py b/examples/harp2.py
new file mode 100644 (file)
index 0000000..f3ebff2
--- /dev/null
@@ -0,0 +1,185 @@
+#         OpenLase - a realtime laser graphics toolkit
+#
+# Copyright (C) 2009-2011 Hector Martin "marcan" <hector@marcansoft.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 or version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+# This is the laser harp sensing portion, which has nothing to do with OpenLase.
+# You need OpenCV (2.2.0) and PyALSA installed
+# Pass the destination ALSA MIDI client:port pair as an argument.
+
+# You may need to edit some constants here
+
+THRESHOLD=60
+WIDTH=320
+HEIGHT=240
+
+VELOCITY=90
+
+import sys, math
+import cv
+
+from pyalsa import alsaseq
+import pylase as ol
+import threading, time
+
+class LaserThread(threading.Thread):
+       def __init__(self):
+               threading.Thread.__init__(self)
+               self.die = False
+               self.dots = []
+
+       def run(self):
+               ol.init()
+               params = ol.RenderParams()
+               params.render_flags = ol.RENDER_NOREORDER | ol.RENDER_GRAYSCALE
+               ol.setRenderParams(params)
+
+               while not self.die:
+                       ol.loadIdentity()
+                       for x,y in self.dots:
+                               ol.dot((x,y), 30, ol.C_WHITE)
+
+                       ol.renderFrame(100)
+               ol.shutdown()
+
+seq = alsaseq.Sequencer(name="default", clientname=sys.argv[0], streams = alsaseq.SEQ_OPEN_DUPLEX, mode = alsaseq.SEQ_NONBLOCK)
+port = seq.create_simple_port(
+       name='out',
+       type = alsaseq.SEQ_PORT_TYPE_MIDI_GENERIC | alsaseq.SEQ_PORT_TYPE_APPLICATION,
+       caps = alsaseq.SEQ_PORT_CAP_READ | alsaseq.SEQ_PORT_CAP_SUBS_READ)
+
+def note(n, state=False):
+       if state:
+               etype = alsaseq.SEQ_EVENT_NOTEON
+       else:
+               etype = alsaseq.SEQ_EVENT_NOTEOFF
+       event = alsaseq.SeqEvent(type=etype)
+       event.set_data({'note.channel': 0, 'note.note': n, 'note.velocity': 90})
+       seq.output_event(event)
+       seq.drain_output()
+
+if len(sys.argv) > 1:
+       toport = seq.parse_address(sys.argv[1])
+       seq.connect_ports((seq.client_id, port), toport)
+
+def getpoints(image):
+       points = []
+       cv.CvtColor(image, grey, cv.CV_RGB2GRAY)
+       cv.Threshold(grey, grey, THRESHOLD, 255, cv.CV_THRESH_BINARY)
+       cv.Copy(grey, grey2)
+
+       storage = cv.CreateMemStorage(0)
+       contour = cv.FindContours(grey, storage, cv.CV_RETR_CCOMP, cv.CV_CHAIN_APPROX_SIMPLE)
+       while contour:
+               bound_rect = cv.BoundingRect(list(contour))
+               contour = contour.h_next()
+
+               pt = (bound_rect[0] + bound_rect[2]/2, bound_rect[1] + bound_rect[3]/2)
+               #cv.Circle(image, pt, 2, cv.CV_RGB(255,0,0), 1)
+               points.append(pt)
+       return points
+
+def near(p1, p2, d):
+       x,y = (p1[0]-p2[0],p1[1]-p2[1])
+       dist = math.sqrt(x**2+y**2)
+       return dist < d
+
+oldstate = None
+
+olt = LaserThread()
+print "Starting thread"
+
+v_off = -0.1
+
+n_start = 47
+n_end = 47 + 25
+n_off = 0
+
+offsets = [0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0]
+xpos = [0, 0.5, 1, 1.5, 2, 3, 3.5, 4, 4.5, 5, 5.5, 6]
+
+def gxpos(x):
+       o = x // 12
+       v = x % 12
+       return xpos[v] + o * 7
+
+dots = []
+
+xstart = gxpos(n_start)
+xend = gxpos(n_end)
+
+for n in range(n_start, n_end+1):
+       ld = (n + n_off) % len(offsets)
+       off = offsets[ld] * v_off
+       x = 2 * (gxpos(n + n_off) - xstart) / float(xend - xstart)
+       x -= 1.0
+       x *= 0.99
+       dots.append((-x, off))
+
+olt.dots = dots
+
+time.sleep(1)
+camera = cv.CreateCameraCapture(0)
+cv.SetCaptureProperty(camera, cv.CV_CAP_PROP_FRAME_WIDTH, WIDTH)
+cv.SetCaptureProperty(camera, cv.CV_CAP_PROP_FRAME_HEIGHT, HEIGHT)
+cv.SetCaptureProperty(camera, cv.CV_CAP_PROP_BRIGHTNESS, 0)
+
+image = cv.QueryFrame(camera)
+image = cv.QueryFrame(camera)
+image = cv.QueryFrame(camera)
+
+grey = cv.CreateImage(cv.GetSize(image), cv.IPL_DEPTH_8U, 1)
+grey2 = cv.CreateImage(cv.GetSize(image), cv.IPL_DEPTH_8U, 1)
+
+
+olt.start()
+print "Thread running"
+
+try:
+       for i in range(30):
+               image = cv.QueryFrame(camera)
+       refpoints = getpoints(image)
+       refpoints.sort(key=lambda x: -x[0])
+
+       print len(refpoints), n_end - n_start + 1
+
+       while True:
+               image = cv.QueryFrame(camera)
+               cpoints = getpoints(image)
+               state = [False]*len(refpoints)
+               for i, rp in enumerate(refpoints):
+                       for cp in cpoints:
+                               if near(rp,cp,4):
+                                       break
+                       else:
+                               state[i] = True
+
+               if oldstate is not None:
+                       for i,(j,k) in enumerate(zip(oldstate, state)):
+                               if j != k:
+                                       note(n_start+len(oldstate)-i, k)
+                                       if k:
+                                               print "PRESSED: %d"%i
+                                       else:
+                                               print "RELEASED: %d"%i
+               oldstate = state
+
+               cv.ShowImage("thresholded", grey2)
+               if cv.WaitKey(10)&0xfff == 27:
+                       break
+except:
+       olt.die = True
+       olt.join()
+       raise
diff --git a/examples/showtext.py b/examples/showtext.py
new file mode 100644 (file)
index 0000000..0a2a86a
--- /dev/null
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+#         OpenLase - a realtime laser graphics toolkit
+#
+# Copyright (C) 2009-2011 Hector Martin "marcan" <hector@marcansoft.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 or version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+import pylase as ol
+
+import sys
+
+if ol.init(10) < 0:
+       sys.exit(1)
+params = ol.RenderParams()
+params.render_flags = ol.RENDER_NOREORDER | ol.RENDER_GRAYSCALE
+params.on_speed = 2/120.0
+params.off_speed = 2/30.0
+params.flatness = 0.000001
+ol.setRenderParams(params)
+
+lines = sys.argv[1:]
+
+while True:
+       lc = len(lines)
+
+       font = ol.getDefaultFont()
+       yoff = (lc/2.0) * 0.3
+
+       for i,line in enumerate(lines):
+               w = ol.getStringWidth(font, 0.3, line)
+               ol.drawString(font, (-w/2,yoff-i*0.3), 0.3, ol.C_WHITE, line)
+
+       ftime = ol.renderFrame(60)
diff --git a/examples/simple.py b/examples/simple.py
new file mode 100644 (file)
index 0000000..da63a3a
--- /dev/null
@@ -0,0 +1,77 @@
+#         OpenLase - a realtime laser graphics toolkit
+#
+# Copyright (C) 2009-2011 Hector Martin "marcan" <hector@marcansoft.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 or version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+import pylase as ol
+from math import pi
+
+ol.init()
+
+time = 0
+frames = 0
+
+while True:
+       ol.loadIdentity3()
+       ol.loadIdentity()
+
+       font = ol.getDefaultFont()
+       s = "Hi!"
+       w = ol.getStringWidth(font, 0.2, s)
+       ol.drawString(font, (-w/2,0.1), 0.2, ol.C_WHITE, s)
+
+       ol.perspective(60, 1, 1, 100)
+       ol.translate3((0, 0, -3))
+
+       for i in range(2):
+               ol.scale3((0.6, 0.6, 0.6))
+               ol.rotate3Z(time * pi * 0.1)
+               ol.rotate3X(time * pi * 0.8)
+               ol.rotate3Y(time * pi * 0.73)
+
+               ol.begin(ol.LINESTRIP)
+               ol.vertex3((-1, -1, -1), ol.C_WHITE)
+               ol.vertex3(( 1, -1, -1), ol.C_WHITE)
+               ol.vertex3(( 1,  1, -1), ol.C_WHITE)
+               ol.vertex3((-1,  1, -1), ol.C_WHITE)
+               ol.vertex3((-1, -1, -1), ol.C_WHITE)
+               ol.vertex3((-1, -1,  1), ol.C_WHITE)
+               ol.end()
+
+               ol.begin(ol.LINESTRIP);
+               ol.vertex3(( 1,  1,  1), ol.C_WHITE)
+               ol.vertex3((-1,  1,  1), ol.C_WHITE)
+               ol.vertex3((-1, -1,  1), ol.C_WHITE)
+               ol.vertex3(( 1, -1,  1), ol.C_WHITE)
+               ol.vertex3(( 1,  1,  1), ol.C_WHITE)
+               ol.vertex3(( 1,  1, -1), ol.C_WHITE)
+               ol.end()
+
+               ol.begin(ol.LINESTRIP)
+               ol.vertex3(( 1, -1, -1), ol.C_WHITE)
+               ol.vertex3(( 1, -1,  1), ol.C_WHITE)
+               ol.end()
+
+               ol.begin(ol.LINESTRIP)
+               ol.vertex3((-1,  1,  1), ol.C_WHITE)
+               ol.vertex3((-1,  1, -1), ol.C_WHITE)
+               ol.end()
+
+       ftime = ol.renderFrame(60)
+       frames += 1
+       time += ftime
+       print "Frame time: %f, FPS:%f"%(ftime, frames/time)
+
+ol.shutdown()
index 3e261452c6dba3f0bde091f4bba6d3ef8ddc88fe..5a848e9f6556e75c09c2e3bb5250b8ac08c96f06 100644 (file)
@@ -25,4 +25,4 @@ target_link_libraries (openlase ${CMAKE_THREAD_LIBS_INIT} m jack)
 add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/fontdef.c
     DEPENDS ${CMAKE_SOURCE_DIR}/tools/genfont.py
     MAIN_DEPENDENCY laserfont.svg
-    COMMAND python ${CMAKE_SOURCE_DIR}/tools/genfont.py ${CMAKE_CURRENT_SOURCE_DIR}/laserfont.svg ${CMAKE_CURRENT_BINARY_DIR}/fontdef.c default_font)
+    COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/genfont.py ${CMAKE_CURRENT_SOURCE_DIR}/laserfont.svg ${CMAKE_CURRENT_BINARY_DIR}/fontdef.c default_font)
diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt
new file mode 100644 (file)
index 0000000..61dd925
--- /dev/null
@@ -0,0 +1,23 @@
+find_package(PythonLibs)
+find_program(CYTHON_EXECUTABLE cython)
+
+if(CYTHON_EXECUTABLE MATCHES "NOTFOUND" OR NOT PYTHONLIBS_FOUND)
+  message(STATUS "Will NOT build python bindings (python libs or cython missing)")
+else()
+  execute_process(COMMAND
+    ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"
+    OUTPUT_VARIABLE PYTHON_SITE_PACKAGES OUTPUT_STRIP_TRAILING_WHITESPACE)
+
+  add_custom_command(OUTPUT pylase.c
+    MAIN_DEPENDENCY pylase.pyx
+    COMMAND ${CYTHON_EXECUTABLE} -I ${CMAKE_SOURCE_DIR}/include -o pylase.c "${CMAKE_CURRENT_SOURCE_DIR}/pylase.pyx")
+    list(APPEND ADDITIONAL_MAKE_CLEAN_FILES pylase.c)
+
+  include_directories(${PYTHON_INCLUDE_PATH} ${CMAKE_SOURCE_DIR}/include)
+  add_library(pylase MODULE pylase.c)
+  set_target_properties(pylase PROPERTIES
+    PREFIX ""
+    OUTPUT_NAME "pylase"
+    LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+  target_link_libraries(pylase openlase)
+endif()
diff --git a/python/pylase.pyx b/python/pylase.pyx
new file mode 100644 (file)
index 0000000..7711ff7
--- /dev/null
@@ -0,0 +1,482 @@
+#         OpenLase - a realtime laser graphics toolkit
+#
+# Copyright (C) 2009-2011 Hector Martin "marcan" <hector@marcansoft.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 or version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+from libc.stdint cimport *
+
+cdef extern from "libol.h":
+       enum:
+               OL_LINESTRIP
+               OL_BEZIERSTRIP
+               OL_POINTS
+       enum:
+               _RENDER_GRAYSCALE "RENDER_GRAYSCALE"
+               _RENDER_NOREORDER "RENDER_NOREORDER"
+               _RENDER_NOREVERSE "RENDER_NOREVERSE"
+
+       ctypedef struct OLRenderParams:
+               int rate
+               float on_speed
+               float off_speed
+               int start_wait
+               int start_dwell
+               int curve_dwell
+               int corner_dwell
+               int end_dwell
+               int end_wait
+               float curve_angle
+               float flatness
+               float snap
+               int render_flags
+               int min_length
+               int max_framelen
+
+       ctypedef struct OLFrameInfo "OLFrameInfo":
+               int objects
+               int points
+               int resampled_points
+               int resampled_blacks
+               int padding_points
+
+       int olInit(int buffer_count, int max_points)
+
+       void olSetRenderParams(OLRenderParams *params)
+       void olGetRenderParams(OLRenderParams *params)
+
+       ctypedef void (*AudioCallbackFunc)(float *leftbuf, float *rightbuf, int samples)
+
+       void olSetAudioCallback(AudioCallbackFunc f)
+
+       void olLoadIdentity()
+       void olPushMatrix()
+       void olPopMatrix()
+
+       void olMultMatrix(float m[9])
+       void olRotate(float theta)
+       void olTranslate(float x, float y)
+       void olScale(float sx, float sy)
+
+       void olLoadIdentity3()
+       void olPushMatrix3()
+       void olPopMatrix3()
+
+       void olMultMatrix3(float m[16])
+       void olRotate3X(float theta)
+       void olRotate3Y(float theta)
+       void olRotate3Z(float theta)
+       void olTranslate3(float x, float y, float z)
+       void olScale3(float sx, float sy, float sz)
+
+       void olFrustum (float left, float right, float bot, float ttop, float near, float far)
+       void olPerspective(float fovy, float aspect, float zNear, float zFar)
+
+       void olResetColor()
+       void olMultColor(uint32_t color)
+       void olPushColor()
+       void olPopColor()
+
+       void olBegin(int prim)
+       void olVertex(float x, float y, uint32_t color)
+       void olVertex3(float x, float y, float z, uint32_t color)
+       void olEnd()
+
+       void olTransformVertex3(float *x, float *y, float *z)
+
+       ctypedef void (*ShaderFunc)(float *x, float *y, uint32_t *color)
+       ctypedef void (*Shader3Func)(float *x, float *y, float *z, uint32_t *color)
+
+       void olSetVertexPreShader(ShaderFunc f)
+       void olSetVertexShader(ShaderFunc f)
+       void olSetVertex3Shader(Shader3Func f)
+
+       void olSetPixelShader(ShaderFunc f)
+
+       void olRect(float x1, float y1, float x2, float y2, uint32_t color)
+       void olLine(float x1, float y1, float x2, float y2, uint32_t color)
+       void olDot(float x, float y, int points, uint32_t color)
+
+       float olRenderFrame(int max_fps) nogil
+
+       void olGetFrameInfo(OLFrameInfo *info)
+
+       void olShutdown()
+
+       void olSetScissor (float x0, float y0, float x1, float y1)
+
+       void olLog(char *fmt, ...)
+
+       ctypedef char* const_char_ptr "const char*"
+       ctypedef void (*LogCallbackFunc)(char *msg)
+
+       void olSetLogCallback(LogCallbackFunc f)
+
+LINESTRIP = OL_LINESTRIP
+BEZIERSTRIP = OL_BEZIERSTRIP
+POINTS = OL_POINTS
+
+RENDER_GRAYSCALE = _RENDER_GRAYSCALE
+RENDER_NOREORDER = _RENDER_NOREORDER
+RENDER_NOREVERSE = _RENDER_NOREVERSE
+
+C_RED  = 0xff0000
+C_GREEN = 0x00ff00
+C_BLUE = 0x0000ff
+C_WHITE = 0xffffff
+
+cpdef uint32_t C_GREY(uint8_t x):
+       return 0x010101 * x
+
+cdef class RenderParams:
+       cdef public int rate
+       cdef public float on_speed
+       cdef public float off_speed
+       cdef public int start_wait
+       cdef public int start_dwell
+       cdef public int curve_dwell
+       cdef public int corner_dwell
+       cdef public int end_dwell
+       cdef public int end_wait
+       cdef public float curve_angle
+       cdef public float flatness
+       cdef public float snap
+       cdef public int render_flags
+       cdef public int min_length
+       cdef public int max_framelen
+       def __init__(self):
+               self.rate = 48000
+               self.on_speed = 2/100.0
+               self.off_speed = 1/30.0
+               self.start_wait = 8
+               self.start_dwell = 3
+               self.curve_dwell = 0
+               self.corner_dwell = 6
+               self.end_dwell = 3
+               self.end_wait = 7
+               self.curve_angle = 0.866
+               self.flatness = 0.00001
+               self.snap = 0.00001
+               self.render_flags = RENDER_GRAYSCALE
+               self.min_length = 0
+               self.max_framelen = 0
+
+cpdef setRenderParams(params):
+       cdef OLRenderParams cparams
+       cparams.rate = params.rate
+       cparams.on_speed = params.on_speed
+       cparams.off_speed = params.off_speed
+       cparams.start_wait = params.start_wait
+       cparams.start_dwell = params.start_dwell
+       cparams.curve_dwell = params.curve_dwell
+       cparams.corner_dwell = params.corner_dwell
+       cparams.end_dwell = params.end_dwell
+       cparams.end_wait = params.end_wait
+       cparams.curve_angle = params.curve_angle
+       cparams.flatness = params.flatness
+       cparams.snap = params.snap
+       cparams.render_flags = params.render_flags
+       cparams.min_length = params.min_length
+       cparams.max_framelen = params.max_framelen
+       olSetRenderParams(&cparams)
+
+cpdef getRenderParams():
+       cdef OLRenderParams params
+       olGetRenderParams(&params)
+       pyparams = RenderParams()
+       pyparams.rate = params.rate
+       pyparams.on_speed = params.on_speed
+       pyparams.off_speed = params.off_speed
+       pyparams.start_wait = params.start_wait
+       pyparams.start_dwell = params.start_dwell
+       pyparams.curve_dwell = params.curve_dwell
+       pyparams.corner_dwell = params.corner_dwell
+       pyparams.end_dwell = params.end_dwell
+       pyparams.end_wait = params.end_wait
+       pyparams.curve_angle = params.curve_angle
+       pyparams.flatness = params.flatness
+       pyparams.snap = params.snap
+       pyparams.render_flags = params.render_flags
+       pyparams.min_length = params.min_length
+       pyparams.max_framelen = params.max_framelen
+       return pyparams
+
+cpdef int init(int buffer_count=4, int max_points=30000):
+       cdef int ret = olInit(buffer_count, max_points)
+       if ret < 0:
+               return ret
+       setRenderParams(RenderParams())
+       return ret
+
+cpdef loadIdentity(): olLoadIdentity()
+cpdef pushMatrix(): olPushMatrix()
+cpdef popMatrix(): olPopMatrix()
+
+cpdef multMatrix(object m):
+       if len(m) == 3:
+               m = m[0] + m[1] + m[2]
+       cdef float cm[9]
+       for i in range(9):
+               cm[i] = m[i]
+       olMultMatrix(cm)
+
+cpdef rotate(float theta): olRotate(theta)
+cpdef translate(tuple coord):
+       x, y = coord
+       olTranslate(x, y)
+cpdef scale(tuple coord):
+       x, y = coord
+       olScale(x, y)
+
+_py_audiocb = None
+cdef void _audiocb(float *l, float *r, int samples) with gil:
+       global _py_audiocb
+       if _py_audiocb is not None:
+               buf = _py_audiocb(samples)
+               for i in range(min(len(buf), samples)):
+                       l[i], r[i] = buf[i]
+               if len(buf) < samples:
+                       for i in range(len(buf), samples):
+                               l[i] = r[i] = 0
+
+cpdef setAudioCallback(object func):
+       global _py_audiocb
+       _py_audiocb = func
+       if func is not None:
+               olSetAudioCallback(_audiocb)
+       else:
+               olSetAudioCallback(NULL)
+
+cpdef loadIdentity3(): olLoadIdentity3()
+cpdef pushMatrix3(): olPushMatrix3()
+cpdef popMatrix3(): olPopMatrix3()
+
+cpdef multMatrix3(object m):
+       if len(m) == 4:
+               m = m[0] + m[1] + m[2] + m[3]
+       cdef float cm[16]
+       for i in range(16):
+               cm[i] = m[i]
+       olMultMatrix(cm)
+
+cpdef rotate3X(float theta): olRotate3X(theta)
+cpdef rotate3Y(float theta): olRotate3Y(theta)
+cpdef rotate3Z(float theta): olRotate3Z(theta)
+cpdef translate3(tuple coord):
+       x, y, z = coord
+       olTranslate3(x, y, z)
+cpdef scale3(tuple coord):
+       x, y, z = coord
+       olScale3(x, y, z)
+
+cpdef frustum(float left, float right, float bot, float top, float near, float far):
+       olFrustum(left, right, bot, top, near, far)
+cpdef perspective(float fovy, float aspect, float zNear, float zFar):
+       olPerspective(fovy, aspect, zNear, zFar)
+
+cpdef resetColor(): olResetColor()
+cpdef multColor(uint32_t color): olMultColor(color)
+cpdef pushColor(): olPushColor()
+cpdef popColor(): olPopColor()
+
+cpdef begin(int prim): olBegin(prim)
+cpdef vertex(tuple coord, uint32_t color):
+       x, y = coord
+       olVertex(x, y, color)
+cpdef vertex3(tuple coord, uint32_t color):
+       x, y, z = coord
+       olVertex3(x, y, z, color)
+cpdef end(): olEnd()
+
+cpdef tuple transformVertex3(float x, float y, float z):
+       olTransformVertex3(&x, &y, &z)
+       return x, y, z
+
+_py_vpreshader = None
+cdef void _vpreshader(float *x, float *y, uint32_t *color) with gil:
+       global _py_vpreshader
+       if _py_vpreshader is not None:
+               (x[0], y[0]), color[0] = _py_vpreshader((x[0], y[0]), color[0])
+
+_py_vshader = None
+cdef void _vshader(float *x, float *y, uint32_t *color) with gil:
+       global _py_vshader
+       if _py_vshader is not None:
+               (x[0], y[0]), color[0] = _py_vshader((x[0], y[0]), color[0])
+
+_py_v3shader = None
+cdef void _v3shader(float *x, float *y, float *z, uint32_t *color) with gil:
+       global _py_v3shader
+       if _py_v3shader is not None:
+               (x[0], y[0], z[0]), color[0] = _py_v3shader((x[0], y[0], z[0]), color[0])
+
+_py_pshader = None
+cdef void _pshader(float *x, float *y, uint32_t *color) with gil:
+       global _py_pshader
+       if _py_pshader is not None:
+               (x[0], y[0]), color[0] = _py_pshader((x[0], y[0]), color[0])
+
+cpdef setVertexPreShader(object func):
+       global _py_vpreshader
+       _py_vpreshader = func
+       if func is not None:
+               olSetVertexPreShader(_vpreshader)
+       else:
+               olSetVertexPreShader(NULL)
+
+cpdef setVertexShader(object func):
+       global _py_vshader
+       _py_vshader = func
+       if func is not None:
+               olSetVertexShader(_vshader)
+       else:
+               olSetVertexShader(NULL)
+
+cpdef setVertex3Shader(object func):
+       global _py_v3shader
+       _py_v3shader = func
+       if func is not None:
+               olSetVertex3Shader(_v3shader)
+       else:
+               olSetVertex3Shader(NULL)
+
+cpdef setPixelShader(object func):
+       global _py_pshader
+       _py_pshader = func
+       if func is not None:
+               olSetPixelShader(_pshader)
+       else:
+               olSetPixelShader(NULL)
+
+cpdef rect(tuple start, tuple end, uint32_t color):
+       x1, y1 = start
+       x2, y2 = end
+       olRect(x1, y1, x2, y2, color)
+
+cpdef line(tuple start, tuple end, uint32_t color):
+       x1, y1 = start
+       x2, y2 = end
+       olLine(x1, y1, x2, y2, color)
+
+cpdef dot(tuple coord, int points, uint32_t color):
+       x, y = coord
+       olDot(x, y, points, color)
+
+cpdef float renderFrame(int max_fps):
+       cdef float ret
+       with nogil:
+               ret = olRenderFrame(max_fps)
+       return ret
+
+cdef class FrameInfo:
+       cdef readonly int objects
+       cdef readonly int points
+       cdef readonly int resampled_points
+       cdef readonly int resampled_blocks
+       cdef readonly int padding_points
+
+cpdef getFrameInfo():
+       cdef OLFrameInfo info
+       olGetFrameInfo(&info)
+       pyinfo = FrameInfo()
+       pyinfo.objects = info.objects
+       pyinfo.points = info.points
+       pyinfo.resampled_points = info.resampled_points
+       pyinfo.resampled_blocks = info.resampled_blocks
+       pyinfo.padding_points = info.padding_points
+       return pyinfo
+
+cpdef shutdown(): olShutdown()
+
+cpdef setScissor(tuple start, tuple end):
+       x1, y1 = start
+       x2, y2 = end
+       olSetScissor(x1, y1, x2, y2)
+
+_py_logcb = None
+cdef void _logcb(const_char_ptr msg):
+       global _py_logcb
+       cdef bytes msg2 = msg
+       if _py_logcb is not None:
+               _py_logcb(msg2)
+
+cpdef setLogCallback(object func):
+       global _py_logcb
+       _py_logcb = func
+       if func is not None:
+               olSetLogCallback(_logcb)
+       else:
+               olSetLogCallback(NULL)
+
+cdef extern from "text.h":
+       ctypedef struct _Font "Font"
+
+       _Font *olGetDefaultFont()
+       float olGetCharWidth(_Font *fnt, char c)
+       float olGetStringWidth(_Font *fnt, float height, char *s)
+       float olGetCharOverlap(_Font *font, float height)
+       float olDrawChar(_Font *fnt, float x, float y, float height, uint32_t color, char c)
+       float olDrawString(_Font *fnt, float x, float y, float height, uint32_t color, char *s)
+
+cdef class Font:
+       cdef _Font *font
+
+cpdef getDefaultFont():
+       f = Font()
+       f.font = olGetDefaultFont()
+       return f
+
+cpdef float getCharWidth(object font, char c):
+       cdef Font fnt = font
+       return olGetCharWidth(fnt.font, c)
+cpdef float getStringWidth(object font, float height, char *s):
+       cdef Font fnt = font
+       return olGetStringWidth(fnt.font, height, s)
+cpdef float getCharOverlap(object font, float height):
+       cdef Font fnt = font
+       return olGetCharOverlap(fnt.font, height)
+cpdef float drawChar(object font, tuple coord, float height, uint32_t color, char c):
+       cdef Font fnt = font
+       x, y = coord
+       return olDrawChar(fnt.font, x, y, height, color, c)
+cpdef float drawString(object font, tuple coord, float height, uint32_t color, char *s):
+       cdef Font fnt = font
+       x, y = coord
+       return olDrawString(fnt.font, x, y, height, color, s)
+
+cdef extern from "ilda.h":
+       ctypedef struct _IldaFile "IldaFile"
+
+       _IldaFile *olLoadIlda(char *filename)
+       void olDrawIlda(_IldaFile *ild)
+       void olDrawIlda3D(_IldaFile *ild)
+       void olFreeIlda(_IldaFile *ild)
+
+cdef class IldaFile:
+       cdef _IldaFile *ilda
+       def __del__(self):
+               olFreeIlda(self.ilda)
+
+cpdef loadIlda(char *file):
+       f = IldaFile()
+       f.ilda = olLoadIlda(file)
+       return f
+
+cpdef drawIlda(object ilda):
+       cdef IldaFile f = ilda
+       olDrawIlda(f.ilda)
+
+cpdef drawIlda3D(object ilda):
+       cdef IldaFile f = ilda
+       olDrawIlda3D(f.ilda)