def get_ray(self, x, y): """ Generate a ray beginning at the near plane, in the direction that the x, y coordinates are facing Consumes: x, y coordinates of mouse on screen Return: start, direction of the ray """ self.init_view() glMatrixMode(GL_MODELVIEW) glLoadIdentity() # get two points on the line. start = numpy.array(gluUnProject(x, y, 0.001)) end = numpy.array(gluUnProject(x, y, 0.999)) # convert those points into a ray direction = end - start direction = direction / norm(direction) return (start, direction)
def pickFloor(x, y): viewp = (0, 0) + screensize mat = glGetDoublev(GL_PROJECTION_MATRIX) p1 = gluUnProject(x, viewp[3] - y, 0.0, scene_matrix, mat, viewp) p2 = gluUnProject(x, viewp[3] - y, 1.0, scene_matrix, mat, viewp) ln = -1 * p1[2] / (p2[2] - p1[2]) return [p1[0] + (p2[0] - p1[0]) * ln, p1[1] + (p2[1] - p1[1]) * ln]
def plane_intersection(self, x, y): # Unproject coordinates into object space nx, ny, nz = gluUnProject(x, y, 0.0) fx, fy, fz = gluUnProject(x, y, 1.0) # Calculate the ray near = Point3(nx, ny, nz) far = Point3(fx, fy, fz) ray = LineSegment3(near, far) # Define our planes # XXX origin assumes planes are at zero offsets, should really # XXX respect any grid plane offset here origin = self.voxels.voxel_to_world(0, 0, self.voxels.depth) planes = (Plane(Vector3(1, 0, 0), origin[0]), Plane(Vector3(0, 1, 0), origin[1]), Plane(Vector3(0, 0, 1), origin[2] + 0.001)) intersection = None, None, None distance = sys.maxint for plane in planes: # Get intersection point intersect = plane.intersect(ray) if intersect: # Adjust to voxel space coordinates x, y, z = self.voxels.world_to_voxel(intersect.x, intersect.y, intersect.z) x = int(x) y = int(y) z = int(z) # Ignore out of bounds insections if not self.voxels.is_valid_bounds(x, y, z): continue length = near.distance(Point3(intersect.x, intersect.y, intersect.z)) if length < distance: intersection = int(x), int(y), int(z) distance = length return intersection
def _Screen2Real(self, x, y, expZ=0.0): ''' Return the x and y in real coordination if the input is the x and y in screen coordination http://nehe.gamedev.net/article/using_gluunproject/16013/ Inputs: - x, y: coordinates get from mouse in screen coordination - expZ: expected Z of the real coordinates Outputs: coordinates in the real world ''' if not self.init: return 0, 0, 0 projection = glGetDoublev(GL_PROJECTION_MATRIX) modelview = glGetDoublev(GL_MODELVIEW_MATRIX) viewport = glGetIntegerv(GL_VIEWPORT) winX = float(x) winY = float(viewport[3]) - float(y) # winZ = glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT) try: posXF, posYF, posZF = gluUnProject(winX, winY, 1, modelview, projection, viewport) posXN, posYN, posZN = gluUnProject(winX, winY, -1, modelview, projection, viewport) posZ = expZ posX = (posZ - posZN) / (posZF - posZN) * (posXF - posXN) + posXN posY = (posZ - posZN) / (posZF - posZN) * (posYF - posYN) + posYN # posX, posY, posZ = gluUnProject(winX, winY, winZ, modelview, projection, viewport) except: return 0, 0, 0 return posX, posY, posZ
def get_ray(self, x, y): self.init_view() glMatrixMode(GL_MODELVIEW) glLoadIdentity() start = numpy.array(gluUnProject(x, y, 0.001)) end = numpy.array(gluUnProject(x, y, 0.999)) direction = end - start direction = direction / norm(direction) return (start, direction)
def view_axis(self): # Shoot a ray into the scene x1, y1, z1 = gluUnProject(self.width() // 2, self.height() // 2, 0.0) x2, y2, z2 = gluUnProject(self.width() // 2, self.height() // 2, 1.0) dx = abs(x2 - x1) dy = abs(y2 - y1) dz = abs(z2 - z1) # The largest deviation is the axis we're looking down if dz >= dx and dz >= dy: return (self.X_AXIS, self.Y_AXIS) elif dy >= dx and dy >= dz: return (self.X_AXIS, self.Z_AXIS) return (self.Z_AXIS, self.Y_AXIS)
def OnMouseLeftUp(self, evt): t_left, t_top, z = gluUnProject(self.startx, self.size[1] - self.starty, 0) right, bottom, z = gluUnProject(self.x, self.size[1] - self.y, 0) left = min(t_left, right) right = max(t_left, right) top = min(t_top, bottom) bottom = max(t_top, bottom) self.zoom.append((left, right, top, bottom)) self.x = self.y = self.startx = self.starty = None self.ReleaseMouse() self.Refresh(False)
def leftDown(self, event): """ Compute the rubber band window starting point, which lies on the near clipping plane, projecting into the same point that current cursor points at on the screen plane. """ self.pWxy = (event.pos().x(), self.glpane.height - event.pos().y()) p1 = A(gluUnProject(self.pWxy[0], self.pWxy[1], 0.005)) self.pStart = p1 self.pPrev = p1 self.firstDraw = True self.command.glStatesChanged = True # this warns our exit code to undo the following OpenGL state changes: self.glpane.redrawGL = False glDisable(GL_DEPTH_TEST) glDisable(GL_LIGHTING) rbwcolor = self.command.rbwcolor glColor3d(rbwcolor[0], rbwcolor[1], rbwcolor[2]) glEnable(GL_COLOR_LOGIC_OP) glLogicOp(GL_XOR) return
def leftDrag(self, event): """ Compute the changing rubber band window ending point. Erase the previous window, draw the new window. """ # bugs 1190, 1818 wware 4/05/2006 - sometimes Qt neglects to call leftDown # before this if not hasattr(self, "pWxy") or not hasattr(self, "firstDraw"): return cWxy = (event.pos().x(), self.glpane.height - event.pos().y()) rbwcolor = self.command.rbwcolor if not self.firstDraw: #Erase the previous rubber window drawrectangle(self.pStart, self.pPrev, self.glpane.up, self.glpane.right, rbwcolor) self.firstDraw = False self.pPrev = A(gluUnProject(cWxy[0], cWxy[1], 0.005)) # draw the new rubber band drawrectangle(self.pStart, self.pPrev, self.glpane.up, self.glpane.right, rbwcolor) glFlush() self.glpane.swapBuffers() # Update display return
def unproject_mouse_on_scene(self,pos): start_x, start_y, start_z = gluUnProject(pos.x(), pos.y(), 1, model=self._modelview_matrix, proj=glGetDoublev(GL_PROJECTION_MATRIX)) end_x, end_y, end_z = gluUnProject(pos.x(), pos.y(), 0, model=self._modelview_matrix, proj=glGetDoublev(GL_PROJECTION_MATRIX)) diff_x = end_x - start_x diff_y = end_y - start_y diff_z = end_z - start_z t = (0 - start_z) / diff_z x = start_x + (diff_x * t) y = start_y + (diff_y * t) return x, y,0
def leftDrag(self, event): """ Compute the changing rubber band window ending point. Erase the previous window, draw the new window. """ # bugs 1190, 1818 wware 4/05/2006 - sometimes Qt neglects to call leftDown # before this if not hasattr(self, "pWxy") or not hasattr(self, "firstDraw"): return cWxy = (event.pos().x(), self.glpane.height - event.pos().y()) rbwcolor = self.command.rbwcolor if not self.firstDraw: #Erase the previous rubberband window drawrectangle(self.pStart, self.pPrev, self.glpane.up, self.glpane.right, rbwcolor) self.firstDraw = False self.pPrev = A(gluUnProject(cWxy[0], cWxy[1], 0.005)) # draw the new rubberband window drawrectangle(self.pStart, self.pPrev, self.glpane.up, self.glpane.right, rbwcolor) glFlush() self.glpane.swapBuffers() # Update display # Based on a suggestion in bug 2961, I added this second call to # swapBuffers(). It definitely helps, but the rectangle disappears # once the zoom cursor stops moving. I suspect this is due to # a gl_update() elsewhere. I'll ask Bruce about his thoughts on # this. --Mark 2008-12-22. self.glpane.swapBuffers() return
def window_to_world(self, x, y, z = None): # Find depth y = self._height - y if z is None: z = glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0] fx, fy, fz = gluUnProject(x, y, z) return fx, fy, fz, z
def window_to_world(self, x, y, z=None): # Find depth y = self._height - y if z is None: z = glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0][0] fx, fy, fz = gluUnProject(x, y, z) return fx, fy, fz, z
def mymousepoints(glpane, x, y): #bruce 071017 moved this here from testdraw.py ### TODO: rename, docstring # modified from GLPane.mousepoints; x and y are window coords (except y is 0 at bottom, positive as you go up [guess 070124]) self = glpane just_beyond = 0.0 p1 = A(gluUnProject(x, y, just_beyond)) p2 = A(gluUnProject(x, y, 1.0)) los = self.lineOfSight # isn't this just norm(p2 - p1)?? Probably not, if we're in perspective mode! [bruce Q 061206] # note: this might be in abs coords (not sure!) even though p1 and p2 would be in local coords. # I need to review that in GLPane.__getattr__. ###k k = dot(los, -self.pov - p1) / dot(los, p2 - p1) p2 = p1 + k*(p2-p1) return (p1, p2)
def glLibUnProject(mouse_pos): viewport = glGetIntegerv(GL_VIEWPORT) modelview = glGetDoublev(GL_MODELVIEW_MATRIX) projection = glGetDoublev(GL_PROJECTION_MATRIX) winX = mouse_pos[0] winY = float(viewport[3])-mouse_pos[1] winZ = glReadPixels(winX,winY,1,1,GL_DEPTH_COMPONENT,GL_FLOAT) cursor_pos = gluUnProject(winX,winY,winZ,modelview,projection,viewport) return cursor_pos
def plane_intersection(self, x, y): # Unproject coordinates into object space nx,ny,nz = gluUnProject(x, y, 0.0) fx,fy,fz = gluUnProject(x, y, 1.0) # Calculate the ray near = Point3(nx, ny, nz) far = Point3(fx, fy, fz) ray = LineSegment3(near, far) # Retrieve the planes defined in the VoxelGrid object and iterate through them planes = [] for grid_plane in self._grids.get_grid_planes_list(): # check if the current plane is colliding. If not, then move on to the next if( not grid_plane.collision ): continue normal_x = 1 if grid_plane.plane == GridPlanes.X else 0 normal_y = 1 if grid_plane.plane == GridPlanes.Y else 0 normal_z = 1 if grid_plane.plane == GridPlanes.Z else 0 origins = { GridPlanes.X: lambda: self.voxels.voxel_to_world(grid_plane.offset-0, 0, 0)[0], GridPlanes.Y: lambda: self.voxels.voxel_to_world(0, grid_plane.offset-0, 0)[1], GridPlanes.Z: lambda: self.voxels.voxel_to_world(0, 0, grid_plane.offset-1)[2] } plane = Plane( Vector3( normal_x, normal_y, normal_z), origins[grid_plane.plane]() ) planes.append( plane ) intersection = None, None, None distance = sys.maxint for plane in planes: # Get intersection point intersect = plane.intersect(ray) if intersect: # Adjust to voxel space coordinates x, y, z = self.voxels.world_to_voxel(intersect.x, intersect.y, intersect.z) # Ignore out of bounds insections if not self.voxels.is_valid_bounds(x, y, z): continue length = near.distance(Point3(intersect.x, intersect.y, intersect.z)) if length < distance: intersection = int(x), int(y), int(round(z)) distance = length return intersection
def _drawtext(self, text, color): # use atom positions to compute center, where text should go if self.picked: # move the text to the lower left corner, and make it big pos = A(gluUnProject(5, 5, 0)) drawtext(text, color, pos, 3 * self.font_size, self.assy.o) else: pos1 = self.atoms[0].posn() pos2 = self.atoms[-1].posn() pos = (pos1 + pos2) / 2 drawtext(text, color, pos, self.font_size, self.assy.o)
def send_mouseclick_to_caller(self, cursor_x, cursor_y, button='right'): client = zmq.Context.instance().socket(zmq.PUSH) client.connect('tcp://127.0.0.1:%d' % (self.mouseclick_port)) cameras = self.on_draw(want_cameras=True) window_height = glutGet(GLUT_WINDOW_HEIGHT) depth_value = glReadPixels(cursor_x, window_height - cursor_y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT) pyobj = { 'event_type': 'mouse_click_%sbutton' % button, 'u': None, 'v': None, 'x': None, 'y': None, 'z': None, 'subwindow_row': None, 'subwindow_col': None } for subwin_row, camera_list in enumerate(cameras): for subwin_col, camera in enumerate(camera_list): # test for out-of-bounds if cursor_x < camera['viewport'][0]: continue if cursor_x > (camera['viewport'][0] + camera['viewport'][2]): continue if window_height - cursor_y < camera['viewport'][1]: continue if window_height - cursor_y > (camera['viewport'][1] + camera['viewport'][3]): continue xx, yy, zz = gluUnProject(cursor_x, window_height - cursor_y, depth_value, camera['modelview_matrix'], camera['projection_matrix'], camera['viewport']) pyobj = { 'event_type': 'mouse_click_%sbutton' % button, 'u': cursor_x - camera['viewport'][0], 'v': window_height - cursor_y - camera['viewport'][1], 'x': xx, 'y': yy, 'z': zz, 'which_subwindow': (subwin_row, subwin_col) } client.send_pyobj(pyobj) del self.mouseclick_port
def drawCenteredText(self, text, origin): """ Draws ruler text centered, so text center == origin. """ # added by piotr 080326 if not text: return fm = self.rulerFontMetrics # get the text dimensions in world coordinates x0, y0, z0 = gluUnProject(0, 0, 0) x1, y1, z1 = gluUnProject(fm.width(text), fm.ascent(), 0) # compute a new origin relative to the old one new_origin = origin - 0.5 * V(x1 - x0, y1 - y0, z1 - z0) # render the text self.drawText(text, new_origin) return
def drawCenteredText(self, text, origin): """ Draws ruler text centered, so text center == origin. """ # added by piotr 080326 if not text: return fm = self.rulerFontMetrics # get the text dimensions in world coordinates x0, y0, z0 = gluUnProject(0,0,0) x1, y1, z1 = gluUnProject(fm.width(text),fm.ascent(),0) # compute a new origin relative to the old one new_origin = origin - 0.5 * V(x1-x0, y1-y0, z1-z0) # render the text self.drawText( text, new_origin) return
def handle_press( self, x, y ): """ """ # opengl calculates coords from lower left width, height = self.size y = height - y print "button pressed at %d, %d" % (x, y) near = Vertex( *gluUnProject( x, y, 0.0 ) ) far = Vertex( *gluUnProject( x, y, 1.0 ) ) selection = Edge( near, far ) print "selection edge from %s to %s" % ( str(near), str(far) ) #self.last_click = near, far # find selected print "processing selection...", self.select( zone=selection ) print "done!" self.redraw()
def glLibUnProject(mouse_pos): viewport = glGetIntegerv(GL_VIEWPORT) modelview = glGetDoublev(GL_MODELVIEW_MATRIX) projection = glGetDoublev(GL_PROJECTION_MATRIX) winX = mouse_pos[0] winY = float(viewport[3]) - mouse_pos[1] winZ = glReadPixels(winX, winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT) if type(winZ) != float: # On windows glReadPixels returns a weird array winZ = winZ[0][0] cursor_pos = gluUnProject(winX, winY, winZ, modelview, projection, viewport) return cursor_pos
def _set_raster_pos(x, y): # staticmethod """ """ # If x or y is exactly 0, then numerical roundoff errors can make the raster position invalid. # Using 0.1 instead apparently fixes it, and causes no noticable image quality effect. # (Presumably they get rounded to integer window positions after the view volume clipping, # though some effects I saw implied that they don't get rounded, so maybe 0.1 is close enough to 0.0.) # This only matters when GLPane size is 100x100 or less, # or when drawing this in lower left corner for debugging, # so we don't have to worry about whether it's perfect. # (The known perfect fix is to initialize both matrices, but we don't want to bother, # or to worry about exceeding the projection matrix stack depth.) x1 = max(x, 0.1) y1 = max(y, 0.1) depth = 0.1 # this should not matter, as long as it's within the viewing volume x1, y1, z1 = gluUnProject(x1, y1, depth) glRasterPos3f( x1, y1, z1 ) # z1 does matter (when in perspective view), since this is a 3d position # Note: using glWindowPos would be simpler and better, but it's not defined # by the PyOpenGL I'm using. [bruce iMac G5 070626] ### UPDATE [bruce 081203]: glWindowPos2i *is* defined, at least on my newer Mac. # There are lots of variants, all suffix-typed with [23][dfis]{,v}. # I need to check whether it's defined on the older Macs too, then use it here. ####FIX if not glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID): # This was happening when we used x, y = exact 0, # and was causing the image to not get drawn sometimes (when mousewheel zoom was used). # It can still happen for extreme values of mousewheel zoom (close to the ones # which cause OpenGL exceptions), mostly only when pos = (0, 0) but not entirely. # Sometimes the actual drawing positions can get messed up then, too. # This doesn't matter much, but indicates that re-initing the matrices would be # a better solution if we could be sure the projection stack depth was sufficient # (or if we reset the standard projection when done, rather than using push/pop). print "bug: not glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID); pos =", x, y # if this happens, the subsequent effect is that glDrawPixels draws nothing else: # verify correct raster position px, py, pz, pw = glGetFloatv(GL_CURRENT_RASTER_POSITION) if not (0 <= px - x < 0.4) and (0 <= py - y < 0.4): # change to -0.4 < px - x ?? print "LIKELY BUG: glGetFloatv(GL_CURRENT_RASTER_POSITION) = %s" % [ px, py, pz, pw ] # seems to be in window coord space, but with float values, # roughly [0.1, 0.1, 0.1, 1.0] but depends on viewpoint, error about 10**-5 pass return
def plane_intersection(self, x, y): # Unproject coordinates into object space nx, ny, nz = gluUnProject(x, y, 0.0) fx, fy, fz = gluUnProject(x, y, 1.0) # Calculate the ray near = Point3(nx, ny, nz) far = Point3(fx, fy, fz) ray = LineSegment3(near, far) # Define our planes # XXX origin assumes planes are at zero offsets, should really # XXX respect any grid plane offset here origin = self.voxels.voxel_to_world(0, 0, self.voxels.depth) planes = (Plane(Vector3(1, 0, 0), origin[0]), Plane(Vector3(0, 1, 0), origin[1]), Plane(Vector3(0, 0, 1), origin[2] + 0.001)) intersection = None, None, None distance = sys.maxint for plane in planes: # Get intersection point intersect = plane.intersect(ray) if intersect: # Adjust to voxel space coordinates x, y, z = self.voxels.world_to_voxel(intersect.x, intersect.y, intersect.z) x = int(x) y = int(y) z = int(z) # Ignore out of bounds insections if not self.voxels.is_valid_bounds(x, y, z): continue length = near.distance( Point3(intersect.x, intersect.y, intersect.z)) if length < distance: intersection = int(x), int(y), int(z) distance = length return intersection
def _set_raster_pos(x, y): # staticmethod """ """ # If x or y is exactly 0, then numerical roundoff errors can make the raster position invalid. # Using 0.1 instead apparently fixes it, and causes no noticable image quality effect. # (Presumably they get rounded to integer window positions after the view volume clipping, # though some effects I saw implied that they don't get rounded, so maybe 0.1 is close enough to 0.0.) # This only matters when GLPane size is 100x100 or less, # or when drawing this in lower left corner for debugging, # so we don't have to worry about whether it's perfect. # (The known perfect fix is to initialize both matrices, but we don't want to bother, # or to worry about exceeding the projection matrix stack depth.) x1 = max(x, 0.1) y1 = max(y, 0.1) depth = 0.1 # this should not matter, as long as it's within the viewing volume x1, y1, z1 = gluUnProject(x1, y1, depth) glRasterPos3f(x1, y1, z1) # z1 does matter (when in perspective view), since this is a 3d position # Note: using glWindowPos would be simpler and better, but it's not defined # by the PyOpenGL I'm using. [bruce iMac G5 070626] ### UPDATE [bruce 081203]: glWindowPos2i *is* defined, at least on my newer Mac. # There are lots of variants, all suffix-typed with [23][dfis]{,v}. # I need to check whether it's defined on the older Macs too, then use it here. ####FIX if not glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID): # This was happening when we used x, y = exact 0, # and was causing the image to not get drawn sometimes (when mousewheel zoom was used). # It can still happen for extreme values of mousewheel zoom (close to the ones # which cause OpenGL exceptions), mostly only when pos = (0, 0) but not entirely. # Sometimes the actual drawing positions can get messed up then, too. # This doesn't matter much, but indicates that re-initing the matrices would be # a better solution if we could be sure the projection stack depth was sufficient # (or if we reset the standard projection when done, rather than using push/pop). print "bug: not glGetBooleanv(GL_CURRENT_RASTER_POSITION_VALID); pos =", x, y # if this happens, the subsequent effect is that glDrawPixels draws nothing else: # verify correct raster position px, py, pz, pw = glGetFloatv(GL_CURRENT_RASTER_POSITION) if not (0 <= px - x < 0.4) and (0 <= py - y < 0.4): # change to -0.4 < px - x ?? print "LIKELY BUG: glGetFloatv(GL_CURRENT_RASTER_POSITION) = %s" % [px, py, pz, pw] # seems to be in window coord space, but with float values, # roughly [0.1, 0.1, 0.1, 1.0] but depends on viewpoint, error about 10**-5 pass return
def get_position_from_click(self, x, y): """https://stackoverflow.com/questions/8739311/opengl-select-sphere-with-mouse?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa https://learnopengl.com/Advanced-OpenGL/Framebuffers """ viewport = glGetIntegerv(GL_VIEWPORT) wx = x wy = self.height - y if self.use_frame_buffer: self.frame_buffer.bind_intermediate() #self.frame_buffer.bind() wz = glReadPixels(wx, wy, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT)[0] if self.use_frame_buffer: self.frame_buffer.unbind() view = np.array(self.camera.get_view_matrix(), dtype=np.double) proj = np.array(self.camera.get_projection_matrix(), dtype=np.double) p = gluUnProject(wx, wy, wz, view, proj, viewport) return p
def GetWorldCoords(self, x, y, camera_z): """ returns (x,y,z) given x,y pygame screen coords. NOTE: try and get the model view matrix back to the camera position when using. """ y = self.height - y mod = glGetDoublev(GL_MODELVIEW_MATRIX) proj = glGetDoublev(GL_PROJECTION_MATRIX) #view = glGetIntegerv(GL_VIEWPORT) view = (0, 0, self.width, self.height) z = abs(camera_z) z_buffer_value = self.buffer_calc_a + self.buffer_calc_b / z objx, objy, objz = gluUnProject(x, y, z_buffer_value, mod, proj, view) return (objx, objy, objz)
def GetWorldCoords(self, x,y, camera_z): """ returns (x,y,z) given x,y pygame screen coords. NOTE: try and get the model view matrix back to the camera position when using. """ y = self.height - y mod = glGetDoublev(GL_MODELVIEW_MATRIX) proj = glGetDoublev(GL_PROJECTION_MATRIX) #view = glGetIntegerv(GL_VIEWPORT) view = (0, 0, self.width, self.height) z = abs(camera_z) z_buffer_value = self.buffer_calc_a +self.buffer_calc_b / z objx, objy, objz = gluUnProject(x,y,z_buffer_value, mod, proj,view) return (objx, objy, objz)
def draw(self): if self.delegate is None: # 070210 -- but I'm not sure if this is a complete ###KLUGE, or good but ought to be done more generally, # or if it would be better to do it totally differently by instantiating None into something like Spacer(). ##k return glMatrixMode(GL_MODELVIEW) # not needed glPushMatrix() glLoadIdentity() try: glpane = self.env.glpane ## aspect = 1.0 ###WRONG -- should use glpane.aspect (which exists as of 070919) aspect = glpane.aspect # revised by bruce 070919, UNTESTED corner = self.corner delegate = self.delegate want_depth = self.want_depth # note about cov_depth: ## self.near = 0.25 ## self.far = 12.0 # so I predict cov_depth is 0.75 / 11.75 == 0.063829787234042548 # but let's print it from a place that computes it, and see. # ... hmm, only place under that name is in selectMode.py, and that prints 0.765957458814 -- I bet it's something # different, but not sure. ###k (doesn't matter for now) # modified from _setup_modelview: saveplace = ( self.transient_state ) # see if this fixes the bug 061211 1117a mentioned below -- it does, use it. if glpane.current_glselect or (0 and "KLUGE" and hasattr(saveplace, "_saved_stuff")): # kluge did make it faster; still slow, and confounded by the highlighting-delay bug; # now I fixed that bug, and now it seems only normally slow for this module -- ok for now. x1, y1, z1 = saveplace._saved_stuff # this is needed to make highlighting work! ###BUG [061211 1117a; fixed now, see above, using saveplace = self.transient_state]: # if we click on an object in testexpr_15d (with DrawInCorner used for other objs in the testbed) # before it has a chance to show its highlighted form, at least after a recent reload, we get an attrerror here. # Easy to repeat in the test conditions mentioned (on g5). Not sure how it can affect a different obj (self) # than the one clicked on too quickly. Best fix would be to let glpane give us the requested info, # which is usually the same for all callers anyway, and the same across reloads (just not across resizes). # But it does depend on want_depth, and (via gluUnProject) on the current modelview coords # (and projection coords if we ever changed them). So it's not completely clear how to combine ease, efficiency, # and safety, for that optim in general, even w/o needing this bugfix. # But the bug is easy to hit, so needs a soon fix... maybe memoize it with a key corresponding to your own # assumed choice of modelview coords and want_depth? Or maybe enough to put it into the transient_state? TRY THAT. works. else: x1, y1, z1 = saveplace._saved_stuff = gluUnProject( glpane.width, glpane.height, want_depth ) # max x and y, i.e. right top # (note, min x and y would be (0,0,want_depth), since these are windows coords, 0,0 is bottom left corner (not center)) # Note: Using gluUnProject is probably better than knowing and reversing _setup_projection, # since it doesn't depend on knowing the setup code, except meaning of glpane height & width attrs, # and knowing that origin is centered between them. ## print x1,y1,z1 x1wish = glpane.width / 2.0 * PIXELS # / 2.0 because in these coords, screen center indeed has x == y == 0 r = x1 / x1wish glScale( r, r, r ) # compensate for zoom*scale in _setup_projection, for error in PIXELS, and for want_depth != cov_depth ## x1 /= r ## y1 /= r z1 /= r # now the following might work except for z, so fix z here glTranslatef(0.0, 0.0, z1) del x1, y1 # not presently used # I don't think we need to usage-track glpane height & width (or scale or zoomFactor etc) # since we'll redraw when those change, and redo this calc every time we draw. # The only exception would be if we're rendering into a display list. # I don't know if this code (gluUnProject) would even work in that case. # [I think I wrote a similar comment in some other file earlier today. #k] # move to desired corner, and align it with same corner of lbox # (#e could use an alignment prim for the corner if we had one) if corner in corner_abbrevs: # normalize how corner is expressed, into a 2-tuple of +-1's corner = corner_abbrevs[corner] x, y = corner if x == -1: # left x_offset = -glpane.width / 2.0 * PIXELS + delegate.bleft elif x == +1: # right x_offset = +glpane.width / 2.0 * PIXELS - delegate.bright elif x == 0: # center(x) x_offset = 0 # note: before 070210 this was (+ delegate.bleft - delegate.bright) / 2.0, # which has an unwanted (since unavoidable) centering effect; use explicit Center if desired. else: print "invalid corner", corner ### raise ValueError, "invalid corner %r" % (corner,) if y == -1: # bottom y_offset = -glpane.height / 2.0 * PIXELS + delegate.bbottom elif y == +1: # top y_offset = +glpane.height / 2.0 * PIXELS - delegate.btop elif y == 0: # center(y) y_offset = 0 # note: # note: before 070210 this was (+ delegate.bbottom - delegate.btop) / 2.0 else: print "invalid corner", corner ### raise ValueError, "invalid corner %r" % (corner,) offset = (x_offset, y_offset) glTranslatef(offset[0], offset[1], 0.0) self.drawkid(delegate) ## delegate.draw() finally: glMatrixMode(GL_MODELVIEW) # not needed glPopMatrix() return
def drawfont2(glpane, msg = None, charwidth = None, charheight = None, testpattern = False, pixelwidth = None): """ Draws a rect of chars (dimensions given as char counts: charwidth x charheight [#e those args are misnamed] using vv's font texture [later 061113: assumed currently bound, i think -- see ensure_courierfile_loaded()], in a klugy way; msg gives the chars to draw (lines must be shorter than charwidth or they will be truncated) """ _bind_courier_font_texture() # adjust these guessed params (about the specific font image we're using as # a texture) until they work right: # (tex_origin_chars appears lower down, since it is revised there) tex_size = (128,128) # tex size in pixels tex_nx = 16 # number of chars in a row, in the tex image tex_ny = 8 # number of chars in a column if msg is None: msg = "(redraw %d)" % env.redraw_counter charwidth = tex_nx charheight = tex_ny + 1 lines = msg.split('\n') # tab not supported if charwidth is None: charwidth = 14 # could be max linelength plus 1 if charheight is None: charheight = len(lines) if not testpattern: while len(lines) < charheight: lines.append('') # draw individual chars from font-texture, # but first, try to position it so they look perfect (which worked for a # while, but broke sometime before 060728) ## glTranslatef( 0, pixelheight / 2, 0 ) # perfect! # (Ortho mode, home view, a certain window size -- not sure if that # matters but it might) # restoring last-saved window position (782, 44) and size (891, 749) ## gap = 2 # in pixels - good for debugging # good for looking nice! but note that idlehack uses one extra pixel of # vspace, and that does probably look better. gap = 0 # to do that efficiently i'd want another image to start from. # (or to modify this one to make the texture, by moving pixrects around) pixfactor = 1 # try *2... now i can see some fuzz... what if i start at origin, to draw? # Did that, got it tolerable for pixfactor 2, then back to 1 and i've lost # the niceness! Is it no longer starting at origin? ##pixelwidth = pixelheight = 0.05 * 2/3 #070124 revisions, general comment... current caveats/bugs: #### # - Only tested in Ortho mode. # - Working well but the bugs depend on whether we add "1 or" before # "pixelwidth is None" in if statement below: # Bugs when it computes pixelwidth here (even when passed, as always in # these tests): # - Textlabel for "current redraw" (in exprs/test.py bottom_left_corner) # disappears during highlighting. # Bugs when it doesn't [usual state & state I'll leave it in]: # - Not tested with displists off, maybe. ### # - Fuzzy text in testexpr_18 [not yet understood.] # - [fixed] Fuzzy text in "current redraw" textlabel during anything's # highlighting. [Fixed by removing DisplayListChunk from that.] # - [No bug in clarity of text in highlighted checkbox prefs themselves.] # - Works with displists on or off now. # - Is disable_translate helping?? Not sure (only matters when it computes # pixelwidth here -- not now.) # - ##e Use direct drawing_phase test instead? doesn't seem to be needed # anymore, from remaining bugs listed above. disable_translate = False #070124 #061211 permit caller to pass it [note: the exprs module usually or always # passes it, acc'd to test 070124] if pixelwidth is None: p1junk, p2a = mymousepoints(glpane, 10, 10) p1junk, p2b = mymousepoints(glpane, 11, 10) px,py,pz = vec = p2b - p2a # should be DX * pixelwidth ## print px,py,pz ### 0.0313971755382 0.0 0.0 (in Ortho mode, near but not at home view, ### also at it (??)) # 0.0313971755382 0.0 0.0 home ortho # 0.03139613018 0.0 0.0 home perspective -- still looks good (looks # the same) (with false "smoother textures") ## pixelwidth = pixelheight = px * pixfactor # 061211 Work better for rotated text (still not good unless # screen-parallel.) pixelwidth = pixelheight = vlen(vec) * pixfactor # print "pixelwidth",pixelwidth ####@@@@ can be one of: # 0.0319194157846 # or 0.0313961295259 # or 0.00013878006302 if pixelwidth < 0.01: # print "low pixelwidth:",pixelwidth, glpane.drawing_phase # e.g. 0.000154639183832 glselect pixelwidth = 0.0319194157846 ### kluge, in case you didn't notice [guess: formula is wrong during # highlighting] but this failed to fix the bug in which a TextRect # doesn't notice clicks unless you slide onto it from a Rect # ####@@@@ # Q 070124: when this happens (presumably due to small window in # glSelect) should we disable glTranslatef below? # I guess yes, so try it. Not that it'll help when we re-disable # always using this case. disable_translate = True # I'll leave this in since it seems right, but it's not obviously # helping by test. pass else: pixelheight = pixelwidth tex_origin_chars = V(3, 64) # revised 070124 #070124 -- note that disable_translate is never set given if statements #above -- if 1 and not disable_translate: ##e Use glpane.drawing_phase == 'glselect' instead? doesn't seem to ## be needed anymore, from remaining bugs listed above. # Translate slightly to make characters clearer (since we're still too # lazy to use glDrawPixels or whatever it's called). # Caveats: # - Assumes we're either not in a displist, or it's always drawn in the # same place. # - Will only work if we're drawing at correct depth for pixelwidth, I # presume -- and of course, parallel to screen. x,y,depth = gluProject(0.0, 0.0, 0.0) # Where we'd draw without any correction (ORIGIN). # Change x and y to a better place to draw (in pixel coords on screen.) # (note: This int(x+0.5) was compared by test to int(x) and int(x)+0.5 # -- this is best, as one might guess; not same for y...) ## if we need a "boldface kluge", using int(x)+0.5 here would be one... x = int(x+0.5) ### NOT UNDERSTOOD: Why x & y differ, in whether it's best to add this ### 0.5. y = int(y+0.5)+0.5 # [btw I'd guessed y+0.5 in int() should be (y-0.5)+0.5 due to outer # +0.5, but that looks worse in checkbox_pref centering; I don't know # why.] # [later, 080521: could it be +0.5 effect differing for x & y due to # different sign, since int() rounds towards zero rather than # towards neginf? ### REVIEW: fix this using intRound?] # # Adding outer 0.5 to y became best after I fixed a bug of # translating before glu*Project (just before this if-statement), # which fails inside displists since the translate effect doesn't # show up in glu*Project then. # # I wonder if the x/y difference could be related to the use of # CenterY around TextRect inside displists, which ought to produce a # similar bug if the shift is not by an exact number of pixels # (which is surely the case since the caller is guessing pixelwidth # as a hardcoded constant, IIRC). So the guess is that caller's # pixelwidth is wrong and/or CenterY shifts by an odd number of # halfpixels, inside displist and not seen by this glu*Project, # causing a bug which this +0.5 sometimes compensates for, but not # always due to pixelwidth being wrong. It's not worth # understanding this compared to switching over to glDrawPixels or # whatever it's called. ###DO THAT SOMETIME. p1 = A(gluUnProject(x, y, depth)) # where we'd like to draw (p1) # Test following code -- with this line, it should make us draw # noticeably higher up the screen -- works. ## p1 += DY glTranslatef( p1[0], p1[1], p1[2]) # fyi: NameError if we try glTranslatefv or glTranslatev -- didn't # look for other variants or in gl doc. pass tex_dx = V(tex_width, 0) # in pixels tex_dy = V(0, tex_height) # Using those guesses, come up with tex-rects for each char as triples of # 2-vectors (tex_origin, tex_dx, tex_dy). # i for y, j or x (is that order normal??), still starting at bottom left def ff(i,j): """ Which char to draw at this position? i is y, going up, -1 is lowest line (sorry.) """ nlines = len(lines) bottom = -1 # Change this api sometime. # This one too -- caller ought to be able to use 0 or 1 for the top # (first) line. abovethat = i - bottom if abovethat < nlines: # Draw chars from lines. test = lines[nlines-1 - abovethat] # Few-day(?)-old comment [as of 060728], not sure why it's # exactly here, maybe since this tells when we redraw, but it # might be correct other than that: this shows that mouseover of # objects (pixels) w/o glnames causes redraws! I understand # glselect ones, but they should not be counted, and should not # show up on the screen, so I don't understand any good reason # for these visible ones to happen. #e To try to find out, we could also record compact_stack of the # first gl_update that caused this redraw... if j < len(test): # Replace i,j with new ones so as to draw those chars instead. ch1 = ord(test[j]) - 32 j = ch1 % tex_nx i = 5 - (ch1 / tex_nx) else: # Draw a blank instead. ch1 = 32 - 32 j = ch1 % tex_nx i = 5 - (ch1 / tex_nx) else: # Use i,j to index the texture, meaning, draw test chars, perhaps # the entire thing. pass return tex_origin_chars + i * tex_dy + j * tex_dx , tex_dx, tex_dy # Now think about where to draw all this... use a gap, but otherwise the # same layout. charwidth1 = tex_width * pixelwidth charheight1 = tex_height * pixelheight char_dx = (tex_width + gap) * pixelwidth char_dy = (tex_height + gap) * pixelheight def gg(i,j): return (ORIGIN + j * char_dx * DX + (i + 1) * char_dy * DY, charwidth1 * DX, charheight1 * DY) # Now draw them. if 1: #### for n in range(65): # Simulate delay of a whole page of chars. # Note, this is significantly slow even if we just draw 5x as many # chars! (range was -1,tex_ny == 8, length 9) - Note, increasing i goes # up on screen, not down! for i in range(-1, charheight - 1): for j in range(charwidth): # Was tex_nx == 16 origin, dx, dy = gg(i,j) # Where to draw this char ###k # Still in pixel ints. # What tex coords to use to find it? tex_origin, ltex_dx, ltex_dy = ff(i,j) # Kluge until i look up how to use pixels directly. tex_origin, ltex_dx, ltex_dy = 1.0/tex_size[0] \ * V(tex_origin, ltex_dx, ltex_dy) #print (origin, dx, dy, tex_origin, tex_dx, tex_dy) # Cool bug effect bfr 'l's here. draw_textured_rect(origin, dx, dy, tex_origin, ltex_dx, ltex_dy) # draw some other ones? done above, with test string inside ff function. # Interesting q -- if i use vertex arrays or displist-index arrays, can # drawing 10k chars be fast? (guess: yes.) # Some facts: this window now has 70 lines, about 135 columns... of course # it's mostly blank, and in fact just now it has about 3714 chars, not 70 * # 135 = 9450 as if it was nowhere blank. # Above we draw 9 * 16 = 144, so ratio is 3714 / 144 = 25, or 9450 / 144 = # 65. # Try 65 redundant loops above & see what happens. It takes it a couple # seconds! Too slow! Of course it's mostly the py code. # Anyway, it means we *will* have to do one of those optims mentioned, or # some other one like not clearing/redrawing the entire screen during most # text editing, or having per-line display lists. return #drawfont2 #e rename, clean up
def drawfont2(glpane, msg=None, charwidth=None, charheight=None, testpattern=False, pixelwidth=None): """ Draws a rect of chars (dimensions given as char counts: charwidth x charheight [#e those args are misnamed] using vv's font texture [later 061113: assumed currently bound, i think -- see ensure_courierfile_loaded()], in a klugy way; msg gives the chars to draw (lines must be shorter than charwidth or they will be truncated) """ _bind_courier_font_texture() # adjust these guessed params (about the specific font image we're using as # a texture) until they work right: # (tex_origin_chars appears lower down, since it is revised there) tex_size = (128, 128) # tex size in pixels tex_nx = 16 # number of chars in a row, in the tex image tex_ny = 8 # number of chars in a column if msg is None: msg = "(redraw %d)" % env.redraw_counter charwidth = tex_nx charheight = tex_ny + 1 lines = msg.split('\n') # tab not supported if charwidth is None: charwidth = 14 # could be max linelength plus 1 if charheight is None: charheight = len(lines) if not testpattern: while len(lines) < charheight: lines.append('') # draw individual chars from font-texture, # but first, try to position it so they look perfect (which worked for a # while, but broke sometime before 060728) ## glTranslatef( 0, pixelheight / 2, 0 ) # perfect! # (Ortho mode, home view, a certain window size -- not sure if that # matters but it might) # restoring last-saved window position (782, 44) and size (891, 749) ## gap = 2 # in pixels - good for debugging # good for looking nice! but note that idlehack uses one extra pixel of # vspace, and that does probably look better. gap = 0 # to do that efficiently i'd want another image to start from. # (or to modify this one to make the texture, by moving pixrects around) pixfactor = 1 # try *2... now i can see some fuzz... what if i start at origin, to draw? # Did that, got it tolerable for pixfactor 2, then back to 1 and i've lost # the niceness! Is it no longer starting at origin? ##pixelwidth = pixelheight = 0.05 * 2/3 #070124 revisions, general comment... current caveats/bugs: #### # - Only tested in Ortho mode. # - Working well but the bugs depend on whether we add "1 or" before # "pixelwidth is None" in if statement below: # Bugs when it computes pixelwidth here (even when passed, as always in # these tests): # - Textlabel for "current redraw" (in exprs/test.py bottom_left_corner) # disappears during highlighting. # Bugs when it doesn't [usual state & state I'll leave it in]: # - Not tested with displists off, maybe. ### # - Fuzzy text in testexpr_18 [not yet understood.] # - [fixed] Fuzzy text in "current redraw" textlabel during anything's # highlighting. [Fixed by removing DisplayListChunk from that.] # - [No bug in clarity of text in highlighted checkbox prefs themselves.] # - Works with displists on or off now. # - Is disable_translate helping?? Not sure (only matters when it computes # pixelwidth here -- not now.) # - ##e Use direct drawing_phase test instead? doesn't seem to be needed # anymore, from remaining bugs listed above. disable_translate = False #070124 #061211 permit caller to pass it [note: the exprs module usually or always # passes it, acc'd to test 070124] if pixelwidth is None: p1junk, p2a = mymousepoints(glpane, 10, 10) p1junk, p2b = mymousepoints(glpane, 11, 10) px, py, pz = vec = p2b - p2a # should be DX * pixelwidth ## print px,py,pz ### 0.0313971755382 0.0 0.0 (in Ortho mode, near but not at home view, ### also at it (??)) # 0.0313971755382 0.0 0.0 home ortho # 0.03139613018 0.0 0.0 home perspective -- still looks good (looks # the same) (with false "smoother textures") ## pixelwidth = pixelheight = px * pixfactor # 061211 Work better for rotated text (still not good unless # screen-parallel.) pixelwidth = pixelheight = vlen(vec) * pixfactor # print "pixelwidth",pixelwidth ####@@@@ can be one of: # 0.0319194157846 # or 0.0313961295259 # or 0.00013878006302 if pixelwidth < 0.01: # print "low pixelwidth:",pixelwidth, glpane.drawing_phase # e.g. 0.000154639183832 glselect pixelwidth = 0.0319194157846 ### kluge, in case you didn't notice [guess: formula is wrong during # highlighting] but this failed to fix the bug in which a TextRect # doesn't notice clicks unless you slide onto it from a Rect # ####@@@@ # Q 070124: when this happens (presumably due to small window in # glSelect) should we disable glTranslatef below? # I guess yes, so try it. Not that it'll help when we re-disable # always using this case. disable_translate = True # I'll leave this in since it seems right, but it's not obviously # helping by test. pass else: pixelheight = pixelwidth tex_origin_chars = V(3, 64) # revised 070124 #070124 -- note that disable_translate is never set given if statements #above -- if 1 and not disable_translate: ##e Use glpane.drawing_phase == 'glselect' instead? doesn't seem to ## be needed anymore, from remaining bugs listed above. # Translate slightly to make characters clearer (since we're still too # lazy to use glDrawPixels or whatever it's called). # Caveats: # - Assumes we're either not in a displist, or it's always drawn in the # same place. # - Will only work if we're drawing at correct depth for pixelwidth, I # presume -- and of course, parallel to screen. x, y, depth = gluProject(0.0, 0.0, 0.0) # Where we'd draw without any correction (ORIGIN). # Change x and y to a better place to draw (in pixel coords on screen.) # (note: This int(x+0.5) was compared by test to int(x) and int(x)+0.5 # -- this is best, as one might guess; not same for y...) ## if we need a "boldface kluge", using int(x)+0.5 here would be one... x = int(x + 0.5) ### NOT UNDERSTOOD: Why x & y differ, in whether it's best to add this ### 0.5. y = int(y + 0.5) + 0.5 # [btw I'd guessed y+0.5 in int() should be (y-0.5)+0.5 due to outer # +0.5, but that looks worse in checkbox_pref centering; I don't know # why.] # [later, 080521: could it be +0.5 effect differing for x & y due to # different sign, since int() rounds towards zero rather than # towards neginf? ### REVIEW: fix this using intRound?] # # Adding outer 0.5 to y became best after I fixed a bug of # translating before glu*Project (just before this if-statement), # which fails inside displists since the translate effect doesn't # show up in glu*Project then. # # I wonder if the x/y difference could be related to the use of # CenterY around TextRect inside displists, which ought to produce a # similar bug if the shift is not by an exact number of pixels # (which is surely the case since the caller is guessing pixelwidth # as a hardcoded constant, IIRC). So the guess is that caller's # pixelwidth is wrong and/or CenterY shifts by an odd number of # halfpixels, inside displist and not seen by this glu*Project, # causing a bug which this +0.5 sometimes compensates for, but not # always due to pixelwidth being wrong. It's not worth # understanding this compared to switching over to glDrawPixels or # whatever it's called. ###DO THAT SOMETIME. p1 = A(gluUnProject(x, y, depth)) # where we'd like to draw (p1) # Test following code -- with this line, it should make us draw # noticeably higher up the screen -- works. ## p1 += DY glTranslatef(p1[0], p1[1], p1[2]) # fyi: NameError if we try glTranslatefv or glTranslatev -- didn't # look for other variants or in gl doc. pass tex_dx = V(tex_width, 0) # in pixels tex_dy = V(0, tex_height) # Using those guesses, come up with tex-rects for each char as triples of # 2-vectors (tex_origin, tex_dx, tex_dy). # i for y, j or x (is that order normal??), still starting at bottom left def ff(i, j): """ Which char to draw at this position? i is y, going up, -1 is lowest line (sorry.) """ nlines = len(lines) bottom = -1 # Change this api sometime. # This one too -- caller ought to be able to use 0 or 1 for the top # (first) line. abovethat = i - bottom if abovethat < nlines: # Draw chars from lines. test = lines[nlines - 1 - abovethat] # Few-day(?)-old comment [as of 060728], not sure why it's # exactly here, maybe since this tells when we redraw, but it # might be correct other than that: this shows that mouseover of # objects (pixels) w/o glnames causes redraws! I understand # glselect ones, but they should not be counted, and should not # show up on the screen, so I don't understand any good reason # for these visible ones to happen. #e To try to find out, we could also record compact_stack of the # first gl_update that caused this redraw... if j < len(test): # Replace i,j with new ones so as to draw those chars instead. ch1 = ord(test[j]) - 32 j = ch1 % tex_nx i = 5 - (ch1 / tex_nx) else: # Draw a blank instead. ch1 = 32 - 32 j = ch1 % tex_nx i = 5 - (ch1 / tex_nx) else: # Use i,j to index the texture, meaning, draw test chars, perhaps # the entire thing. pass return tex_origin_chars + i * tex_dy + j * tex_dx, tex_dx, tex_dy # Now think about where to draw all this... use a gap, but otherwise the # same layout. charwidth1 = tex_width * pixelwidth charheight1 = tex_height * pixelheight char_dx = (tex_width + gap) * pixelwidth char_dy = (tex_height + gap) * pixelheight def gg(i, j): return (ORIGIN + j * char_dx * DX + (i + 1) * char_dy * DY, charwidth1 * DX, charheight1 * DY) # Now draw them. if 1: #### for n in range(65): # Simulate delay of a whole page of chars. # Note, this is significantly slow even if we just draw 5x as many # chars! (range was -1,tex_ny == 8, length 9) - Note, increasing i goes # up on screen, not down! for i in range(-1, charheight - 1): for j in range(charwidth): # Was tex_nx == 16 origin, dx, dy = gg(i, j) # Where to draw this char ###k # Still in pixel ints. # What tex coords to use to find it? tex_origin, ltex_dx, ltex_dy = ff(i, j) # Kluge until i look up how to use pixels directly. tex_origin, ltex_dx, ltex_dy = 1.0/tex_size[0] \ * V(tex_origin, ltex_dx, ltex_dy) #print (origin, dx, dy, tex_origin, tex_dx, tex_dy) # Cool bug effect bfr 'l's here. draw_textured_rect(origin, dx, dy, tex_origin, ltex_dx, ltex_dy) # draw some other ones? done above, with test string inside ff function. # Interesting q -- if i use vertex arrays or displist-index arrays, can # drawing 10k chars be fast? (guess: yes.) # Some facts: this window now has 70 lines, about 135 columns... of course # it's mostly blank, and in fact just now it has about 3714 chars, not 70 * # 135 = 9450 as if it was nowhere blank. # Above we draw 9 * 16 = 144, so ratio is 3714 / 144 = 25, or 9450 / 144 = # 65. # Try 65 redundant loops above & see what happens. It takes it a couple # seconds! Too slow! Of course it's mostly the py code. # Anyway, it means we *will* have to do one of those optims mentioned, or # some other one like not clearing/redrawing the entire screen during most # text editing, or having per-line display lists. glpane.kluge_reset_texture_mode_to_work_around_renderText_bug() return #drawfont2 #e rename, clean up
def select(self, wX, wY): """ Use the OpenGL picking/selection to select any object. Return the selected object, otherwise, return None. Restore projection and model/view matrices before returning. """ ####@@@@ WARNING: The original code for this, in GLPane, has been duplicated and slightly modified # in at least three other places (search for glRenderMode to find them). This is bad; common code # should be used. Furthermore, I suspect it's sometimes needlessly called more than once per frame; # that should be fixed too. [bruce 060721 comment] wZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT) gz = wZ[0][0] if gz >= GL_FAR_Z: ##Empty space was clicked return None pxyz = A(gluUnProject(wX, wY, gz)) pn = self.out pxyz -= 0.0002 * pn # Note: if this runs before the model is drawn, this can have an # exception "OverflowError: math range error", presumably because # appropriate state for gluUnProject was not set up. That doesn't # normally happen but can happen due to bugs (no known open bugs # of that kind). # Sometimes our drawing area can become "stuck at gray", # and when that happens, the same exception can occur from this line. # Could it be that too many accidental mousewheel scrolls occurred # and made the scale unreasonable? (To mitigate, we should prevent # those from doing anything unless we have a valid model, and also # reset that scale when loading a new model (latter is probably # already done, but I didn't check).) [bruce 080220 comment] dp = -dot(pxyz, pn) # Save projection matrix before it's changed. glMatrixMode(GL_PROJECTION) glPushMatrix() current_glselect = (wX, wY, 1, 1) self._setup_projection(glselect=current_glselect) glSelectBuffer(self.glselectBufferSize) glRenderMode(GL_SELECT) glInitNames() glMatrixMode(GL_MODELVIEW) # Save model view matrix before it's changed. glPushMatrix() try: glClipPlane(GL_CLIP_PLANE0, (pn[0], pn[1], pn[2], dp)) glEnable(GL_CLIP_PLANE0) self.drawModel() glDisable(GL_CLIP_PLANE0) except: print_compact_traceback("exception in mode.Draw() during GL_SELECT; ignored; restoring modelview matrix: ") glPopMatrix() glRenderMode(GL_RENDER) return None else: # Restore model/view matrix glPopMatrix() # Restore project matrix and set matrix mode to Model/View glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glFlush() hit_records = list(glRenderMode(GL_RENDER)) if debug_flags.atom_debug and 0: print "%d hits" % len(hit_records) for (near, far, names) in hit_records: # see example code, renderpass.py if debug_flags.atom_debug and 0: print "hit record: near, far, names:", near, far, names # e.g. hit record: near, far, names: 1439181696 1453030144 (1638426L,) # which proves that near/far are too far apart to give actual depth, # in spite of the 1-pixel drawing window (presumably they're vertices # taken from unclipped primitives, not clipped ones). if names: name = names[-1] assy = self.assy obj = assy and assy.object_for_glselect_name(name) # k should always return an obj return obj return None # from ThumbView.select
def draw(self): if self.delegate is None: # 070210 -- but I'm not sure if this is a complete ###KLUGE, or good but ought to be done more generally, # or if it would be better to do it totally differently by instantiating None into something like Spacer(). ##k return glMatrixMode(GL_MODELVIEW) # not needed glPushMatrix() glLoadIdentity() try: glpane = self.env.glpane aspect = glpane.aspect # revised by bruce 070919 corner = self.corner delegate = self.delegate want_depth = self.want_depth # note about cov_depth: ## self.near = 0.25 ## self.far = 12.0 # so I predict cov_depth is 0.75 / 11.75 == 0.063829787234042548 # but let's print it from a place that computes it, and see. # ... hmm, only place under that name is in selectMode.py, and that prints 0.765957458814 -- I bet it's something # different, but not sure. ###k (doesn't matter for now) # modified from _setup_modelview: saveplace = self.transient_state # see if this fixes the bug 061211 1117a mentioned below -- it does, use it. # BUG (probably not related to this code, but not known for sure): # mousewheel zoom makes checkboxes inside DrawInCorner # either not highlight, or if they are close enough to # the center of the screen (I guess), highlight in the # wrong size and place. For more info and a partial theory, # see the 081202 update in DisplayListChunk.py docstring. # Before I realized the connection to DisplayListChunk, # I tried using saveplace = self.per_frame_state instead of # self.transient_state above, but that had no effect on the bug. # That was before I fixed some bugs in same_vals related to # numpy.ndarray, when I was plagued with mysterious behavior # from those in my debug code (since solved), but I tried it # again afterwards and it still doesn't fix this bug, which # makes sense if my partial theory about it is true. # # In principle (unrelated to this bug), I'm dubious we're # storing this state in the right place, but I won't change # it for now (see related older comments below). # # Note: I fixed the bug in another way, by changing # Highlightable's projection option to default True. # [bruce 081202] if glpane.current_glselect or (0 and 'KLUGE' and hasattr( saveplace, '_saved_stuff')): # kluge did make it faster; still slow, and confounded by the highlighting-delay bug; # now I fixed that bug, and now it seems only normally slow for this module -- ok for now. # when that cond is false, we have a nonstandard projection matrix # (see also the related comments in save_coords_if_safe in Highlightable.py) # [bruce 081204 comment] x1, y1, z1 = saveplace._saved_stuff # this is needed to make highlighting work! ###BUG [061211 1117a; fixed now, see above, using saveplace = self.transient_state]: # if we click on an object in testexpr_15d (with DrawInCorner used for other objs in the testbed) # before it has a chance to show its highlighted form, at least after a recent reload, we get an attrerror here. # Easy to repeat in the test conditions mentioned (on g5). Not sure how it can affect a different obj (self) # than the one clicked on too quickly. Best fix would be to let glpane give us the requested info, # which is usually the same for all callers anyway, and the same across reloads (just not across resizes). # But it does depend on want_depth, and (via gluUnProject) on the current modelview coords # (and projection coords if we ever changed them). So it's not completely clear how to combine ease, efficiency, # and safety, for that optim in general, even w/o needing this bugfix. # But the bug is easy to hit, so needs a soon fix... maybe memoize it with a key corresponding to your own # assumed choice of modelview coords and want_depth? Or maybe enough to put it into the transient_state? TRY THAT. works. if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: retrieved", x1, y1, z1 else: x1, y1, z1 = saveplace._saved_stuff = \ gluUnProject(glpane.width, glpane.height, want_depth) # max x and y, i.e. right top # (note, to get the min x and y we'd want to pass (0, 0, want_depth), # since these are windows coords -- (0, 0) is bottom left corner (not center)) # # Note: Using gluUnProject is probably better than knowing and reversing _setup_projection, # since it doesn't depend on knowing the setup code, except meaning of glpane height & width attrs, # and knowing that origin is centered between them and 0. if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: saved", x1, y1, z1 ## print x1,y1,z1 # use glScale to compensate for zoom * scale in _setup_projection, # for error in PIXELS, and for want_depth != cov_depth x1wish = glpane.width / 2.0 * PIXELS # / 2.0 because in these coords, screen center indeed has x == y == 0 r = x1 / x1wish glScale(r, r, r) ## x1 /= r ## y1 /= r z1 /= r # now the following might work except for z, so fix z here glTranslatef(0.0, 0.0, z1) del x1, y1 # not presently used if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: r = %r, translated z by z1 == %r" % ( r, z1) # I don't think we need to usage-track glpane height & width (or scale or zoomFactor etc) # since we'll redraw when those change, and redo this calc every time we draw. # The only exception would be if we're rendering into a display list. # I don't know if this code (gluUnProject) would even work in that case. # [I think I wrote a similar comment in some other file earlier today. #k] # move to desired corner, and align it with same corner of lbox # (#e could use an alignment prim for the corner if we had one) if corner in corner_abbrevs: # normalize how corner is expressed, into a 2-tuple of +-1's corner = corner_abbrevs[corner] x, y = corner if x == -1: # left x_offset = -glpane.width / 2.0 * PIXELS + delegate.bleft elif x == +1: # right x_offset = +glpane.width / 2.0 * PIXELS - delegate.bright elif x == 0: # center(x) x_offset = 0 # note: before 070210 this was (+ delegate.bleft - delegate.bright) / 2.0, # which has an unwanted (since unavoidable) centering effect; use explicit Center if desired. else: print "invalid corner", corner ### raise ValueError, "invalid corner %r" % (corner, ) if y == -1: # bottom y_offset = -glpane.height / 2.0 * PIXELS + delegate.bbottom elif y == +1: # top y_offset = +glpane.height / 2.0 * PIXELS - delegate.btop elif y == 0: # center(y) y_offset = 0 # note: # note: before 070210 this was (+ delegate.bbottom - delegate.btop) / 2.0 else: print "invalid corner", corner ### raise ValueError, "invalid corner %r" % (corner, ) offset = (x_offset, y_offset) glTranslatef(offset[0], offset[1], 0.0) if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: offset =", offset self.drawkid(delegate) ## delegate.draw() finally: glMatrixMode(GL_MODELVIEW) # not needed glPopMatrix() return
def render_pointcloud(self, width, height, cam_yaw=0): """Return an image as 3D points""" # Move the camera with the base_pos base_pos, orn = self.physics.getBasePositionAndOrientation( self.robot.racecarUniqueId) cam_yaw = 180 * cam_yaw / math.pi + 90 cam_yaw = cam_yaw % 360 cam_dist = 1 cam_roll = 0 cam_pitch = 0 base_pos = (base_pos[0], base_pos[1], base_pos[2] + 1) view_matrix = self.physics.computeViewMatrixFromYawPitchRoll( cameraTargetPosition=base_pos, distance=cam_dist, yaw=cam_yaw, pitch=cam_pitch, roll=cam_roll, upAxisIndex=2) view_matrix_reverse = self.physics.computeViewMatrixFromYawPitchRoll( cameraTargetPosition=base_pos, distance=cam_dist, yaw=(cam_yaw + 180) % 360, pitch=cam_pitch, roll=cam_roll, upAxisIndex=2) proj_matrix = self.physics.computeProjectionMatrixFOV( fov=60, aspect=float(width) / height, nearVal=1, farVal=40.0) (_, _, rgb, depth, segmentation) = self.physics.getCameraImage( width=width, height=height, viewMatrix=view_matrix, projectionMatrix=proj_matrix, renderer=pybullet.ER_BULLET_HARDWARE_OPENGL) # Make image rgb_image = np.array(rgb) rgb_image = rgb_image[:, :, :3] # Make points viewport = (0, 0, width, height) matrixModelView = view_matrix matrixProjection = proj_matrix # Make points slightly noise depth += np.random.normal(scale=ZED_NOISE, size=depth.shape) # Make camera params camera = make_camera(height, width, view_matrix_reverse, proj_matrix) # Output vectors pts = np.zeros((width * height, 3)) pts_rgb = np.zeros((width * height, 3)) pts_seg = np.zeros((width * height, )) for y in range(height): if y % 10 == 0: pass #print('row:',y) for x in range(width): z = depth[y, x] if z == 1.0: continue if z < ZED_MIN_RANGE or z > ZED_MAX_RANGE: continue i = x + y * width # Simple counter pts[i, :] = gluUnProject(x, y, z, matrixModelView, matrixProjection, viewport) pts_rgb[i, :] = rgb_image[y, x, :] / 255 pts_seg[i] = segmentation[y, x] return pts, pts_rgb, pts_seg, camera
def draw(self): if self.delegate is None: # 070210 -- but I'm not sure if this is a complete ###KLUGE, or good but ought to be done more generally, # or if it would be better to do it totally differently by instantiating None into something like Spacer(). ##k return glMatrixMode(GL_MODELVIEW) # not needed glPushMatrix() glLoadIdentity() try: glpane = self.env.glpane ## aspect = 1.0 ###WRONG -- should use glpane.aspect (which exists as of 070919) aspect = glpane.aspect # revised by bruce 070919, UNTESTED corner = self.corner delegate = self.delegate want_depth = self.want_depth # note about cov_depth: ## self.near = 0.25 ## self.far = 12.0 # so I predict cov_depth is 0.75 / 11.75 == 0.063829787234042548 # but let's print it from a place that computes it, and see. # ... hmm, only place under that name is in selectMode.py, and that prints 0.765957458814 -- I bet it's something # different, but not sure. ###k (doesn't matter for now) # modified from _setup_modelview: saveplace = self.transient_state # see if this fixes the bug 061211 1117a mentioned below -- it does, use it. if glpane.current_glselect or (0 and 'KLUGE' and hasattr(saveplace, '_saved_stuff')): # kluge did make it faster; still slow, and confounded by the highlighting-delay bug; # now I fixed that bug, and now it seems only normally slow for this module -- ok for now. x1, y1, z1 = saveplace._saved_stuff # this is needed to make highlighting work! ###BUG [061211 1117a; fixed now, see above, using saveplace = self.transient_state]: # if we click on an object in testexpr_15d (with DrawInCorner used for other objs in the testbed) # before it has a chance to show its highlighted form, at least after a recent reload, we get an attrerror here. # Easy to repeat in the test conditions mentioned (on g5). Not sure how it can affect a different obj (self) # than the one clicked on too quickly. Best fix would be to let glpane give us the requested info, # which is usually the same for all callers anyway, and the same across reloads (just not across resizes). # But it does depend on want_depth, and (via gluUnProject) on the current modelview coords # (and projection coords if we ever changed them). So it's not completely clear how to combine ease, efficiency, # and safety, for that optim in general, even w/o needing this bugfix. # But the bug is easy to hit, so needs a soon fix... maybe memoize it with a key corresponding to your own # assumed choice of modelview coords and want_depth? Or maybe enough to put it into the transient_state? TRY THAT. works. else: x1, y1, z1 = saveplace._saved_stuff = gluUnProject(glpane.width, glpane.height, want_depth) # max x and y, i.e. right top # (note, min x and y would be (0,0,want_depth), since these are windows coords, 0,0 is bottom left corner (not center)) # Note: Using gluUnProject is probably better than knowing and reversing _setup_projection, # since it doesn't depend on knowing the setup code, except meaning of glpane height & width attrs, # and knowing that origin is centered between them. ## print x1,y1,z1 x1wish = glpane.width / 2.0 * PIXELS # / 2.0 because in these coords, screen center indeed has x == y == 0 r = x1/x1wish glScale(r,r,r) # compensate for zoom*scale in _setup_projection, for error in PIXELS, and for want_depth != cov_depth ## x1 /= r ## y1 /= r z1 /= r # now the following might work except for z, so fix z here glTranslatef( 0.0, 0.0, z1) del x1,y1 # not presently used # I don't think we need to usage-track glpane height & width (or scale or zoomFactor etc) # since we'll redraw when those change, and redo this calc every time we draw. # The only exception would be if we're rendering into a display list. # I don't know if this code (gluUnProject) would even work in that case. # [I think I wrote a similar comment in some other file earlier today. #k] # move to desired corner, and align it with same corner of lbox # (#e could use an alignment prim for the corner if we had one) if corner in corner_abbrevs: # normalize how corner is expressed, into a 2-tuple of +-1's corner = corner_abbrevs[corner] x, y = corner if x == -1: # left x_offset = - glpane.width / 2.0 * PIXELS + delegate.bleft elif x == +1: # right x_offset = + glpane.width / 2.0 * PIXELS - delegate.bright elif x == 0: # center(x) x_offset = 0 # note: before 070210 this was (+ delegate.bleft - delegate.bright) / 2.0, # which has an unwanted (since unavoidable) centering effect; use explicit Center if desired. else: print "invalid corner",corner### raise ValueError, "invalid corner %r" % (corner,) if y == -1: # bottom y_offset = - glpane.height / 2.0 * PIXELS + delegate.bbottom elif y == +1: # top y_offset = + glpane.height / 2.0 * PIXELS - delegate.btop elif y == 0: # center(y) y_offset = 0 # note: # note: before 070210 this was (+ delegate.bbottom - delegate.btop) / 2.0 else: print "invalid corner",corner### raise ValueError, "invalid corner %r" % (corner,) offset = (x_offset, y_offset) glTranslatef(offset[0], offset[1], 0.0) self.drawkid( delegate) ## delegate.draw() finally: glMatrixMode(GL_MODELVIEW) # not needed glPopMatrix() return
def leftUp(self, event): """ Erase the final rubber band window and do zoom if user indeed draws a rubber band window. """ # bugs 1190, 1818 wware 4/05/2006 - sometimes Qt neglects to call # leftDown before this if not hasattr(self, "pWxy") or not hasattr(self, "firstDraw"): return cWxy = (event.pos().x(), self.glpane.height - event.pos().y()) zoomX = (abs(cWxy[0] - self.pWxy[0]) + 0.0) / (self.glpane.width + 0.0) zoomY = (abs(cWxy[1] - self.pWxy[1]) + 0.0) / (self.glpane.height + 0.0) # The rubber band window size can be larger than that of glpane. # Limit the zoomFactor to 1.0 zoomFactor = min(max(zoomX, zoomY), 1.0) # Huaicai: when rubber band window is too small, # like a double click, a single line rubber band, skip zoom DELTA = 1.0E-5 if self.pWxy[0] == cWxy[0] or self.pWxy[1] == cWxy[1] \ or zoomFactor < DELTA: self.command.command_Done() return # Erase the last rubber-band window rbwcolor = self.command.rbwcolor drawrectangle(self.pStart, self.pPrev, self.glpane.up, self.glpane.right, rbwcolor) glFlush() self.glpane.swapBuffers() winCenterX = (cWxy[0] + self.pWxy[0]) / 2.0 winCenterY = (cWxy[1] + self.pWxy[1]) / 2.0 winCenterZ = \ glReadPixelsf(int(winCenterX), int(winCenterY), 1, 1, GL_DEPTH_COMPONENT) assert winCenterZ[0][0] >= 0.0 and winCenterZ[0][0] <= 1.0 if winCenterZ[0][0] >= GL_FAR_Z: # window center touches nothing p1 = A(gluUnProject(winCenterX, winCenterY, 0.005)) p2 = A(gluUnProject(winCenterX, winCenterY, 1.0)) los = self.glpane.lineOfSight k = dot(los, -self.glpane.pov - p1) / dot(los, p2 - p1) zoomCenter = p1 + k*(p2-p1) else: zoomCenter = \ A(gluUnProject(winCenterX, winCenterY, winCenterZ[0][0])) self.glpane.pov = V(-zoomCenter[0], -zoomCenter[1], -zoomCenter[2]) # The following are 2 ways to do the zoom, the first one # changes view angles, the 2nd one change viewing distance # The advantage for the 1st one is model will not be clipped by # near or back clipping planes, and the rubber band can be # always shown. The disadvantage: when the view field is too # small, a selection window may be actually act as a single pick. # rubber band window will not look as rectangular any more. #zf = self.glpane.getZoomFactor() # [note: method does not exist] #zoomFactor = pow(zoomFactor, 0.25) #zoomFactor *= zf #self.glpane.setZoomFactor(zoomFactor) # [note: method does not exist] # Change viewing distance to do zoom. This works better with # mouse wheel, since both are changing viewing distance, and # it's not too bad of model being clipped, since the near/far clip # plane change as scale too. self.glpane.scale *= zoomFactor self.command.command_Done() return
def select(self, wX, wY): """ Use the OpenGL picking/selection to select any object. Return the selected object, otherwise, return None. Restore projection and model/view matrices before returning. """ ####@@@@ WARNING: The original code for this, in GLPane, has been duplicated and slightly modified # in at least three other places (search for glRenderMode to find them). This is bad; common code # should be used. Furthermore, I suspect it's sometimes needlessly called more than once per frame; # that should be fixed too. [bruce 060721 comment] wZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT) gz = wZ[0][0] if gz >= GL_FAR_Z: ##Empty space was clicked return None pxyz = A(gluUnProject(wX, wY, gz)) pn = self.out pxyz -= 0.0002 * pn # Note: if this runs before the model is drawn, this can have an # exception "OverflowError: math range error", presumably because # appropriate state for gluUnProject was not set up. That doesn't # normally happen but can happen due to bugs (no known open bugs # of that kind). # Sometimes our drawing area can become "stuck at gray", # and when that happens, the same exception can occur from this line. # Could it be that too many accidental mousewheel scrolls occurred # and made the scale unreasonable? (To mitigate, we should prevent # those from doing anything unless we have a valid model, and also # reset that scale when loading a new model (latter is probably # already done, but I didn't check).) [bruce 080220 comment] dp = -dot(pxyz, pn) #Save projection matrix before it's changed. glMatrixMode(GL_PROJECTION) glPushMatrix() current_glselect = (wX, wY, 1, 1) self._setup_projection(glselect=current_glselect) glSelectBuffer(self.glselectBufferSize) glRenderMode(GL_SELECT) glInitNames() glMatrixMode(GL_MODELVIEW) # Save model view matrix before it's changed. glPushMatrix() try: glClipPlane(GL_CLIP_PLANE0, (pn[0], pn[1], pn[2], dp)) glEnable(GL_CLIP_PLANE0) self.drawModel() glDisable(GL_CLIP_PLANE0) except: print_compact_traceback( "exception in mode.Draw() during GL_SELECT; ignored; restoring modelview matrix: " ) glPopMatrix() glRenderMode(GL_RENDER) return None else: # Restore model/view matrix glPopMatrix() #Restore project matrix and set matrix mode to Model/View glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glFlush() hit_records = list(glRenderMode(GL_RENDER)) if debug_flags.atom_debug and 0: print "%d hits" % len(hit_records) for (near, far, names) in hit_records: # see example code, renderpass.py if debug_flags.atom_debug and 0: print "hit record: near, far, names:", near, far, names # e.g. hit record: near, far, names: 1439181696 1453030144 (1638426L,) # which proves that near/far are too far apart to give actual depth, # in spite of the 1-pixel drawing window (presumably they're vertices # taken from unclipped primitives, not clipped ones). if names: name = names[-1] assy = self.assy obj = assy and assy.object_for_glselect_name(name) #k should always return an obj return obj return None # from ThumbView.select
def leftUp(self, event): """ Erase the final rubber band window and do zoom if user indeed draws a rubber band window. """ # bugs 1190, 1818 wware 4/05/2006 - sometimes Qt neglects to call # leftDown before this if not hasattr(self, "pWxy") or not hasattr(self, "firstDraw"): return cWxy = (event.pos().x(), self.glpane.height - event.pos().y()) zoomX = (abs(cWxy[0] - self.pWxy[0]) + 0.0) / (self.glpane.width + 0.0) zoomY = (abs(cWxy[1] - self.pWxy[1]) + 0.0) / (self.glpane.height + 0.0) # The rubber band window size can be larger than that of glpane. # Limit the zoomFactor to 1.0 zoomFactor = min(max(zoomX, zoomY), 1.0) # Huaicai: when rubber band window is too small, # like a double click, a single line rubber band, skip zoom DELTA = 1.0E-5 if self.pWxy[0] == cWxy[0] or self.pWxy[1] == cWxy[1] \ or zoomFactor < DELTA: self.command.Done() return # Erase the last rubber-band window rbwcolor = self.command.rbwcolor drawrectangle(self.pStart, self.pPrev, self.glpane.up, self.glpane.right, rbwcolor) glFlush() self.glpane.swapBuffers() winCenterX = (cWxy[0] + self.pWxy[0]) / 2.0 winCenterY = (cWxy[1] + self.pWxy[1]) / 2.0 winCenterZ = \ glReadPixelsf(int(winCenterX), int(winCenterY), 1, 1, GL_DEPTH_COMPONENT) assert winCenterZ[0][0] >= 0.0 and winCenterZ[0][0] <= 1.0 if winCenterZ[0][0] >= GL_FAR_Z: # window center touches nothing p1 = A(gluUnProject(winCenterX, winCenterY, 0.005)) p2 = A(gluUnProject(winCenterX, winCenterY, 1.0)) los = self.glpane.lineOfSight k = dot(los, -self.glpane.pov - p1) / dot(los, p2 - p1) zoomCenter = p1 + k * (p2 - p1) else: zoomCenter = \ A(gluUnProject(winCenterX, winCenterY, winCenterZ[0][0])) self.glpane.pov = V(-zoomCenter[0], -zoomCenter[1], -zoomCenter[2]) # The following are 2 ways to do the zoom, the first one # changes view angles, the 2nd one change viewing distance # The advantage for the 1st one is model will not be clipped by # near or back clipping planes, and the rubber band can be # always shown. The disadvantage: when the view field is too # small, a selection window may be actually act as a single pick. # rubber ban window will not look as rectanglular any more. #zf = self.glpane.getZoomFactor() #zoomFactor = pow(zoomFactor, 0.25) #zoomFactor *= zf #self.glpane.setZoomFactor(zoomFactor) # Change viewing distance to do zoom. This works better with # mouse wheel, since both are changing viewing distance, and # it's not too bad of model being clipped, since the near/far clip # plane change as scale too. self.glpane.scale *= zoomFactor self.command.Done(exit_using_done_or_cancel_button=False) return
def select(self, wX, wY): """ Use the OpenGL picking/selection to select any object. Return the selected object, otherwise, return None. Restore projection and modelview matrices before returning. """ ### NOTE: this code is similar to (and was copied and modified from) # GLPane_highlighting_methods.do_glselect_if_wanted, but also differs # in significant ways (too much to make it worth merging, unless we # decide to merge the differing algorithms as well). It's one of # several instances of hit-test code that calls glRenderMode. # [bruce 060721/080917 comment] wZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT) gz = wZ[0][0] if gz >= GL_FAR_Z: # Empty space was clicked return None pxyz = A(gluUnProject(wX, wY, gz)) pn = self.out pxyz -= 0.0002 * pn # Note: if this runs before the model is drawn, this can have an # exception "OverflowError: math range error", presumably because # appropriate state for gluUnProject was not set up. That doesn't # normally happen but can happen due to bugs (no known open bugs # of that kind). # Sometimes our drawing area can become "stuck at gray", # and when that happens, the same exception can occur from this line. # Could it be that too many accidental mousewheel scrolls occurred # and made the scale unreasonable? (To mitigate, we should prevent # those from doing anything unless we have a valid model, and also # reset that scale when loading a new model (latter is probably # already done, but I didn't check). See also the comments # in def wheelEvent.) [bruce 080220 comment] dp = - dot(pxyz, pn) # Save projection matrix before it's changed. glMatrixMode(GL_PROJECTION) glPushMatrix() current_glselect = (wX, wY, 1, 1) self._setup_projection(glselect = current_glselect) glSelectBuffer(self.SIZE_FOR_glSelectBuffer) glRenderMode(GL_SELECT) glInitNames() glMatrixMode(GL_MODELVIEW) # Save model view matrix before it's changed. glPushMatrix() # Draw model using glRenderMode(GL_SELECT) as set up above try: glClipPlane(GL_CLIP_PLANE0, (pn[0], pn[1], pn[2], dp)) glEnable(GL_CLIP_PLANE0) self._drawModel_using_DrawingSets() except: #bruce 080917 fixed predicted bugs in this except clause (untested) print_compact_traceback("exception in ThumbView._drawModel_using_DrawingSets() during GL_SELECT; ignored; restoring matrices: ") glDisable(GL_CLIP_PLANE0) glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glPopMatrix() glRenderMode(GL_RENDER) return None else: glDisable(GL_CLIP_PLANE0) # Restore model/view matrix glPopMatrix() # Restore projection matrix and set matrix mode to Model/View glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glFlush() hit_records = list(glRenderMode(GL_RENDER)) ## print "%d hits" % len(hit_records) for (near, far, names) in hit_records: ## print "hit record: near, far, names:", near, far, names # note from testing: near/far are too far apart to give actual depth, # in spite of the 1-pixel drawing window (presumably they're vertices # taken from unclipped primitives, not clipped ones). ### REVIEW: this just returns the first candidate object found. # The clip plane may restrict the set of candidates well enough to # make sure that's the right one, but this is untested and unreviewed. # (And it's just my guess that that was Huaicai's intention in # setting up clipping, since it's not documented. I'm guessing that # the plane is just behind the hitpoint, but haven't confirmed this.) # [bruce 080917 comment] if names: name = names[-1] assy = self.assy obj = assy and assy.object_for_glselect_name(name) #k should always return an obj return obj return None # from ThumbView.select
def draw(self): if self.delegate is None: # 070210 -- but I'm not sure if this is a complete ###KLUGE, or good but ought to be done more generally, # or if it would be better to do it totally differently by instantiating None into something like Spacer(). ##k return glMatrixMode(GL_MODELVIEW) # not needed glPushMatrix() glLoadIdentity() try: glpane = self.env.glpane aspect = glpane.aspect # revised by bruce 070919 corner = self.corner delegate = self.delegate want_depth = self.want_depth # note about cov_depth: ## self.near = 0.25 ## self.far = 12.0 # so I predict cov_depth is 0.75 / 11.75 == 0.063829787234042548 # but let's print it from a place that computes it, and see. # ... hmm, only place under that name is in selectMode.py, and that prints 0.765957458814 -- I bet it's something # different, but not sure. ###k (doesn't matter for now) # modified from _setup_modelview: saveplace = self.transient_state # see if this fixes the bug 061211 1117a mentioned below -- it does, use it. # BUG (probably not related to this code, but not known for sure): # mousewheel zoom makes checkboxes inside DrawInCorner # either not highlight, or if they are close enough to # the center of the screen (I guess), highlight in the # wrong size and place. For more info and a partial theory, # see the 081202 update in DisplayListChunk.py docstring. # Before I realized the connection to DisplayListChunk, # I tried using saveplace = self.per_frame_state instead of # self.transient_state above, but that had no effect on the bug. # That was before I fixed some bugs in same_vals related to # numpy.ndarray, when I was plagued with mysterious behavior # from those in my debug code (since solved), but I tried it # again afterwards and it still doesn't fix this bug, which # makes sense if my partial theory about it is true. # # In principle (unrelated to this bug), I'm dubious we're # storing this state in the right place, but I won't change # it for now (see related older comments below). # # Note: I fixed the bug in another way, by changing # Highlightable's projection option to default True. # [bruce 081202] if glpane.current_glselect or (0 and 'KLUGE' and hasattr(saveplace, '_saved_stuff')): # kluge did make it faster; still slow, and confounded by the highlighting-delay bug; # now I fixed that bug, and now it seems only normally slow for this module -- ok for now. # when that cond is false, we have a nonstandard projection matrix # (see also the related comments in save_coords_if_safe in Highlightable.py) # [bruce 081204 comment] x1, y1, z1 = saveplace._saved_stuff # this is needed to make highlighting work! ###BUG [061211 1117a; fixed now, see above, using saveplace = self.transient_state]: # if we click on an object in testexpr_15d (with DrawInCorner used for other objs in the testbed) # before it has a chance to show its highlighted form, at least after a recent reload, we get an attrerror here. # Easy to repeat in the test conditions mentioned (on g5). Not sure how it can affect a different obj (self) # than the one clicked on too quickly. Best fix would be to let glpane give us the requested info, # which is usually the same for all callers anyway, and the same across reloads (just not across resizes). # But it does depend on want_depth, and (via gluUnProject) on the current modelview coords # (and projection coords if we ever changed them). So it's not completely clear how to combine ease, efficiency, # and safety, for that optim in general, even w/o needing this bugfix. # But the bug is easy to hit, so needs a soon fix... maybe memoize it with a key corresponding to your own # assumed choice of modelview coords and want_depth? Or maybe enough to put it into the transient_state? TRY THAT. works. if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: retrieved", x1, y1, z1 else: x1, y1, z1 = saveplace._saved_stuff = \ gluUnProject(glpane.width, glpane.height, want_depth) # max x and y, i.e. right top # (note, to get the min x and y we'd want to pass (0, 0, want_depth), # since these are windows coords -- (0, 0) is bottom left corner (not center)) # # Note: Using gluUnProject is probably better than knowing and reversing _setup_projection, # since it doesn't depend on knowing the setup code, except meaning of glpane height & width attrs, # and knowing that origin is centered between them and 0. if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: saved", x1, y1, z1 ## print x1,y1,z1 # use glScale to compensate for zoom * scale in _setup_projection, # for error in PIXELS, and for want_depth != cov_depth x1wish = glpane.width / 2.0 * PIXELS # / 2.0 because in these coords, screen center indeed has x == y == 0 r = x1 / x1wish glScale(r, r, r) ## x1 /= r ## y1 /= r z1 /= r # now the following might work except for z, so fix z here glTranslatef( 0.0, 0.0, z1) del x1, y1 # not presently used if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: r = %r, translated z by z1 == %r" % (r, z1) # I don't think we need to usage-track glpane height & width (or scale or zoomFactor etc) # since we'll redraw when those change, and redo this calc every time we draw. # The only exception would be if we're rendering into a display list. # I don't know if this code (gluUnProject) would even work in that case. # [I think I wrote a similar comment in some other file earlier today. #k] # move to desired corner, and align it with same corner of lbox # (#e could use an alignment prim for the corner if we had one) if corner in corner_abbrevs: # normalize how corner is expressed, into a 2-tuple of +-1's corner = corner_abbrevs[corner] x, y = corner if x == -1: # left x_offset = - glpane.width / 2.0 * PIXELS + delegate.bleft elif x == +1: # right x_offset = + glpane.width / 2.0 * PIXELS - delegate.bright elif x == 0: # center(x) x_offset = 0 # note: before 070210 this was (+ delegate.bleft - delegate.bright) / 2.0, # which has an unwanted (since unavoidable) centering effect; use explicit Center if desired. else: print "invalid corner",corner### raise ValueError, "invalid corner %r" % (corner,) if y == -1: # bottom y_offset = - glpane.height / 2.0 * PIXELS + delegate.bbottom elif y == +1: # top y_offset = + glpane.height / 2.0 * PIXELS - delegate.btop elif y == 0: # center(y) y_offset = 0 # note: # note: before 070210 this was (+ delegate.bbottom - delegate.btop) / 2.0 else: print "invalid corner",corner### raise ValueError, "invalid corner %r" % (corner,) offset = (x_offset, y_offset) glTranslatef(offset[0], offset[1], 0.0) if _DEBUG_SAVED_STUFF: print "_DEBUG_SAVED_STUFF: offset =", offset self.drawkid( delegate) ## delegate.draw() finally: glMatrixMode(GL_MODELVIEW) # not needed glPopMatrix() return
def renderTextAtPosition(self, position, textString, textColor = black, textFont = None, fontSize = 11, ): """ Renders the text at the specified position (x, y, z coordinates) @param position: The x, y, z coordinates of the object at which the text needs to be rendered. @type position: B{A} @param textString: the text to be rendered at the specified position. @type textString : str @see: self.renderTextNearCursor() This method is different than that method. That method uses QPoint (the current cursor position) to render the text (thus needs integers x and y) whereas this method uses the actual object coordinates @see: MultiplednaSegment_GraphicsMode._drawDnaRubberbandLine() [obsolete class name, what is correct one?] @see: QGLWidget.renderText() @TODO: refactor to move the common code in this method and self.renderTextNearCursor(). """ if textFont is not None: font = textFont else: font = self._getFontForTextNearCursor(fontSize = fontSize, isBold = True) x = position[0] y = position[1] z = position[2] glDisable(GL_LIGHTING) #Convert the object coordinates to the window coordinates. wX, wY, wZ = gluProject(x, y, z) halo_color = getTextHaloColor(textColor) offset_val = 1 bg_z_offset = 0 fg_z_offset = -1e-7 render_positions = (( offset_val, offset_val, bg_z_offset, halo_color), (-offset_val, -offset_val, bg_z_offset, halo_color), (-offset_val, offset_val, bg_z_offset, halo_color), ( offset_val, -offset_val, bg_z_offset, halo_color), ( 0, 0, fg_z_offset, textColor)) for dx, dy, dz, color in render_positions: x1, y1, z1 = gluUnProject(wX + dx, wY + dy, wZ + dz) self.qglColor(RGBf_to_QColor(color)) self.renderText(x1, y1, z1, QString(textString), font) ## self.qglClearColor(RGBf_to_QColor(color)) ## # question: is this related to glClearColor? [bruce 071214 question] ## # -- yes [Ninad 2008-08-20] glEnable(GL_LIGHTING)
def model_coords(self,x,y): """given an (x,y) coordinate pair on the viewport, returns the coordinates in model space""" tz = gluProject(self._look_at[0],self._look_at[1], self._look_at[2])[2] # retrieve the depth coordinate return array(gluUnProject(x,self.get_view_height()-y,tz))
def select(self, wX, wY): """ Use the OpenGL picking/selection to select any object. Return the selected object, otherwise, return None. Restore projection and modelview matrices before returning. """ ### NOTE: this code is similar to (and was copied and modified from) # GLPane_highlighting_methods.do_glselect_if_wanted, but also differs # in significant ways (too much to make it worth merging, unless we # decide to merge the differing algorithms as well). It's one of # several instances of hit-test code that calls glRenderMode. # [bruce 060721/080917 comment] wZ = glReadPixelsf(wX, wY, 1, 1, GL_DEPTH_COMPONENT) gz = wZ[0][0] if gz >= GL_FAR_Z: # Empty space was clicked return None pxyz = A(gluUnProject(wX, wY, gz)) pn = self.out pxyz -= 0.0002 * pn # Note: if this runs before the model is drawn, this can have an # exception "OverflowError: math range error", presumably because # appropriate state for gluUnProject was not set up. That doesn't # normally happen but can happen due to bugs (no known open bugs # of that kind). # Sometimes our drawing area can become "stuck at gray", # and when that happens, the same exception can occur from this line. # Could it be that too many accidental mousewheel scrolls occurred # and made the scale unreasonable? (To mitigate, we should prevent # those from doing anything unless we have a valid model, and also # reset that scale when loading a new model (latter is probably # already done, but I didn't check). See also the comments # in def wheelEvent.) [bruce 080220 comment] dp = -dot(pxyz, pn) # Save projection matrix before it's changed. glMatrixMode(GL_PROJECTION) glPushMatrix() current_glselect = (wX, wY, 1, 1) self._setup_projection(glselect=current_glselect) glSelectBuffer(self.SIZE_FOR_glSelectBuffer) glRenderMode(GL_SELECT) glInitNames() glMatrixMode(GL_MODELVIEW) # Save model view matrix before it's changed. glPushMatrix() # Draw model using glRenderMode(GL_SELECT) as set up above try: glClipPlane(GL_CLIP_PLANE0, (pn[0], pn[1], pn[2], dp)) glEnable(GL_CLIP_PLANE0) self._drawModel_using_DrawingSets() except: #bruce 080917 fixed predicted bugs in this except clause (untested) print_compact_traceback( "exception in ThumbView._drawModel_using_DrawingSets() during GL_SELECT; ignored; restoring matrices: " ) glDisable(GL_CLIP_PLANE0) glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glPopMatrix() glRenderMode(GL_RENDER) return None else: glDisable(GL_CLIP_PLANE0) # Restore model/view matrix glPopMatrix() # Restore projection matrix and set matrix mode to Model/View glMatrixMode(GL_PROJECTION) glPopMatrix() glMatrixMode(GL_MODELVIEW) glFlush() hit_records = list(glRenderMode(GL_RENDER)) ## print "%d hits" % len(hit_records) for (near, far, names) in hit_records: ## print "hit record: near, far, names:", near, far, names # note from testing: near/far are too far apart to give actual depth, # in spite of the 1-pixel drawing window (presumably they're vertices # taken from unclipped primitives, not clipped ones). ### REVIEW: this just returns the first candidate object found. # The clip plane may restrict the set of candidates well enough to # make sure that's the right one, but this is untested and unreviewed. # (And it's just my guess that that was Huaicai's intention in # setting up clipping, since it's not documented. I'm guessing that # the plane is just behind the hitpoint, but haven't confirmed this.) # [bruce 080917 comment] if names: name = names[-1] assy = self.assy obj = assy and assy.object_for_glselect_name(name) #k should always return an obj return obj return None # from ThumbView.select
def screenToReal(self, x, y): x, y, z = gluUnProject(x, self.size[1] - y, 0) return x,y