#!/usr/bin/python # $Header: /jch/play/cvsroot/twist/twist1.py,v 1.13 2003/05/30 03:20:04 jch Exp $ # # DO SOME ART!!! jch - April 2003 - twist.py # originally: /python1.5/site-packages/OpenGL/Demo - with python 1.5.6? # currently: Python 2.3b1 (#1, Apr 28 2003, 20:29:14) # OpenGL.__version__ = '2.0.0.44' # # This is a total abortion of an application, but, it is still kind of cool. # Working in progress started April 2003. The next version anitwist.py # has a mass spring engine built in to drive the control points... but... # Anyway, back to the matter at hand: # # twist1.py currently has 6 buttons: save, load, snap, points, regen, reset # save - saves the current NURB curve state. NO VIEWING INFO IS SAVED. # load - load the saved NURB curve # snap - saves a .jpg # points - toggles between showing the control points and the extrustions. # regen - regenerate the extrusions from the control points. # reset - does it do anything??? # # TERRIBLE HACK UI for moving control points. # Mouse over the control point. When it goes to being a cross, # then hold down CTRL and click. It is now stuck to the cursor until # you control click again. # # I am currently running a mish mosh of versions - Python 2.3, PyOpenGL 2.0X, # and I was not able to get wx to build, so, I stuck with tkIntr. Anything, # as long as I don't have to think in order to know what to do. # # Humbly, YON - Jan C. Hardenbergh http://www.jch.com/jch # # This is statement is required by the build system to query build info if __name__ == '__build__': raise Exception import string __version__ = ' One ' __date__ = '$Date: 2003/05/30 03:20:04 $' __author__ = '$Author: jch $ aka YON - Jan C. Hardenbergh' #'YON - jch@jch.com' # Tarn Weisner Burton ' gApp = 0 # ======================================================================== # NURBS Playground # ======================================================================== # ported to Python 26-APR-2003 #include #include # #// Copyright YON - Jan C. Hardenbergh. Permission to copy is #// granted as long as this copyright and this URL are maintained: #// http://www.jch.com/NURBS/ # #// Primary reference Les Peigl - On NURBS: A Survey published #// in IEEE Computer Graphics and Applications (CG&A) January 1991. # #// http://www.cs.wpi.edu/~matt/courses/cs563/talks/nurbs.html # #// this is intended to explore NURBS - how they are evaluated. #// It is explicitly unoptimized. import Numeric from Point import Point def B(i,k,t,knots): ret = 0 if k>0: n1 = (t-knots[i])*B(i,k-1,t,knots) d1 = knots[i+k] - knots[i] n2 = (knots[i+k+1] - t) * B(i+1,k-1,t,knots) d2 = knots[i+k+1] - knots[i+1] if d1 > 0.0001 or d1 < -0.0001: a = n1 / d1 else: a = 0 if d2 > 0.0001 or d2 < -0.0001: b = n2 / d2 else: b = 0 ret = a + b #print "B i = %d, k = %d, ret = %g, a = %g, b = %g\n"%(i,k,ret,a,b) else: if knots[i] <= t and t <= knots[i+1]: ret = 1 else: ret = 0 return ret def C(t, order, points, weights, knots): c = Point([0,0,0]) rational = 0 i = 0 while i < len(points): b = B(i, order, t, knots) p = points[i] * (b * weights[i]) c = c + p rational = rational + b*weights[i] i = i + 1 return c * (1.0/rational) def handleMotion(event): if gApp.getDragging(): gApp.drag(event.x,event.y) else: found = gApp.testControlPoint(event.x,event.y) gApp.setHotControlPoint(found) if found > 0: gApp.gl.configure(cursor="crosshair") else: gApp.gl.configure(cursor="arrow") def handleCB1(event): if gApp.getDragging(): gApp.setDragging(0) else: gApp.setDragging(gApp.getHotControlPoint()) def ignore(event): pass def close(x,y,px,py): tol = 4 tx = x - px if tx < -tol or tx > tol: return 0 ty = y - py if ty < -tol or ty > tol: return 0 return 1 # ======================================================================== # Based on some code from the GL Extrusion # ======================================================================== from OpenGL.GL import * from OpenGL.Tk import * from OpenGL.GLE import * from Tkinter import * import sys, os, time, string import LinearAlgebra from tkFileDialog import * # ======================================================================== # ======================================================================== # ======================================================================== class MyApp(Frame): def initLights(self): glMaterialfv(GL_FRONT, GL_AMBIENT, [0.2, 0.2, 0.2, 1.0]) glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]) glMaterialfv(GL_FRONT, GL_SPECULAR, [1.0, 0.0, 1.0, 1.0]) glMaterialfv(GL_FRONT, GL_SHININESS, 10.0) glLightfv(GL_LIGHT0, GL_AMBIENT, [0.2, 0.1, 0.1, 1.0]) glLightfv(GL_LIGHT0, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]) glLightfv(GL_LIGHT0, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0]) glLightfv(GL_LIGHT0, GL_POSITION, [1.0, 1.0, 1.0, 0.0]); glLightfv(GL_LIGHT1, GL_AMBIENT, [0.1, 0, 0.2, 1.0]) glLightfv(GL_LIGHT1, GL_DIFFUSE, [0.7, 0.7, 0.7, 1.0]) glLightfv(GL_LIGHT1, GL_SPECULAR, [1.0, 1.0, 1.0, 1.0]) glLightfv(GL_LIGHT1, GL_POSITION, [-1.0, 0, 0, 0.0]); # glLightModelfv(GL_LIGHT_MODEL_AMBIENT, [0.2, 0.2, 0.2, 1.0]) glEnable(GL_LIGHTING) glEnable(GL_LIGHT0) glEnable(GL_LIGHT1) glCullFace(GL_BACK) glEnable(GL_CULL_FACE) def evaluate(self,maxt,steps): order = 2 self.path = [] self.twists = [] self.colors = [] for i in range(steps): t = i*(maxt*0.999/steps) c = C(t, order, self.controlPoints, self.weights, self.knots) # c.Print() self.path = self.path + [[c[0],c[1],c[2]]] self.twists = self.twists + [-i*30] self.colors = self.twists + [[1,1,1]] def initGeometry(self): self.contour = [[0,0]] self.normals = [[1,0]] count = 24 angle = 0.0 step = 0 while step < count: angle = angle + 6.28/(count) self.contour.append([math.cos(angle)-1,math.sin(angle)]) self.normals.append([math.cos(angle),math.sin(angle)]) step += 1 step = 1 while step < count: angle = angle - 6.28/(count) self.contour.append([1-math.cos(angle),math.sin(angle)]) self.normals.append([-math.cos(angle),math.sin(angle)]) step += 1 # main ************************************************** self.path = [[0,0,0]] rad3 = 1.732 self.drawControlPoints = 0 # def NURBSEval(): zScale = -8 unscaledPoints = [Point([0,0,0*zScale]), Point([0.4,0.1,0.1*zScale]), Point([.5,.5,0.2*zScale]), Point([0,0.8,0.3*zScale]), Point([-.5,.5,0.4*zScale]), Point([-0.7,0.2,0.5*zScale]), Point([0,0,0.6*zScale])] bigScale = 20 self.controlPoints = [] for cp in unscaledPoints: self.controlPoints = self.controlPoints + [cp*bigScale] self.knots = [0,0,0,1,2,3,4,5,5,5] self.maxtparam = 5 self.strokes = 10 self.weights = [1,1,1,1,1,1,1] self.evaluate(self.maxtparam,self.maxtparam*self.strokes) self.quad = gluNewQuadric() gluQuadricOrientation (self.quad,GLU_OUTSIDE) def setupView(self): width = self.gl.winfo_width() height = self.gl.winfo_height() aspect = 2*float(width)/float(height) viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX) zNear = -100 zFar = 0 for i in range(len(self.controlPoints)): cp = self.controlPoints[i] vertex = Numeric.array([cp[0], cp[1], cp[2], 1.0]) npcPoint = Numeric.matrixmultiply(vertex,viewMatrix) # print npcPoint if npcPoint[2] > zNear: zNear = npcPoint[2] if npcPoint[2] - 5 < zFar: zFar = npcPoint[2] + 5 zNear = zNear zFar = zFar if zNear > -1: zNear = -1 # print "near %g, far %g"%(zNear, zFar) glMatrixMode(GL_PROJECTION) gluPerspective(90, aspect, zNear, zFar ) glMatrixMode(GL_MODELVIEW) def redraw(self, o): self.setupView() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) gleSetJoinStyle(TUBE_CONTOUR_CLOSED | TUBE_NORM_EDGE | TUBE_JN_CAP | TUBE_JN_ROUND ) glMaterialfv(GL_FRONT, GL_AMBIENT, [0.3, 0.4, 0.5, 1.0]) glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]) if self.drawControlPoints == 0: gleTwistExtrusion(self.contour, self.normals, [0,0,1], self.path, self.colors, self.twists) else: glMaterialfv(GL_FRONT, GL_AMBIENT, [0.6, 0.5, 0.4, 1.0]) glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.8, 0.8, 0.8, 1.0]) contour = [[-1,-1],[1,-1],[0,1]] gleTwistExtrusion(contour, contour, [0,0,1], self.path, self.colors, self.twists) glMatrixMode(GL_MODELVIEW) for i in range(len(self.controlPoints)): glPushMatrix() cp = self.controlPoints[i] # cp.Print() if self.dragging - 1 == i: glMaterialfv(GL_FRONT, GL_AMBIENT, [0.5, 1, 0.8, 1.0]) glMaterialfv(GL_FRONT, GL_DIFFUSE, [0.5, 1, 0.8, 1.0]) else: glMaterialfv(GL_FRONT, GL_AMBIENT, [1, 0.5, 0.8, 1.0]) glMaterialfv(GL_FRONT, GL_DIFFUSE, [1, 0.5, 0.8, 1.0]) glTranslated(cp[0], cp[1], cp[2]) gluSphere (self.quad, 0.7, 8, 8) glPopMatrix() def save(self): newfile = asksaveasfilename(filetypes=[("twist paramters", "*.tws")]) if (newfile==None or newfile==""): return if os.path.splitext(newfile)[1] != '.tws': newfile = newfile + '.tws' paramfile = open(newfile, 'w') paramfile.write("# twisty parameters 0.1 "+time.strftime("%a, %d %b %Y %H:%M:%S GMT\n", time.gmtime(time.time()))) paramfile.write("\n") for cp in self.controlPoints: paramfile.write("cp %8.3f %8.3f %8.3f\n"%(cp[0], cp[1], cp[2])) paramfile.write("\n") #paramfile.write(self.knots.tostring()) kstring = "" for knot in self.knots: kstring = kstring + " %d"%(knot) kstring = kstring + " \n" paramfile.write(kstring) paramfile.write("\n") paramfile.close() print "Wrote parameter file "+newfile; def load(self): # d = FileDialog(master) # file = d.go() myPath = askopenfilename(filetypes=[("twist paramters", "*.tws")]) if (myPath==None or myPath==""): return lines = open(myPath, 'r').readlines() state = 0 knots = 0 cPoints = 0 weights = 0 for l in lines: tokens = string.split(l) first_word = tokens[0] if first_word[0] == '#': pass elif first_word == '': state = 1 elif first_word == 'cp': if state == 1 and len(tokens) == 4: cp = Point([string.atof(tokens[1]), string.atof(tokens[2]), string.atof(tokens[3])]) if cPoints == 0: cPoints = [cp] weights = [1] else: cPoints = cPoints + [cp] weights = weights + [1] else: print "bad control point " print tokens elif first_word == '' and state == 1: state = 2 elif first_word == '': if state == 2 and tokens[len(tokens)-1] == '': knots = [string.atoi(tokens[1])] ki = 2 while ki < len(tokens) - 1: knots = knots + [string.atoi(tokens[ki])] ki += 1 state = 3 else: print 'Bad knotty' elif first_word == '' and state == 3: state = 4 else: print tokens print 'Junk found' if state == 4: self.controlPoints = cPoints self.weights = weights self.knots = knots self.maxtparam = knots[-1] self.regen() else: print "failed to parse %s last state %d"%(myPath, state) def snap(self): self.SaveTo("snap.jpg") def SaveTo( self, filename, format="JPEG" ): """Snap current buffer to filename in format""" import Image # get PIL's functionality... width = self.gl.winfo_width() height = self.gl.winfo_height() glPixelStorei(GL_PACK_ALIGNMENT, 1) data = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE) image = Image.fromstring( "RGB", (width, height), data ) image = image.transpose( Image.FLIP_TOP_BOTTOM) # print dir(image) image.save( filename, format, quality=100 ) print 'Snapshot image in %s'% (os.path.abspath( filename)) return image def resetView(self): self.gl.reset() self.gl.tkRedraw() def regen(self): self.evaluate(self.maxtparam,self.maxtparam*self.strokes) self.gl.tkRedraw() def testControlPoint(self, x, y): # vpMatrix = Numeric.array([[scale,0,0,width/2.0], # [0,scale,0,height/2.0],[0,0,1,0],[0,0,0,1]]) width = self.gl.winfo_width() height = self.gl.winfo_height() viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX) projMatrix = glGetFloatv(GL_PROJECTION_MATRIX) # concat = Numeric.matrixmultiply(projMatrix,viewMatrix) concat = Numeric.matrixmultiply(viewMatrix,projMatrix) # ivy = LinearAlgebra.inverse(concat) for i in range(len(self.controlPoints)): cp = self.controlPoints[i] vertex = Numeric.array([cp[0], cp[1], cp[2], 1.0]) # print vertex npcPoint = Numeric.matrixmultiply(vertex,concat) if (npcPoint[3] < 0): continue px = npcPoint[0]/npcPoint[3]*width/2.0 + width/2.0 py = height/2 - npcPoint[1]/npcPoint[3]*height/2.0 # print px, py # print "got %d %d %d - t %d %d"%(i,x,y,px,py) if close(x,y,px,py): return i+1 # print "none %d %d"%(x,y) return 0 def setDragging(self, index): self.dragging = index if index == 0: self.lastY = -1 self.gl.tkRedraw() def getDragging(self): return self.dragging def setHotControlPoint(self, index): self.hotControlPoint = index def getHotControlPoint(self): return self.hotControlPoint def drag(self, x, y): if self.lastY == -1: self.lastX = x self.lastY = y else: tx = x - self.lastX ty = y - self.lastY ############################################## width = self.gl.winfo_width() height = self.gl.winfo_height() viewMatrix = glGetFloatv(GL_MODELVIEW_MATRIX) projMatrix = glGetFloatv(GL_PROJECTION_MATRIX) concat = Numeric.matrixmultiply(viewMatrix,projMatrix) index = self.getHotControlPoint() - 1 cp = self.controlPoints[index] vertex = Numeric.array([cp[0], cp[1], cp[2], 1.0]) npcPoint = Numeric.matrixmultiply(vertex,concat) npcZ = npcPoint[2]/npcPoint[3] npcX = npcPoint[0]/npcPoint[3] + 2.0*tx/float(width) npcY = npcPoint[1]/npcPoint[3] - 2.0*ty/float(height) vertex = Numeric.array([npcX, npcY, npcZ, 1.0]) ivy = LinearAlgebra.inverse(concat) newcp = Numeric.matrixmultiply(vertex,ivy) cpz = newcp[2]/newcp[3] cpx = newcp[0]/newcp[3] cpy = newcp[1]/newcp[3] # print "new cp %g %g %g"%(cpx, cpy, cpz) self.controlPoints[index].Assign(cpx,cpy,cpz) # self.controlPoints[index].Print() ############################################## self.lastX = x self.lastY = y self.gl.tkRedraw() def togp(self): if self.drawControlPoints == 0: self.drawControlPoints = 1 self.gl.bind("",handleMotion) self.gl.configure(cursor="hand1") else: self.drawControlPoints = 0 self.gl.bind("",ignore) self.gl.configure(cursor="arrow") self.gl.tkRedraw() def __init__(self): global gApp import OpenGL print OpenGL.__version__ self.f = Frame() self.f.pack() self.gl = Opengl(width = 400, height = 400, double = 1, depth = 1) self.gl.redraw = self.redraw self.gl.autospin_allowed = 1 self.gl.pack(side = TOP, expand = YES, fill = BOTH) self.gl.set_background(255,205,245) #self.gl.bind("",handleMotion) # self.gl.bind("", ignore) # self.gl.help() self.hotControlPoint = 0 self.dragging = 0 self.initLights() self.initGeometry() self.sv = Button(self.f, text="Save", command=self.save) self.sv.pack(side='left') self.ld = Button(self.f, text="Load", command=self.load) self.ld.pack(side='left') self.b = Button(self.f, text="Snap", command=self.snap) self.b.pack(side='left') self.cp = Button(self.f, text = 'Points', command = self.togp) self.cp.pack(side = 'left') self.reg = Button(self.f, text = 'Regen', command = self.regen) self.reg.pack(side = 'left') self.reset = Button(self.f, text = 'Reset',command = self.resetView) self.reset.pack(side = 'left') # print self.f.config() # print "next" # print dir(self.f) self.gl.bind("",handleCB1) self.lastX = 0 self.lastY = -1 print "gApp %d"%(gApp) gApp = self self.gl.mainloop() app = MyApp()