class RectAlignmentControllerView(BaseToolController, GenModel): changed = QtCore.Signal() def __init__(self, parent, model): super(RectAlignmentControllerView, self).__init__() self._parent = parent self.model_overall = model self.model = model.ra self.__init_interaction() self.gls = None self.active = False idx_handle_sel = mdlacc(None) idx_handle_hover = mdlacc(None) #sel_mode = mdlacc(SEL_MODE_NONE) behave_mode = mdlacc(MODE_NONE) ghost_handle = mdlacc(None) def change(self): self.changed.emit() def __init_interaction(self): # Selection / drag handling self.idx_handle_sel = None #self.sel_mode = SEL_MODE_NONE self.behave_mode = MODE_NONE # ghost handle for showing placement self.ghost_handle = None def initialize(self): self.__init_interaction() self.active = True def finalize(self): self.active = False def initializeGL(self, gls): self.gls = gls # Basic solid-color program self.prog = self.gls.shader_cache.get("vert2", "frag1") self.mat_loc = GL.glGetUniformLocation(self.prog, "mat") self.col_loc = GL.glGetUniformLocation(self.prog, "color") # Build a VBO for rendering square "drag-handles" self.vbo_handles_ar = numpy.ndarray(4, dtype=[("vertex", numpy.float32, 2)]) self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_handles = VAO() with self.vbo_handles, self.vao_handles: vbobind(self.prog, self.vbo_handles_ar.dtype, "vertex").assign() # Build a VBO/VAO for the perimeter # We don't initialize it here because it is updated every render # 4 verticies for outside perimeter # 6 verticies for each dim self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)]) self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_per_dim = VAO() with self.vao_per_dim, self.vbo_per_dim: vbobind(self.prog, self.vbo_per_dim_ar.dtype, "vertex").assign() def im2V(self, pt): """Translate Image coordinates to viewport coordinates""" if self.model_overall.view_mode: ph = projectPoint(self.model.image_matrix, pt) return self.viewState.tfW2V(ph) else: return self.viewState.tfW2V(pt) def V2im(self, pt): """ Translate viewport coordinates to image coordinates :param pt: :return: """ world = self.viewState.tfV2W(pt) if self.model_overall.view_mode: inv = numpy.linalg.inv(self.model.image_matrix) return projectPoint(inv, world) else: return Vec2(world) #inv = numpy.linalg.inv(self.model.image_matrix) #return Vec2(inv.dot(pt)[:2]) def gen_dim(self, idx, always_above = True): """ Generate rendering data for the dimension-lines :param idx: :return: """ a = self.im2V(self.model.dim_handles[0 + idx]) b = self.im2V(self.model.dim_handles[1 + idx]) d = b-a delta = (b-a).norm() normal = Point2(rotate(math.pi / 2)[:2,:2].dot(delta)) if always_above: if numpy.cross(Vec2(1,0), normal) > 0: normal = -normal res = numpy.array([ a + normal * 8, a + normal * 20, a + normal * 15, b + normal * 15, b + normal * 8, b + normal * 20, ]) return res def render(self, vs): self.viewState = vs disabled = not self.active or self.model_overall.view_mode # Perimeter is defined by the first 4 handles self.vbo_per_dim_ar["vertex"][:4] = [self.im2V(pt) for pt in self.model.align_handles[:4]] # Generate the dimension lines. For ease of use, we always draw the dim-lines above when dims are manual # or below when dims are unlocked self.vbo_per_dim_ar["vertex"][4:10] = self.gen_dim(0, not self.model.dims_locked) self.vbo_per_dim_ar["vertex"][10:16] = self.gen_dim(2, not self.model.dims_locked) self.vbo_per_dim.set_array(self.vbo_per_dim_ar) # Ugh..... PyOpenGL isn't smart enough to bind the data when it needs to be copied with self.vbo_per_dim: self.vbo_per_dim.copy_data() GL.glDisable(GL.GL_BLEND) # ... and draw the perimeter with self.vao_per_dim, self.prog: GL.glUniformMatrix3fv(self.mat_loc, 1, True, self.viewState.glWMatrix.astype(numpy.float32)) # Draw the outer perimeter if disabled: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0.8, 1) else: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0, 1) GL.glDrawArrays(GL.GL_LINE_LOOP, 0, 4) # Draw the dimensions GL.glUniform4f(self.col_loc, 0.8, 0.0, 0.0, 1) GL.glDrawArrays(GL.GL_LINES, 4, 6) GL.glUniform4f(self.col_loc, 0.0, 0.0, 0.8, 1) GL.glDrawArrays(GL.GL_LINES, 10, 6) if disabled: return # Now draw a handle at each corner with self.vao_handles, self.prog: for n, i in enumerate(self.model.align_handles): # skip nonexistent handles if i is None: continue is_anchor = IDX_IS_ANCHOR(n) corner_pos = self.im2V(i) if disabled: color = [0.8, 0.8, 0.8, 1] elif self.idx_handle_sel == n: color = [1, 1, 1, 1] elif self.idx_handle_hover == n: color = [1, 1, 0, 1] else: color = [0.8, 0.8, 0, 0.5] self.render_handle(corner_pos, color, is_anchor, True) if self.idx_handle_sel == n: self.render_handle(corner_pos, [0,0,0,1], is_anchor, False) if self.ghost_handle is not None: self.render_handle(self.ghost_handle,[0.8, 0.8, 0, 0.5], True) if not self.model.dims_locked: for n, i in enumerate(self.model.dim_handles): handle_pos = self.im2V(i) if n == self.idx_handle_sel: color = [1, 1, 1, 1] if n < 2: color = [0.8, 0.0, 0.0, 1] else: color = [0.0, 0.0, 0.8, 1] self.render_handle(handle_pos, color, False, True) if self.idx_handle_sel == n: self.render_handle(corner_pos, [0,0,0,1], is_anchor, False) def render_handle(self, position, color, diagonal=False, filled=False): if diagonal: r = rotate(math.pi/4) else: r = numpy.identity(3) m = self.viewState.glWMatrix.dot(translate(*position).dot(r)) GL.glUniformMatrix3fv(self.mat_loc, 1, True, m.astype(numpy.float32)) GL.glUniform4f(self.col_loc, *color) GL.glDrawArrays(GL.GL_TRIANGLE_FAN if filled else GL.GL_LINE_LOOP, 0, 4) def get_handle_for_mouse(self, pos): for n, handle in enumerate(self.model.all_handles()): if handle is None: continue # get the pix-wise BBOX of the handle p = self.im2V(handle) r = QtCore.QRect(p[0], p[1], 0, 0) r.adjust(-HANDLE_HALF_SIZE, -HANDLE_HALF_SIZE, HANDLE_HALF_SIZE, HANDLE_HALF_SIZE) # If event inside the bbox if r.contains(pos): return n return None def get_line_query_for_mouse(self, pos): for n, (p1, p2) in enumerate(self.model.line_iter()): p1_v = self.im2V(p1) p2_v = self.im2V(p2) p, d = project_point_line(pos, p1_v, p2_v) if p is not None and d < HANDLE_HALF_SIZE: return n, p return None, None def mousePressEvent(self, event): disabled = not self.active or self.model_overall.view_mode if disabled: return False handle = self.get_handle_for_mouse(event.pos()) if event.button() == QtCore.Qt.LeftButton and event.modifiers() & ADD_MODIFIER: idx, p = self.get_line_query_for_mouse(event.pos()) if idx is not None: anchors = self.model.get_anchors(idx) if len(anchors) < 2: p = self.V2im(p) idx = new_anchor_index(self.model, idx) cmd = cmd_set_handle_position(self.model, idx, p) self._parent.undoStack.push(cmd) self.idx_handle_sel = idx self.idx_handle_hover = None elif event.button() == QtCore.Qt.LeftButton and event.modifiers() & DEL_MODIFIER and ( handle is not None and handle >= 4): cmd = cmd_set_handle_position(self.model, handle, None) self._parent.undoStack.push(cmd) #self.model.set_handle(handle, None) self.idx_handle_sel = None self.idx_handle_hover = None elif event.button() == QtCore.Qt.LeftButton: self.idx_handle_sel = handle self.idx_handle_hover = None if handle is not None: self.behave_mode = MODE_DRAGGING else: return False return True def mouseReleaseEvent(self, event): disabled = not self.active or self.model_overall.view_mode if disabled: return False if event.button() == QtCore.Qt.LeftButton and self.behave_mode == MODE_DRAGGING: self.behave_mode = MODE_NONE else: return False return True def mouseMoveEvent(self, event): disabled = not self.active or self.model_overall.view_mode if disabled: return False needs_update = False idx = self.get_handle_for_mouse(event.pos()) if self.ghost_handle is not None: self.ghost_handle = None if self.behave_mode == MODE_NONE: if idx is not None: self.idx_handle_hover = idx else: self.idx_handle_hover = None if event.modifiers() & ADD_MODIFIER: line_idx, pos = self.get_line_query_for_mouse(event.pos()) if line_idx is not None: self.ghost_handle = pos elif self.behave_mode == MODE_DRAGGING: w_pos = self.V2im(Vec2(event.pos())) cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, w_pos, merge=True) self._parent.undoStack.push(cmd) #self.model.set_handle(self.idx_handle_sel, w_pos) return False def focusOutEvent(self, evt): self.idx_handle_sel = None def keyPressEvent(self, evt): disabled = not self.active or self.model_overall.view_mode if disabled: return False if evt.key() == QtCore.Qt.Key_Escape: self.idx_handle_sel = None elif self.idx_handle_sel is not None: if evt.key() in (QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace) and IDX_IS_ANCHOR(self.idx_handle_sel): cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, None) self._parent.undoStack.push(cmd) #self.model.set_handle(self.idx_handle_sel, None) self.idx_handle_sel = None # Basic 1-px nudging elif evt.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down): nudge = { QtCore.Qt.Key_Left: (-1, 0), QtCore.Qt.Key_Right: ( 1, 0), QtCore.Qt.Key_Up: ( 0, -1), QtCore.Qt.Key_Down: ( 0, 1), }[evt.key()] current = Vec2(self.im2V(self.model.align_handles[self.idx_handle_sel])) viewspace = self.V2im(current + nudge) cmd = cmd_set_handle_position(self.model, self.idx_handle_sel, viewspace) self._parent.undoStack.push(cmd) #self.model.set_handle(self.idx_handle_sel, viewspace) self.ghost_handle = None def next_prev_child(self, next): all_handles = self.model.all_handles() step = -1 if next: step = 1 if self.behave_mode == MODE_DRAGGING: return True else: idx = self.idx_handle_sel if idx == None: return False while 1: idx = (idx + step) % len(all_handles) if all_handles[idx] is not None: self.idx_handle_sel = idx return True
class RectAlignmentControllerView(BaseToolController): changed = QtCore.Signal() def __init__(self, parent, model: 'pcbre.ui.dialogs.layeralignmentdialog.dialog.AlignmentViewModel'): super(RectAlignmentControllerView, self).__init__() self._parent = parent self.model_overall = model self.model = model.ra self.__idx_handle_sel: Optional[int] = None self.__idx_handle_hover: Optional[int] = None self.__behave_mode = DM.NONE self.__ghost_handle = None self.__init_interaction() self.gls: Optional[Any] = None self.active = False def change(self) -> None: self.changed.emit() @property def tool_actions(self) -> 'Sequence[ToolActionDescription]': return g_ACTIONS def __init_interaction(self) -> None: # Selection / drag handling self.__idx_handle_sel = None self.__behave_mode = DM.NONE # ghost handle for showing placement self.__ghost_handle = None def initialize(self) -> None: self.__init_interaction() self.active = True def finalize(self) -> None: self.active = False def initializeGL(self, gls: 'GLShared') -> None: self.gls = gls assert self.gls is not None # Basic solid-color program self.prog = self.gls.shader_cache.get("vert2", "frag1") self.mat_loc = GL.glGetUniformLocation(self.prog.program, "mat") self.col_loc = GL.glGetUniformLocation(self.prog.program, "color") # Build a VBO for rendering square "drag-handles" self.vbo_handles_ar = numpy.ndarray((4, ), dtype=[("vertex", numpy.float32, 2)]) self.vbo_handles_ar["vertex"] = numpy.array(corners) * HANDLE_HALF_SIZE self.vbo_handles = VBO(self.vbo_handles_ar, GL.GL_STATIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_handles = VAO() with self.vbo_handles, self.vao_handles: VBOBind(self.prog.program, self.vbo_handles_ar.dtype, "vertex").assign() # Build a VBO/VAO for the perimeter # We don't initialize it here because it is updated every render # 4 verticies for outside perimeter # 6 verticies for each dim self.vbo_per_dim_ar = numpy.zeros(16, dtype=[("vertex", numpy.float32, 2)]) self.vbo_per_dim = VBO(self.vbo_per_dim_ar, GL.GL_DYNAMIC_DRAW, GL.GL_ARRAY_BUFFER) self.vao_per_dim = VAO() with self.vao_per_dim, self.vbo_per_dim: VBOBind(self.prog.program, self.vbo_per_dim_ar.dtype, "vertex").assign() def im2V(self, pt: Vec2) -> Vec2: """Translate Image coordinates to viewport coordinates""" if self.model_overall.view_mode.is_aligned(): ph = project_point(self.model.image_matrix, pt) return self.viewPort.tfW2V(ph) else: return self.viewPort.tfW2V(pt) def V2im(self, pt: Vec2) -> Vec2: """ Translate viewport coordinates to image coordinates :param pt: :return: """ world = self.viewPort.tfV2W(pt) if self.model_overall.view_mode.is_aligned(): inv = numpy.linalg.inv(self.model.image_matrix) return project_point(inv, world) else: return world # inv = numpy.linalg.inv(self.model.image_matrix) # return Vec2(inv.dot(pt)[:2]) def gen_dim(self, idx: int, always_above: bool = True) -> 'npt.NDArray[numpy.float64]': """ Generate rendering data for the dimension-lines :param idx: :return: """ a = self.im2V(self.model.dim_handles[0 + idx]) b = self.im2V(self.model.dim_handles[1 + idx]) d = b - a delta = (b - a).norm() normal = Vec2.from_mat(rotate(math.pi / 2)[:2, :2].dot(delta)) if always_above: if numpy.cross(Vec2(1, 0), normal) > 0: normal = -normal res = numpy.array([ a + normal * 8, a + normal * 20, a + normal * 15, b + normal * 15, b + normal * 8, b + normal * 20, ], dtype=numpy.float64) return res @property def disabled(self) -> bool: # When the we're showing the aligned view; or if we are using keypoint align # don't render any handles return not self.active or self.model_overall.view_mode.is_aligned() def render(self, viewPort: 'ViewPort') -> None: self.viewPort = viewPort # Perimeter is defined by the first 4 handles self.vbo_per_dim_ar["vertex"][:4] = [self.im2V(pt) for pt in self.model.align_handles[:4]] # Generate the dimension lines. For ease of use, we always draw the dim-lines above when dims are manual # or below when dims are unlocked self.vbo_per_dim_ar["vertex"][4:10] = self.gen_dim(0, not self.model.dims_locked) self.vbo_per_dim_ar["vertex"][10:16] = self.gen_dim(2, not self.model.dims_locked) self.vbo_per_dim.set_array(self.vbo_per_dim_ar) # Ugh..... PyOpenGL isn't smart enough to bind the data when it needs to be copied with self.vbo_per_dim: self.vbo_per_dim.copy_data() GL.glDisable(GL.GL_BLEND) # ... and draw the perimeter with self.vao_per_dim, self.prog.program: GL.glUniformMatrix3fv(self.mat_loc, 1, True, self.viewPort.glWMatrix.astype(numpy.float32)) # Draw the outer perimeter if self.disabled: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0.8, 1) else: GL.glUniform4f(self.col_loc, 0.8, 0.8, 0, 1) GL.glDrawArrays(GL.GL_LINE_LOOP, 0, 4) # Draw the dimensions GL.glUniform4f(self.col_loc, 0.8, 0.0, 0.0, 1) GL.glDrawArrays(GL.GL_LINES, 4, 6) GL.glUniform4f(self.col_loc, 0.0, 0.0, 0.8, 1) GL.glDrawArrays(GL.GL_LINES, 10, 6) if self.disabled: return # Now draw a handle at each corner with self.vao_handles, self.prog.program: for n, i in enumerate(self.model.align_handles): # skip nonexistent handles if i is None: continue is_anchor = IDX_IS_LINE(n) corner_pos = self.im2V(i) if self.disabled: color = [0.8, 0.8, 0.8, 1] elif self.__idx_handle_sel == n: color = [1, 1, 1, 1] elif self.__idx_handle_hover == n: color = [1, 1, 0, 1] else: color = [0.8, 0.8, 0, 0.5] self.render_handle(corner_pos, color, is_anchor, True) if self.__idx_handle_sel == n: self.render_handle(corner_pos, [0, 0, 0, 1], is_anchor, False) if self.__ghost_handle is not None: self.render_handle(self.__ghost_handle, [0.8, 0.8, 0, 0.5], True) if not self.model.dims_locked: for n, i in enumerate(self.model.dim_handles): handle_pos = self.im2V(i) if n == self.__idx_handle_sel: color = [1, 1, 1, 1] if n < 2: color = [0.8, 0.0, 0.0, 1] else: color = [0.0, 0.0, 0.8, 1] self.render_handle(handle_pos, color, False, True) if self.__idx_handle_sel == n: self.render_handle(corner_pos, [0, 0, 0, 1], is_anchor, False) def render_handle(self, position: Vec2, color: Sequence[float], diagonal: bool = False, filled: bool = False) -> None: if diagonal: r = rotate(math.pi / 4) else: r = numpy.identity(3) m = self.viewPort.glWMatrix.dot(translate(*position).dot(r)) GL.glUniformMatrix3fv(self.mat_loc, 1, True, m.astype(numpy.float32)) GL.glUniform4f(self.col_loc, *color) GL.glDrawArrays(GL.GL_TRIANGLE_FAN if filled else GL.GL_LINE_LOOP, 0, 4) def get_handle_index_for_mouse(self, pos: Vec2) -> Optional[int]: """ Returns the index of a handle (or None if one isn't present) given a MoveEvent""" for n, handle in enumerate(self.model.all_handles()): if handle is None: continue # get the pix-wise BBOX of the handle p = self.im2V(handle) # Rect encompassing the handle r = Rect.from_center_size(p, HANDLE_HALF_SIZE * 2, HANDLE_HALF_SIZE * 2) # If event inside the bbox if r.point_test(pos) != 0: return n return None def get_line_query_for_mouse(self, pos: Vec2) -> Tuple[Optional[int], Optional[Vec2]]: for n, (p1, p2) in enumerate(self.model.lines()): p1_v = self.im2V(p1) p2_v = self.im2V(p2) p, d = project_point_line(pos, p1_v, p2_v) if d is not None and d < HANDLE_HALF_SIZE: return n, p return None, None def event_add_constraint(self, event: 'ToolActionEvent') -> None: idx, p = self.get_line_query_for_mouse(event.cursor_pos) if idx is not None: anchors = self.model.get_anchors(idx) if len(anchors) < 2: p = self.V2im(p) idx = new_anchor_index(self.model, idx) cmd = cmd_set_handle_position(self.model, idx, p) self._parent.undoStack.push(cmd) self.__idx_handle_sel = idx self.__idx_handle_hover = None def event_remove_constraint(self, event: 'ToolActionEvent') -> None: handle = self.get_handle_index_for_mouse(event.cursor_pos) if handle is not None and handle >= 4: cmd = cmd_set_handle_position(self.model, handle, None) self._parent.undoStack.push(cmd) self.__idx_handle_sel = None self.__idx_handle_hover = None def event_select(self, event: 'ToolActionEvent') -> None: handle = self.get_handle_index_for_mouse(event.cursor_pos) self.__idx_handle_sel = handle self.__idx_handle_hover = None if handle is not None: self.__behave_mode = DM.DRAGGING def event_release(self, event: 'ToolActionEvent') -> None: if self.__behave_mode == DM.DRAGGING: self.__behave_mode = DM.NONE def tool_event(self, event: 'ToolActionEvent') -> None: if self.disabled: return if event.code == EventCode.Select: self.event_select(event) elif event.code == EventCode.Release: self.event_release(event) elif event.code == EventCode.AddToLine: self.event_add_constraint(event) elif event.code == EventCode.RemoveFromLine: self.event_remove_constraint(event) def mouseMoveEvent(self, event: 'MoveEvent') -> None: if self.disabled: return needs_update = False idx = self.get_handle_index_for_mouse(event.cursor_pos) if self.__ghost_handle is not None: self.__ghost_handle = None if self.__behave_mode == DM.NONE: if idx is not None: self.__idx_handle_hover = idx else: self.__idx_handle_hover = None # if event.modifiers() & ADD_MODIFIER: # line_idx, pos = self.get_line_query_for_mouse(event.cursor_pos) # if line_idx is not None: # self.__ghost_handle = pos elif self.__behave_mode == DM.DRAGGING: w_pos = self.V2im(event.cursor_pos) # print(w_pos, event.world_pos) cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, w_pos, merge=True) self._parent.undoStack.push(cmd) def focusOutEvent(self, evt: QtCore.QEvent) -> None: self.__idx_handle_sel = None def keyPressEvent(self, evt: QtGui.QKeyEvent) -> bool: if self.disabled: return False if evt.key() == QtCore.Qt.Key_Escape: self.__idx_handle_sel = None elif self.__idx_handle_sel is not None: if evt.key() in (QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace) and IDX_IS_LINE(self.__idx_handle_sel): cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, None) self._parent.undoStack.push(cmd) # self.model.set_handle(self.__idx_handle_sel, None) self.__idx_handle_sel = None # Basic 1-px nudging elif evt.key() in (QtCore.Qt.Key_Left, QtCore.Qt.Key_Right, QtCore.Qt.Key_Up, QtCore.Qt.Key_Down): _nudgetab: Dict[int,Tuple[int, int]] = { QtCore.Qt.Key_Left: (-1, 0), QtCore.Qt.Key_Right: (1, 0), QtCore.Qt.Key_Up: (0, -1), QtCore.Qt.Key_Down: (0, 1), } nudge = _nudgetab[evt.key()] current = self.im2V(self.model.align_handles[self.__idx_handle_sel]) viewspace = self.V2im(current + Vec2.from_mat(nudge)) cmd = cmd_set_handle_position(self.model, self.__idx_handle_sel, viewspace) self._parent.undoStack.push(cmd) # self.model.set_handle(self.__idx_handle_sel, viewspace) self.__ghost_handle = None def next_prev_child(self, next: bool) -> bool: all_handles = self.model.all_handles() step = -1 if next: step = 1 if self.__behave_mode == DM.DRAGGING: return True else: idx = self.__idx_handle_sel if idx is None: return False while 1: idx = (idx + step) % len(all_handles) if all_handles[idx] is not None: self.__idx_handle_sel = idx return True
# disable cursor glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED) glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) glfw.set_scroll_callback(theWindow, window_scroll_callback) vbo = VBO(vertices, 'GL_STATIC_DRAW') vbo.create_buffers() vao = glGenVertexArrays(1) glBindVertexArray(vao) vbo.bind() vbo.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glBindVertexArray(0) # compile program renderProgram = GLProgram(rayTracingVertexShaderSource, rayTracingFragmentShaderSource) renderProgram.compile_and_link() uniformInfos = [ ('backColor', 'vec3f'), ('ambientColor', 'vec3f'), ('o_c', 'vec3f'), ('o_p', 'vec3f'), ('x_c', 'vec3f'), ('y_c', 'vec3f'), ('x_p', 'vec3f'),
glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED) glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) glfw.set_scroll_callback(theWindow, window_scroll_callback) sphereDataVBO = VBO(sphereData, usage='GL_STATIC_DRAW') sphereDataVBO.create_buffers() sphereVAO = glGenVertexArrays(1) glBindVertexArray(sphereVAO) sphereDataVBO.bind() sphereDataVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) sphereDataVBO.unbind() glBindVertexArray(0) cylinderDataVBO = VBO(cylinderData, usage='GL_STATIC_DRAW') cylinderDataVBO.create_buffers() cylinderVAO = glGenVertexArrays(1)
glfw.set_framebuffer_size_callback(theWindow, window_resize_callback) # create VBO to store vertices verticesVBO = VBO(triangleVertices, usage='GL_STATIC_DRAW') verticesVBO.create_buffers() # create VAO to describe array information triangleVAO = glGenVertexArrays(1) # bind VAO glBindVertexArray(triangleVAO) # bind VBO verticesVBO.bind() # buffer data into OpenGL verticesVBO.copy_data() # configure the fist 3-vector (pos) # arguments: index, size, type, normalized, stride, pointer # the stride is 6 * 4 because there are six floats per vertex, and the size of # each float is 4 bytes glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(0)) glEnableVertexAttribArray(0) # configure the second 3-vector (color) # the offset is 3 * 4 = 12 bytes glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * 4, ctypes.c_void_p(3 * 4)) glEnableVertexAttribArray(1) # unbind VBO
glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) glfw.set_scroll_callback(theWindow, window_scroll_callback) vertexVBO = VBO(vertices, usage='GL_STATIC_DRAW') vertexVBO.create_buffers() textureVBO = VBO(textureVertices, usage='GL_STATIC_DRAW') textureVBO.create_buffers() frameVAO = glGenVertexArrays(1) glBindVertexArray(frameVAO) vertexVBO.bind() vertexVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glBindVertexArray(0) textureVAO = glGenVertexArrays(1) glBindVertexArray(textureVAO) textureVBO.bind() textureVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
# create window theWindow = glfw.create_window(windowSize[0], windowSize[1], 'Audio Oscilloscope', None, None) # make window the current context glfw.make_context_current(theWindow) # enable z-buffer glEnable(GL_DEPTH_TEST) dataVBO = VBO(dataBuffer, usage='GL_STATIC_DRAW') dataVAO = glGenVertexArrays(1) glBindVertexArray(dataVAO) dataVBO.bind() dataVBO.copy_data() glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) dataVBO.unbind() glBindVertexArray(0) renderProgram = GLProgram(waveVertexShaderSource, waveFragmentShaderSource) renderProgram.compile_and_link() waveColorUniform = GLUniform(renderProgram.get_program_id(), 'waveColor', 'vec3f') # change drawing mode # glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
# create VBOs to store vertices, normals and elements cubeDataVBO = VBO(cubeData, usage='GL_STATIC_DRAW') cubeDataVBO.create_buffers() sphereDataVBO = VBO(sphereData, usage='GL_STATIC_DRAW') sphereDataVBO.create_buffers() # create VAO to describe array information triangleVAO, sphereVAO = glGenVertexArrays(2) # bind VAO glBindVertexArray(triangleVAO) # bind data VBO cubeDataVBO.bind() cubeDataVBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) # unbind VBO cubeDataVBO.unbind() # unbind VAO glBindVertexArray(0) glBindVertexArray(sphereVAO) sphereDataVBO.bind()
def image_server(evtQueue, resultQueue): # resource-taking objects resObjs = [] # initialize glfw glfw.init() # set glfw config glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) glfw.window_hint(glfw.RESIZABLE, GL_FALSE) if pyPlatform.system().lower() == 'darwin': glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, GL_TRUE) # create window theWindow = glfw.create_window(windowSize[0], windowSize[1], 'Spherical Projection', None, None) # make window the current context glfw.make_context_current(theWindow) # enable z-buffer glEnable(GL_DEPTH_TEST) # set resizing callback function # glfw.set_framebuffer_size_callback(theWindow, window_resize_callback) #glfw.set_key_callback(theWindow, window_keypress_callback) # disable cursor #glfw.set_input_mode(theWindow, glfw.CURSOR, glfw.CURSOR_DISABLED) #glfw.set_cursor_pos_callback(theWindow, window_cursor_callback) # initialize cursor position cursorPos = glfw.get_cursor_pos(theWindow) # glfw.set_scroll_callback(theWindow, window_scroll_callback) vbo = VBO(vertices, 'GL_STATIC_DRAW') vbo.create_buffers() resObjs.append(vbo) vao = glGenVertexArrays(1) glBindVertexArray(vao) vbo.bind() vbo.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glBindVertexArray(0) # compile program renderProgram = GLProgram(rayTracingVertexShaderSource, rayTracingFragmentShaderSource) renderProgram.compile_and_link() uniformInfos = [ ('backColor', 'vec3f'), ('ambientColor', 'vec3f'), ('o_c', 'vec3f'), ('o_p', 'vec3f'), ('x_c', 'vec3f'), ('y_c', 'vec3f'), ('x_p', 'vec3f'), ('y_p', 'vec3f'), ('c_c', 'vec3f'), ('c_p', 'vec3f'), ('winSize', 'vec2f') ] uniforms = create_uniform(renderProgram.get_program_id(), uniformInfos) # keep rendering until the window should be closed while not glfw.window_should_close(theWindow): # set background color glClearColor(*windowBackgroundColor) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) renderProgram.use() # update uniforms o_c, c_c, x_c, y_c = get_camera_vectors(camera) o_p, c_p, x_p, y_p = get_camera_vectors(projector) uniforms['o_c'].update(o_c) uniforms['x_c'].update(x_c) uniforms['y_c'].update(y_c) uniforms['c_c'].update(c_c) uniforms['o_p'].update(o_p) uniforms['x_p'].update(x_p) uniforms['y_p'].update(y_p) uniforms['c_p'].update(c_p) uniforms['backColor'].update(backColor) uniforms['ambientColor'].update(ambientColor) uniforms['winSize'].update(windowSize.astype(np.float32)) try: newImage = evtQueue.get(timeout=0.05) except Exception as e: # tell glfw to poll and process window events glfw.poll_events() # swap frame buffer glfw.swap_buffers(theWindow) continue texture = create_texture(newImage) glBindVertexArray(vao) glActiveTexture(GL_TEXTURE0) texture.bind() glDrawArrays(GL_TRIANGLES, 0, 6) texture.unbind() glBindVertexArray(0) texture.delete() # respond key press keyboard_respond_func() # tell glfw to poll and process window events glfw.poll_events() # swap frame buffer glfw.swap_buffers(theWindow) result = get_screenshot(windowSize) resultQueue.put(result) for obj in resObjs: obj.delete() # terminate glfw glfw.terminate()
cursorPos = glfw.get_cursor_pos(theWindow) glfw.set_scroll_callback(theWindow, window_scroll_callback) gridVBO = VBO(gridArray, usage='GL_DYNAMIC_DRAW') gridVBO.create_buffers() gridEBO = VBO(elementArray, usage='GL_STATIC_DRAW', target='GL_ELEMENT_ARRAY_BUFFER') gridEBO.create_buffers() gridVAO = glGenVertexArrays(1) glBindVertexArray(gridVAO) gridVBO.bind() gridVBO.copy_data() gridEBO.bind() gridEBO.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) glBindVertexArray(0) gridVBO.unbind() renderProgram = GLProgram(vertexShaderSource, fragmentShaderSource) renderProgram.compile_and_link()
class TextDrawer: def __init__(self): self.face = None self.textures = dict() # compile rendering program self.renderProgram = GLProgram(_textVertexShaderSource, _textFragmentShaderSource) self.renderProgram.compile_and_link() # make projection uniform self.projectionUniform = GLUniform(self.renderProgram.get_program_id(), 'projection', 'mat4f') self.textColorUniform = GLUniform(self.renderProgram.get_program_id(), 'textColor', 'vec3f') self.textureSizeUniform = GLUniform( self.renderProgram.get_program_id(), 'textureSize', 'vec2f') # create rendering buffer self.vbo = VBO(_get_rendering_buffer(0, 0, 0, 0)) self.vbo.create_buffers() self.vboId = glGenBuffers(1) # initialize VAO self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) glBindBuffer(GL_ARRAY_BUFFER, self.vboId) self.vbo.bind() self.vbo.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) # self.vbo.unbind() glBindVertexArray(0) self.zNear = -1.0 self.zFar = 1.0 def delete(self): self.textures.clear() self.face = None self.renderProgram.delete() self.projectionUniform = None self.textColorUniform = None self.textureSizeUniform = None self.vbo.delete() glDeleteVertexArrays(1, [self.vao]) def load_font(self, fontFilename, fontSize): assert os.path.exists(fontFilename) self.textures.clear() self.face = ft.Face(fontFilename) self.face.set_char_size(fontSize) # load all ASCII characters for i in range(128): self.load_character(chr(i)) def load_character(self, character): assert self.face is not None assert len(character) == 1 if character not in self.textures: # load glyph in freetype self.face.load_char(character) ftBitmap = self.face.glyph.bitmap height, width = ftBitmap.rows, ftBitmap.width bitmap = np.array(ftBitmap.buffer, dtype=np.uint8).reshape( (height, width)) # create texture texture = _create_text_texture(bitmap) # add texture object to the dictionary characterSlot = CharacterSlot(texture, self.face.glyph) self.textures[character] = characterSlot def get_character(self, ch): if ch not in self.textures: self.load_character(ch) return self.textures[ch] def _draw_text(self, text, textPos, windowSize, scale, linespread, foreColor): if len(text) == 0: return blendEnabled = glIsEnabled(GL_BLEND) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) self.renderProgram.use() glBindVertexArray(self.vao) glActiveTexture(GL_TEXTURE0) foreColor = np.asarray(foreColor, np.float32) self.textColorUniform.update(foreColor) projectionMat = orthographic_projection(0.0, windowSize[0], 0.0, windowSize[1], self.zNear, self.zFar) self.projectionUniform.update(projectionMat) lineY = textPos[1] nowX = textPos[0] # split text into lines lines = text.split('\n') for line in lines: if len(line) > 0: # analyze this line bearings = [] for ch in line: charSlot = self.get_character(ch) bearings.append(charSlot.bearing[1] * scale[1]) maxBearings = max(bearings) for ch in line: charSlot = self.get_character(ch) xpos = nowX + charSlot.bearing[0] * scale[0] yLowerd = (maxBearings - charSlot.bearing[1] * scale[1]) ypos = lineY - yLowerd w = charSlot.textureSize[0] * scale[0] h = charSlot.textureSize[1] * scale[1] self.textureSizeUniform.update(np.array((w, h), np.float32)) charSlot.texture.bind() self.vbo.bind() self.vbo.set_array( _get_rendering_buffer(xpos, ypos, w, h, 0.999)) self.vbo.copy_data() self.vbo.unbind() glDrawArrays(GL_TRIANGLES, 0, 6) charSlot.texture.unbind() # the advance is number of 1/64 pixels nowX += (charSlot.advance / 64.0) * scale[0] nowX = textPos[0] yOffset = self.get_character( 'X').textureSize[1] * scale[1] * linespread lineY -= yOffset glBindVertexArray(0) if not blendEnabled: glDisable(GL_BLEND) def draw_text(self, text, textPos, windowSize, color=(1.0, 1.0, 1.0), scale=(1.0, 1.0), linespread=1.5): return self._draw_text(text, textPos, windowSize, scale, linespread, color)
class TextDrawer_Outlined: def __init__(self): self.face = None self.stroker = None self.foreTextures = dict() self.backTextures = dict() # compile rendering program self.renderProgram = GLProgram(_textVertexShaderSource, _textFragmentShaderSource) self.renderProgram.compile_and_link() # make projection uniform self.projectionUniform = GLUniform(self.renderProgram.get_program_id(), 'projection', 'mat4f') self.textColorUniform = GLUniform(self.renderProgram.get_program_id(), 'textColor', 'vec3f') self.textureSizeUniform = GLUniform( self.renderProgram.get_program_id(), 'textureSize', 'vec2f') # create rendering buffer self.vbo = VBO(_get_rendering_buffer(0, 0, 0, 0)) self.vbo.create_buffers() self.vboId = glGenBuffers(1) # initialize VAO self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) glBindBuffer(GL_ARRAY_BUFFER, self.vboId) self.vbo.bind() self.vbo.copy_data() glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(0)) glEnableVertexAttribArray(0) glVertexAttribPointer( 1, 2, GL_FLOAT, GL_FALSE, 5 * ctypes.sizeof(ctypes.c_float), ctypes.c_void_p(3 * ctypes.sizeof(ctypes.c_float))) glEnableVertexAttribArray(1) # self.vbo.unbind() glBindVertexArray(0) self.zNear = -1.0 self.zFar = 1.0 def delete(self): self.foreTextures.clear() self.backTextures.clear() self.face = None self.stroker = None self.renderProgram.delete() self.projectionUniform = None self.textColorUniform = None self.textureSizeUniform = None self.vbo.delete() glDeleteVertexArrays(1, [self.vao]) def load_font(self, fontFilename, fontSize, outlineSize=2 * 64): assert os.path.exists(fontFilename) self.foreTextures.clear() self.backTextures.clear() self.face = ft.Face(fontFilename) self.face.set_char_size(fontSize) self.outlineSize = outlineSize self.stroker = ft.Stroker() self.stroker.set(outlineSize, ft.FT_STROKER_LINECAPS['FT_STROKER_LINECAP_ROUND'], ft.FT_STROKER_LINEJOINS['FT_STROKER_LINEJOIN_ROUND'], 0) # load all ASCII characters for i in range(128): self.load_character(chr(i)) def load_character(self, character): assert self.face is not None assert len(character) == 1 if character not in self.foreTextures: # load background glyph # the render option will lead to an outline glyph (not rendered) self.face.load_char(character, ft.FT_LOAD_FLAGS['FT_LOAD_DEFAULT']) backGlyph = ft.FT_Glyph() ft.FT_Get_Glyph(self.face.glyph._FT_GlyphSlot, ft.byref(backGlyph)) backGlyph = ft.Glyph(backGlyph) # add border to the glyph error = ft.FT_Glyph_StrokeBorder(ft.byref(backGlyph._FT_Glyph), self.stroker._FT_Stroker, False, False) if error: raise ft.FT_Exception(error) # the render option will lead to a rendered glyph backBitmapGlyph = backGlyph.to_bitmap( ft.FT_RENDER_MODES['FT_RENDER_MODE_NORMAL'], 0) backBitmap = backBitmapGlyph.bitmap backHeight, backWidth = backBitmap.rows, backBitmap.width backBitmap = np.array(backBitmap.buffer, dtype=np.uint8).reshape( (backHeight, backWidth)) backTexture = _create_text_texture(backBitmap) backSlot = CharacterSlot(backTexture, backBitmapGlyph) self.backTextures[character] = backSlot # load foreground glyph self.face.load_char(character, ft.FT_LOAD_FLAGS['FT_LOAD_RENDER']) foreBitmap = self.face.glyph.bitmap foreHeight, foreWidth = foreBitmap.rows, foreBitmap.width foreBitmap = np.array(foreBitmap.buffer, dtype=np.uint8).reshape( (foreHeight, foreWidth)) foreTexture = _create_text_texture(foreBitmap) foreSlot = CharacterSlot(foreTexture, self.face.glyph) self.foreTextures[character] = foreSlot def get_character(self, ch): if ch not in self.foreTextures: self.load_character(ch) return (self.foreTextures[ch], self.backTextures[ch]) def _draw_text(self, text, textPos, windowSize, scale, linespread, foreColor, backColor): if len(text) == 0: return blendEnabled = glIsEnabled(GL_BLEND) glEnable(GL_BLEND) glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) self.renderProgram.use() glBindVertexArray(self.vao) glActiveTexture(GL_TEXTURE0) foreColor = np.asarray(foreColor, np.float32) backColor = np.asarray(backColor, np.float32) projectionMat = orthographic_projection(0.0, windowSize[0], 0.0, windowSize[1], self.zNear, self.zFar) self.projectionUniform.update(projectionMat) lineY = textPos[1] nowX = textPos[0] # split text into lines lines = text.split('\n') for line in lines: if len(line) > 0: # analyze this line bearings = [] for ch in line: _, backSlot = self.get_character(ch) bearings.append(backSlot.bearing) minBearings_X = min(zip(*bearings), key=operator.itemgetter(0))[0] * scale[0] maxBearings_Y = max(zip(*bearings), key=operator.itemgetter(1))[1] * scale[1] nowX = -minBearings_X for ch in line: foreSlot, backSlot = self.get_character(ch) # draw the background xpos = nowX + backSlot.bearing[0] * scale[0] yLowerd = (maxBearings_Y - backSlot.bearing[1] * scale[1]) ypos = lineY - yLowerd w = backSlot.textureSize[0] * scale[0] h = backSlot.textureSize[1] * scale[1] self.textureSizeUniform.update(np.array((w, h), np.float32)) backSlot.texture.bind() self.textColorUniform.update(backColor) self.vbo.bind() self.vbo.set_array( _get_rendering_buffer(xpos, ypos, w, h, 0.99)) self.vbo.copy_data() self.vbo.unbind() glDrawArrays(GL_TRIANGLES, 0, 6) backSlot.texture.unbind() # draw the foreground xpos = nowX + foreSlot.bearing[0] * scale[0] yLowerd = (maxBearings_Y - foreSlot.bearing[1] * scale[1]) ypos = lineY - yLowerd w = foreSlot.textureSize[0] * scale[0] h = foreSlot.textureSize[1] * scale[1] foreSlot.texture.bind() self.textColorUniform.update(foreColor) self.vbo.bind() # the foreground is set closer to the screen so that it # is rendered above the background self.vbo.set_array( _get_rendering_buffer(xpos, ypos, w, h, 0.999)) self.vbo.copy_data() self.vbo.unbind() glDrawArrays(GL_TRIANGLES, 0, 6) foreSlot.texture.unbind() # the advance is number of 1/64 pixels nowX += ((foreSlot.advance + 2.0 * self.outlineSize) / 64.0) * scale[0] yOffset = self.get_character( 'X')[1].textureSize[1] * scale[1] * linespread lineY -= yOffset glBindVertexArray(0) if not blendEnabled: glDisable(GL_BLEND) def draw_text(self, text, textPos, windowSize, foreColor=(1.0, 1.0, 1.0), backColor=(0.0, 0.0, 0.0), scale=(1.0, 1.0), linespread=1.5): return self._draw_text(text, textPos, windowSize, scale, linespread, foreColor, backColor)