def apply_angle_scale_trans(self, angle, scale, trans, point=Vector(0, 0)): '''Update matrix transformation by adding new angle, scale and translate. :Parameters: `angle` : float Rotation angle to add `scale` : float Scaling value to add `trans` : Vector Vector translation to add `point` : Vector, default to (0, 0) Point to apply transformation ''' old_scale = self.scale new_scale = old_scale * scale if new_scale < self.scale_min or old_scale > self.scale_max: scale = 1. t = Matrix().translate( trans[0] * self.do_translation_x, trans[1] * self.do_translation_y, 0) t = t.multiply(Matrix().translate(point[0], point[1], 0)) t = t.multiply(Matrix().rotate(angle, 0, 0, 1)) t = t.multiply(Matrix().scale(scale, scale, scale)) t = t.multiply(Matrix().translate(-point[0], -point[1], 0)) self.apply_transform(t)
def update_viewport(self): from kivy.graphics.opengl import glViewport from kivy.graphics.transformation import Matrix from math import radians w, h = self.system_size w2, h2 = w / 2., h / 2. r = radians(self.rotation) # prepare the viewport glViewport(0, 0, w, h) # do projection matrix projection_mat = Matrix() projection_mat.view_clip(0.0, w, 0.0, h, -1.0, 1.0, 0) self.render_context['projection_mat'] = projection_mat # do modelview matrix modelview_mat = Matrix().translate(w2, h2, 0) modelview_mat = modelview_mat.multiply(Matrix().rotate(r, 0, 0, 1)) w, h = self.size w2, h2 = w / 2., h / 2. modelview_mat = modelview_mat.multiply(Matrix().translate(-w2, -h2, 0)) self.render_context['modelview_mat'] = modelview_mat # redraw canvas self.canvas.ask_update() # and update childs self.update_childsize()
def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)): ''' Transforms the scatter by applying the "trans" transformation matrix (on top of its current transformation state). The resultant matrix can be found in the :attr:`~Scatter.transform` property. :Parameters: `trans`: :class:`~kivy.graphics.transformation.Matrix`. Transformation matix to be applied to the scatter widget. `anchor`: tuple, defaults to (0, 0). The point to use as the origin of the transformation (uses local widget space). `post_multiply`: bool, defaults to False. If True, the transform matrix is post multiplied (as if applied before the current transform). Usage example:: from kivy.graphics.transformation import Matrix mat = Matrix().scale(3, 3, 3) scatter_instance.apply_transform(mat) ''' t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if post_multiply: self.transform = self.transform.multiply(t) else: self.transform = t.multiply(self.transform)
def apply_angle_scale_trans(self, angle, scale, trans, point=Vector(0, 0)): '''Update matrix transformation by adding new angle, scale and translate. :Parameters: `angle` : float Rotation angle to add `scale` : float Scaling value to add `trans` : Vector Vector translation to add `point` : Vector, default to (0, 0) Point to apply transformation ''' old_scale = self.scale new_scale = old_scale * scale if new_scale < self.scale_min or old_scale > self.scale_max: scale = 1. t = Matrix().translate( trans[0] * self.do_translation_x, trans[1] * self.do_translation_y, 0) t = t.multiply(Matrix().translate(point[0], point[1], 0)) t = t.multiply(Matrix().rotate(angle, 0, 0, 1)) t = t.multiply(Matrix().scale(scale, scale, scale)) t = t.multiply(Matrix().translate(-point[0], -point[1], 0)) self.apply_transform(t)
def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)): ''' Transforms the scatter by applying the "trans" transformation matrix (on top of its current transformation state). The resultant matrix can be found in the :attr:`~Scatter.transform` property. :Parameters: `trans`: :class:`~kivy.graphics.transformation.Matrix`. Transformation matix to be applied to the scatter widget. `anchor`: tuple, defaults to (0, 0). The point to use as the origin of the transformation (uses local widget space). `post_multiply`: bool, defaults to False. If True, the transform matrix is post multiplied (as if applied before the current transform). Usage example:: from kivy.graphics.transformation import Matrix mat = Matrix().scale(3, 3, 3) scatter_instance.apply_transform(mat) ''' t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if post_multiply: self.transform = self.transform.multiply(t) else: self.transform = t.multiply(self.transform)
def update_viewport(self): from kivy.graphics.opengl import glViewport from kivy.graphics.transformation import Matrix from math import radians w, h = self.system_size if self._density != 1: w, h = self.size smode = self.softinput_mode target = self._system_keyboard.target targettop = target.to_window(0, target.y)[1] if target else 0 kheight = self.keyboard_height w2, h2 = w / 2., h / 2. r = radians(self.rotation) x, y = 0, 0 _h = h if smode == 'pan': y = kheight elif smode == 'below_target': y = 0 if kheight < targettop else (kheight - targettop) + dp(9) if smode == 'scale': _h -= kheight # prepare the viewport glViewport(x, y, w, _h) # do projection matrix projection_mat = Matrix() projection_mat.view_clip(0.0, w, 0.0, h, -1.0, 1.0, 0) self.render_context['projection_mat'] = projection_mat # do modelview matrix modelview_mat = Matrix().translate(w2, h2, 0) modelview_mat = modelview_mat.multiply(Matrix().rotate(r, 0, 0, 1)) w, h = self.size w2, h2 = w / 2., h / 2. modelview_mat = modelview_mat.multiply(Matrix().translate(-w2, -h2, 0)) self.render_context['modelview_mat'] = modelview_mat # redraw canvas self.canvas.ask_update() # and update childs self.update_childsize()
def update_glsl(self, *largs): width = self.width if self.width > 1 else 100 height = self.height if self.height > 1 else 100 asp = width / float(height) proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 600, 1) proj = Matrix() proj.perspective(self.perspective_value, asp, 1, 1000) matrix_camera = Matrix().identity() matrix_camera.look_at(0, 100, 300, 100, 0, -100, 0, 1, 0) self.canvas['projection_mat'] = proj self.canvas['diffuse_light'] = (0.0, 1.0, 0.0) self.canvas['ambient_light'] = (0.1, 0.1, 0.1) if self.shadow: self.canvas['texture1'] = 1 self.canvas["enabled_shadow"] = 1.0 else: self.canvas["enabled_shadow"] = 0.0 self.canvas["texture1"] = 0 depthProjectionMatrix = Matrix().view_clip(-100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 200 * self.shadow_threshold * 2, 0) depthViewMatrix = Matrix().look_at(-0.5, -50, -100, 0, 0, 0, 0, 1, 0) depthModelMatrix = Matrix().identity() depthMVP = depthProjectionMatrix.multiply(depthViewMatrix).multiply(depthModelMatrix) self.canvas['depthMVP'] = depthMVP self.canvas['cond'] = (0.0, 0.7) self.canvas['val_sin'] = (self.alpha, 0.0) if self.shadow: self.update_fbo(largs[0])
def update_glsl(self, *largs): width = self.width if self.width > 1 else 100 height = self.height if self.height > 1 else 100 asp = width / float(height) proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 600, 1) proj = Matrix() proj.perspective(self.perspective_value, asp, 1, 1000) matrix_camera = Matrix().identity() matrix_camera = matrix_camera.look_at(*self.look_at) self.canvas['projection_mat'] = proj self.canvas['camera'] = matrix_camera self.canvas['diffuse_light'] = (0.0, 1.0, 0.0) self.canvas['ambient_light'] = (0.1, 0.1, 0.1) if self.shadow: self.canvas['texture1'] = 1 self.canvas["enabled_shadow"] = 1.0 else: self.canvas["enabled_shadow"] = 0.0 self.canvas["texture1"] = 0 depthProjectionMatrix = Matrix().view_clip( -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 200 * self.shadow_threshold * 2, 0) _shadow_pos = self._shadow_pos _shadow_target = self._shadow_target depthViewMatrix = Matrix().look_at( _shadow_target[0], _shadow_target[1], _shadow_target[2] + self._shadow_offset, _shadow_pos[0], _shadow_pos[1], _shadow_pos[2], 0, 1, 0) depthModelMatrix = Matrix().identity() depthMVP = depthProjectionMatrix.multiply(depthViewMatrix).multiply( depthModelMatrix) self.canvas['depthMVP'] = depthMVP self.canvas['cond'] = (0.0, 0.7) self.canvas['val_sin'] = (self.alpha, 0.0) if self.shadow: self.update_fbo(largs[0]) # label.text = str(Clock.get_rfps()) if self.parent.parent is None: # del self.parent self.parent.canvas3d = None self.fbo_list.clear() self.fbo = None for a in self.nodes: a.remove_a() self.nodes = [] self.parent._nodes = [] self.parent.clear_widgets() self.adding_queue = [] self.nt.cancel()
def update_glsl(self, *largs): width = self.width if self.width > 1 else 100 height = self.height if self.height > 1 else 100 asp = width / float(height) proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 600, 1) proj = Matrix() proj.perspective(self.perspective_value, asp, 1, 1000) matrix_camera = Matrix().identity() matrix_camera.look_at(0, 100, 300, 100, 0, -100, 0, 1, 0) self.canvas['projection_mat'] = proj self.canvas['diffuse_light'] = (0.0, 1.0, 0.0) self.canvas['ambient_light'] = (0.1, 0.1, 0.1) if self.shadow: self.canvas['texture1'] = 1 self.canvas["enabled_shadow"] = 1.0 else: self.canvas["enabled_shadow"] = 0.0 self.canvas["texture1"] = 0 depthProjectionMatrix = Matrix().view_clip( -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 200 * self.shadow_threshold * 2, 0) depthViewMatrix = Matrix().look_at(-0.5, -50, -100, 0, 0, 0, 0, 1, 0) depthModelMatrix = Matrix().identity() depthMVP = depthProjectionMatrix.multiply(depthViewMatrix).multiply( depthModelMatrix) self.canvas['depthMVP'] = depthMVP self.canvas['cond'] = (0.0, 0.7) self.canvas['val_sin'] = (self.alpha, 0.0) if self.shadow: self.update_fbo(largs[0])
def __init__(self, fs, length1, length2 = 1): size = (length1, length2) # it doesn't look like we can use float textures on mobile kivy, but people sometimes interconvert floats with 32bit rgba in shaders. # we would then have 3 shaders or texture rows or such, for x coord, y coord, angle, etc #Logger.info('float: ' + str(gl.getExtension('OES_texture_float'))) texture = Texture.create( size = size, #bufferfmt = 'float' ) self._fbo = Fbo( size = size, texture = texture, vs = default_vs, fs = header_fs + fs, ) # these matrices are to transform # window coordinates into data # coordinates centermat = Matrix() centermat.translate(-.5,-.5,-.5) idxscale = 1.0 / 255.0; idxmat = Matrix() idxmat.scale(idxscale, idxscale, idxscale) self._fbo['frag_coord2idx'] = idxmat.multiply(centermat) ratiomat = Matrix() ratiomat.scale(1.0 / length1, 1.0 / length2, 1.0) self._fbo['frag_coord2ratio'] = ratiomat self._texture_bindings = {} self._fbo.add_reload_observer(self._populate_fbo) self._populate_fbo(self._fbo)
def update_fbo(self, time): width = self.width if self.width > 1 else 100 height = self.height if self.height > 1 else 100 asp = (width / float(height)) proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 1600, 1) proj = Matrix() proj.perspective(self.perspective_value, asp, 1, 1000) lightInvDir = (0.5, 2, 2) depthProjectionMatrix = Matrix().view_clip( -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 200 * self.shadow_threshold * 2, 0) _shadow_pos = self._shadow_pos _shadow_target = self._shadow_target depthViewMatrix = Matrix().look_at( _shadow_target[0], _shadow_target[1], _shadow_target[2] + self._shadow_offset, _shadow_pos[0], _shadow_pos[1], _shadow_pos[2], 0, 1, 0) depthModelMatrix = Matrix().identity() depthMVP = depthProjectionMatrix.multiply(depthViewMatrix).multiply( depthModelMatrix) self.fbo['projection_mat'] = proj self.fbo['depthMVP'] = depthMVP self.fbo['diffuse_light'] = (0.0, 1.0, 0.0) self.fbo['ambient_light'] = self.ambient_light for m_pos in range(len(self.nodes)): motion_matrix = Matrix().view_clip(-asp, asp, -1, 1, 1, 600, 1) angle = self.nodes[m_pos].rotate[0] * 3.14 / 180 pos = self.nodes[m_pos].get_pos() trans = self.nodes[m_pos].translate[:] result = [0, 0, 0] result[0] = 0.3 if trans[0] < pos[0] else -0.3 result[1] = 0.3 if trans[1] < pos[1] else -0.3 result[2] = 0.3 if trans[2] < pos[2] else -0.3 motion_matrix = motion_matrix.translate(trans[0] + 0.1, trans[1] + 0.1, trans[2]) self.motion_blur_fbo['oldTransformation{0}'.format( str(m_pos))] = motion_matrix self.motion_blur_fbo['projection_mat'] = proj self.motion_blur_fbo['depthMVP'] = depthMVP matrix_camera = Matrix().identity() matrix_camera = matrix_camera.look_at(*self.look_at) if self.picking_fbo: self.picking_fbo['projection_mat'] = proj self.picking_fbo['camera'] = matrix_camera self.alpha += 10 * time self.fbo['cond'] = (0.0, 0.7) self.fbo['val_sin'] = (self.alpha, 0.0)
def update_viewport(self): from kivy.graphics.opengl import glViewport from kivy.graphics.transformation import Matrix from math import radians w, h = self.system_size if self._density != 1: w, h = self.size smode = self.softinput_mode kheight = self.keyboard_height w2, h2 = w / 2.0, h / 2.0 r = radians(self.rotation) x, y = 0, 0 _h = h if smode: y = kheight if smode == "scale": _h -= kheight # prepare the viewport glViewport(x, y, w, _h) # do projection matrix projection_mat = Matrix() projection_mat.view_clip(0.0, w, 0.0, h, -1.0, 1.0, 0) self.render_context["projection_mat"] = projection_mat # do modelview matrix modelview_mat = Matrix().translate(w2, h2, 0) modelview_mat = modelview_mat.multiply(Matrix().rotate(r, 0, 0, 1)) w, h = self.size w2, h2 = w / 2.0, h / 2.0 modelview_mat = modelview_mat.multiply(Matrix().translate(-w2, -h2, 0)) self.render_context["modelview_mat"] = modelview_mat # redraw canvas self.canvas.ask_update() # and update childs self.update_childsize()
def update_viewport(self): from kivy.graphics.opengl import glViewport from kivy.graphics.transformation import Matrix from math import radians w, h = self.system_size smode = self.softinput_mode kheight = self.keyboard_height w2, h2 = w / 2., h / 2. r = radians(self.rotation) x, y = 0, 0 _h = h if smode: y = kheight if smode == 'scale': _h -= kheight # prepare the viewport glViewport(x, y, w, _h) # do projection matrix projection_mat = Matrix() projection_mat.view_clip(0.0, w, 0.0, h, -1.0, 1.0, 0) self.render_context['projection_mat'] = projection_mat # do modelview matrix modelview_mat = Matrix().translate(w2, h2, 0) modelview_mat = modelview_mat.multiply(Matrix().rotate(r, 0, 0, 1)) w, h = self.size w2, h2 = w / 2., h / 2. modelview_mat = modelview_mat.multiply(Matrix().translate(-w2, -h2, 0)) self.render_context['modelview_mat'] = modelview_mat # redraw canvas self.canvas.ask_update() # and update childs self.update_childsize()
def apply_image_transform(self, x=0, y=0, scale=1, post_multiply=False, anchor=(0, 0)): # Test if the x move is valid t = Matrix() t = t.translate(x, 0, 0) if not self.test_valid(t.multiply(self.transform)): x = 0 # Test if the y move is valid t = Matrix() t = t.translate(0, y, 0) if not self.test_valid(t.multiply(self.transform)): y = 0 # Test if the scale is valid t = Matrix().translate(anchor[0], anchor[1], 0) t = t.scale(scale, scale, scale) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if not self.test_valid(t.multiply(self.transform)): scale = 1 # Compile final matrix t = Matrix().translate(anchor[0], anchor[1], 0) t = t.translate(x, y, 0) t = t.scale(scale, scale, scale) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if post_multiply: self.transform = self.transform.multiply(t) else: self.transform = t.multiply(self.transform)
def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)): ''' Transforms scatter by trans (on top of its current transformation state) :Parameters: `trans`: transformation matrix from transformation lib. Transformation to be applied to the scatter widget `anchor`: tuple, default to (0, 0) The point to use as the origin of the transformation (uses local widget space) `post_multiply`: bool, default to False If true the transform matrix is post multiplied (as if applied before the current transform) ''' t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if post_multiply: self.transform = self.transform.multiply(t) else: self.transform = t.multiply(self.transform)
def apply_transform(self, trans, post_multiply=False, anchor=(0, 0)): ''' Transforms scatter by trans (on top of its current transformation state) :Parameters: `trans`: transformation matrix from transformation lib. Transformation to be applied to the scatter widget `anchor`: tuple, default to (0, 0) The point to use as the origin of the transformation (uses local widget space) `post_multiply`: bool, default to False If true the transform matrix is post multiplied (as if applied before the current transform) ''' t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) if post_multiply: self.transform = self.transform.multiply(t) else: self.transform = t.multiply(self.transform)
def update_fbo(self, time): width = self.width if self.width > 1 else 100 height = self.height if self.height > 1 else 100 asp = (width / float(height)) proj = Matrix().view_clip(-asp, asp, -1, 1, 1, 600, 1) proj = Matrix() proj.perspective(self.perspective_value, asp, 1, 1000) lightInvDir = (0.5, 2, 2) depthProjectionMatrix = Matrix().view_clip(-100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 100 * self.shadow_threshold, -100 * self.shadow_threshold, 200 * self.shadow_threshold * 2, 0) depthViewMatrix = Matrix().look_at(-0.5, -50, -100, 0, 0, 0, 0, 1, 0) depthModelMatrix = Matrix().identity() depthMVP = depthProjectionMatrix.multiply(depthViewMatrix).multiply(depthModelMatrix) self.fbo['projection_mat'] = proj self.fbo['depthMVP'] = depthMVP self.fbo['diffuse_light'] = (0.0, 1.0, 0.0) self.fbo['ambient_light'] = (0.1, 0.1, 0.1) for m_pos in range(len(self.nodes)): motion_matrix = Matrix().view_clip(-asp, asp, -1, 1, 1, 600, 1) angle = self.nodes[m_pos].rotate[0] * 3.14 / 180 pos = self.nodes[m_pos].get_pos() trans = self.nodes[m_pos].translate[:] result = [0, 0, 0] result[0] = 0.3 if trans[0] < pos[0] else -0.3 result[1] = 0.3 if trans[1] < pos[1] else -0.3 result[2] = 0.3 if trans[2] < pos[2] else -0.3 motion_matrix = motion_matrix.translate(trans[0] + 0.1, trans[1] + 0.1, trans[2]) self.motion_blur_fbo['oldTransformation{0}'.format(str(m_pos))] = motion_matrix self.motion_blur_fbo['projection_mat'] = proj self.motion_blur_fbo['depthMVP'] = depthMVP if self.picking_fbo: self.picking_fbo['projection_mat'] = proj self.alpha += 10 * time self.fbo['cond'] = (0.0, 0.7) self.fbo['val_sin'] = (self.alpha, 0.0)
class SingleRotate(object): def __init__(self, angle, axis, render_context): # It shold be way to get current context # but in simple case we may just pass it to constructor self.context = render_context self._axis = axis self._angle = angle self.renderer = render_context self.prev_mvm = Matrix() self.matrix = Matrix() Callback(self._rotate) # here we perform rotation def radians(self, degrees): """ Calculate radians from angle here """ return degrees * (3.14159265 / 180.) @property def angle(self): return self._angle @angle.setter def angle(self, v): self._angle = v angle = self.radians(self._angle) ax, ay, az = self._axis # calculate rotated matrix and store it self.matrix = Matrix().rotate(angle, ax, ay, az) def clear(self): Callback(self._clear) def _rotate(self, *args): " This sets rotation in callback " # get previous matrix and save it self.prev_mvm = self.renderer['modelview_mat'] # do multiply for rotation self.context['modelview_mat'] = self.prev_mvm.multiply(self.matrix) def _clear(self, *args): self.renderer['modelview_mat'] = self.prev_mvm
class SingleRotate(object): def __init__(self, angle, axis, render_context): # It shold be way to get current context # but in simple case we may just pass it to constructor self.context = render_context self._axis = axis self._angle = angle self.renderer = render_context self.prev_mvm = Matrix() self.matrix = Matrix() Callback(self._rotate) # here we perform rotation def radians(self, degrees): """ Calculate radians from angle here """ return degrees * (3.14159265 / 180.) @property def angle(self): return self._angle @angle.setter def angle(self, v): self._angle = v angle = self.radians(self._angle) ax, ay, az = self._axis # calculate rotated matrix and store it self.matrix = Matrix().rotate(angle, ax, ay, az) def clear(self): Callback(self._clear) def _rotate(self, *args): " This sets rotation in callback " # get previous matrix and save it self.prev_mvm = self.renderer['modelview_mat'] # do multiply for rotation self.context['modelview_mat'] = self.prev_mvm.multiply(self.matrix) def _clear(self, *args): self.renderer['modelview_mat'] = self.prev_mvm
class MachineScreen(Widget): # Watched by the root app class. Set to True to close menus. close_menus = BooleanProperty() # Watched by the root app class. If either of these are set to not None, a menu will be shown # in the root class. selected_state = ObjectProperty(None, allownone=True) selected_transition = ObjectProperty(None, allownone=True) def __init__(self, **kwargs): super(MachineScreen, self).__init__(**kwargs) self.start_state = None # This gets set by the root widget self.undo_handler = None self.states = [] self.transitions = [] self.handled_touches = [] # TODO: DON'T HARD CODE THIS self.blank_char = '_' self.canvas_created = False # Used to differentiate between objects. self.unique_id = 0 self.bgGrabbed = False self.grabbed_object = None self.initial_grab_pt = None self.grabPt = (0, 0) self.held_state = None # Listened to by main.py to close state/transition menus. self.close_menus = False self.selected_state = None # Initial distance between two touches. self.initial_dist = 1 self.multitouch = False self.previous_scale = 1 self.temp_scale = 1 # We check the simulator to see if its running when handling touches. self.turing_simulator = None self.state_group = InstructionGroup() self.transition_group = InstructionGroup() self.transitionline = TempTransitionLine(self.get_unique_id()) """ Desc: Each object (for now state and transition) has a unique id. This is used predominately in the display queue and in the undo handler. """ def get_unique_id(self): self.unique_id += 1 return self.unique_id """ Desc: Creates a new machine by wiping the current states and transitions. Clears the frame buffer, and resets the undo_handler. """ def create_new_machine(self): # Removing visual components self.fbo.bind() self.fbo.clear_buffer() for state in self.states: invalid_transitions = state.delete() self.state_group.remove_group(state.get_unique_id()) for transition in invalid_transitions: self.transition_group.remove_group(transition.get_unique_id()) self.fbo.release() self.undo_handler.reset() self.states = [] self.transitions = [] self.start_state = None # ------------------------ GRAPHICS ----------------------- # """ Desc: Called on the first resize (moving from default (100,100) dimensions) Builds the frame buffer which we draw everything onto (much faster than individual widgets) Also sets up our transformation matricies that we use for scaling and translations. """ def initialise_canvas(self, size): self.matrix = Matrix() with self.canvas: self.fbo = Fbo(size=size) Color(1, 1, 1, 1) self.bg = Rectangle(size=self.size, pos=self.pos, texture=self.fbo.texture) self.canvas_created = True with self.fbo: self.matrix_instruction = MatrixInstruction() self.matrix_instruction.matrix = self.matrix self.fbo.add(self.transition_group) self.fbo.add(self.state_group) """ Desc: Bound in the root class, updates the widget size when app size is adjusted. """ def resize(self, size, pos): if not self.canvas_created: self.initialise_canvas(size) self.bg.size = size """ Desc: Adds an instruction group at given index. WARNING: Don't add at multiple index's without first clearing the original instruction group from the display list. """ def add_at_index(self, ins, index): self.fbo.bind() self.fbo.clear_buffer() self.fbo.insert(index, ins) self.fbo.release() """ Desc: Moves an instruction group to the top of another instruction group. EG. Moving a state to the top of the state display list. The ins must already be in the ins_group or an exception will be raised. """ def add_to_top(self, ins_group, ins, ins_uid): self.fbo.bind() self.fbo.clear_buffer() ins_group.remove_group(ins_uid) ins_group.add(ins) self.fbo.release() self.fbo.ask_update() """ Desc: Updates the screen after the change of a graphic instruction. If the graphic instruction doesn't change, this will clear the screen. """ def update_graphic_ins(self, ins, args): self.fbo.bind() self.fbo.clear_buffer() return_value = ins(*args) self.fbo.release() self.fbo.ask_update() return return_value """ Desc: Pans the main fbo by a vector delta. """ def pan(self, delta): self.matrix = self.matrix.multiply(Matrix().translate( delta[0], delta[1], 0)) self.fbo.bind() self.fbo.clear_buffer() self.matrix_instruction.matrix = self.matrix self.fbo.release() """ Desc: Zooms the fbo at a world point by a factor. Keeps the screen centred on the zoom. """ def zoom(self, origin, factor): self.matrix = self.matrix.multiply(Matrix().translate( origin[0], origin[1], 0)) self.matrix = self.matrix.multiply(Matrix().scale( factor, factor, factor)) self.matrix = self.matrix.multiply(Matrix().translate( -origin[0], -origin[1], 0)) self.fbo.bind() self.fbo.clear_buffer() self.matrix_instruction.matrix = self.matrix self.fbo.release() # -------------------- COORDINATE FUNCTIONS --------------------- # """ Desc: Uses the transformation matrix we are using on the fbo to transfer a point on the root screen to world space. (Used for adding transitions etc.) """ def screen_to_world(self, screenPos): worldPos = self.matrix.inverse().transform_point( screenPos[0], screenPos[1] - self.parent.y, 0) return worldPos """ Desc: Uses the transformation matrix we are using on the fbo to transfer a point on our world screen to the root app screen. (Used for getting the proper point to put state/transition menus at) """ def world_to_screen(self, objPos): screenPos = self.matrix.transform_point(objPos[0], objPos[1], 0) return (screenPos[0], screenPos[1] + self.parent.y, 0) # -------------------- TOUCH EVENT HANDLERS ---------------------- # """ Desc: Handles a touch onto the gui. All touches are stored for later use (with multitouch functionality) and also movement functionality such as dragging a state around. """ def handle_touch_down(self, instance, touch): if touch not in self.handled_touches: # Set up the reference variable. A single touch won't shut menus # but a move (outside stray) or 'click' will shut menu. self.close_menus = False # Only want to handle at max 2 touches. if len(self.handled_touches) < 2: self.handled_touches.append(touch) if not self.multitouch and len(self.handled_touches) == 2: self.multitouch = True # As this is the first multitouch, we want to close menus, drop held objects etc. self.close_menus = True self.bgGrabbed = False self.grabbed_object = None if self.held_state: self.unhold_state(None) if self.multitouch and len(self.handled_touches) == 2: # Set up for scaling. self.initial_dist = Vector( self.handled_touches[0].pos).distance( Vector(self.handled_touches[1].pos)) # Remember the current scale before more scaling. self.temp_scale = self.previous_scale """ Desc: Handles a touch up (no matter where it happened, or even if we're tracking it) If we are tracking the touch, various things can happen. If its a zoom we stop zooming, if we're creating a transition we'll create it etc. """ def handle_touch_up(self, instance, touch): # We no longer need to remember the touch, so remove it. if touch in self.handled_touches: self.handled_touches.remove(touch) # To get out of multitouch mode all touches need to be removed. Simplifies things a bit. if len(self.handled_touches) == 0: self.multitouch = False # If a touch and hold had been activated, we need to unhighlight the state. self.unhold_state(touch.pos) self.bgGrabbed = False if self.grabbed_object: self.undo_handler.add_action( Action_MoveObject(self.grabbed_object.get_unique_id(), self.initial_grab_pt, self.grabbed_object.get_position())) self.grabbed_object = None """ Desc: Handles a touch move on the gui. We are only interested in moves which origininated on the gui. """ def handle_touch_move(self, instance, move_triggered): # Handling new touches to the machine. touch = instance.curr_touch_move if touch in self.handled_touches: if not self.multitouch: if touch.strayed: self.close_menus = True if not self.held_state: if not self.grabbed_object and not self.bgGrabbed: self.handle_initial_grab(touch.opos) self.handle_grab(touch.pos) else: self.handle_transition_move(touch) else: self.handle_scale() """ Desc: Listens to the touchhandler for a click_triggered event. (ie, a quick tap) If a transition or state is under the tap, it will be selected and an event will be fired too the root app to create the respective menu. """ def handle_click(self, instance, click_triggered): if click_triggered and not self.turing_simulator.run_mode: world_pos = self.screen_to_world(instance.initial_touch.opos) for state in self.states: if Vector(world_pos).distance(Vector( state.pos)) < state.radius: if self.selected_transition: self.deselect_objects() self.close_menus = True if self.selected_state and state is not self.selected_state: self.deselect_objects() self.update_graphic_ins(state.set_highlight, (0, 1, 1)) # Brings the selected state to the top of the display list. self.add_to_top(self.state_group, state.get_instruction_group(), state.get_unique_id()) self.selected_state = state return for transition in self.transitions: if transition.collide_point(world_pos): if self.selected_state: self.deselect_objects() self.close_menus = True if self.selected_transition and transition is not self.selected_transition: self.deselect_objects() self.update_graphic_ins(transition.set_highlight, (0, 1, 1)) self.add_to_top(self.transition_group, transition.get_instruction_group(), transition.get_unique_id()) self.selected_transition = transition return # If we're at this point we know that no states/transitions have # been selected. self.close_menus = True """ Desc: Listens to the touchhandler for a hold_triggered event. If this happens ontop of a state we go into transition creation mode. """ def handle_hold(self, instance, hold_triggered): if hold_triggered and not self.turing_simulator.run_mode: world_pos = self.screen_to_world(instance.initial_touch.opos) for state in self.states: if Vector(world_pos).distance(Vector( state.pos)) < state.radius: self.update_graphic_ins(state.set_highlight, (1, 1, 0)) self.held_state = state """ Desc: Listens to the touchhandler for a double_triggered event. If the double tap is on empty space a new state will be created. """ def handle_double_touch(self, instance, double_triggered): if double_triggered and not self.turing_simulator.run_mode: world_pos = self.screen_to_world(instance.initial_touch.opos) for state in self.states: if Vector(world_pos).distance(Vector( state.pos)) < state.radius: return self.add_state(instance.initial_touch.opos, False) # -------------------------- MACHINE FUNCTIONS ------------------------ # # (Majority of these will be called by the above touch handlers) # Checks if either the background or a state has been selected on the move # Background selected: Pan's camera # State selected: Move state def handle_initial_grab(self, pos): world_pos = self.screen_to_world(pos) self.grabPt = (world_pos[0], world_pos[1]) if not self.turing_simulator.run_mode: for state in self.states: if Vector(world_pos).distance(Vector( state.pos)) < state.radius: self.grabbed_object = state self.add_to_top(self.state_group, state.get_instruction_group(), state.get_unique_id()) break for transition in self.transitions: if transition.collide_point(world_pos): self.grabbed_object = transition self.add_to_top(self.transition_group, transition.get_instruction_group(), transition.get_unique_id()) break if self.grabbed_object: self.initial_grab_pt = self.grabbed_object.get_position() else: self.bgGrabbed = True def handle_grab(self, pos): world_pos = self.screen_to_world(pos) if self.grabbed_object: self.update_graphic_ins(self.grabbed_object.set_position, [world_pos]) elif self.bgGrabbed: # pan by the delta position self.pan( (world_pos[0] - self.grabPt[0], world_pos[1] - self.grabPt[1])) def handle_transition_move(self, touch): world_pos = self.screen_to_world(touch.pos) world_pos = (world_pos[0], world_pos[1]) if self.transitionline.displayed: if Vector(world_pos).distance(Vector( self.held_state.pos)) > self.held_state.radius: self.update_graphic_ins( self.transitionline.update_line, (self.held_state.pos, world_pos, False)) else: self.update_graphic_ins(self.transitionline.update_line, (self.held_state.pos, world_pos, True)) else: if Vector(world_pos).distance(Vector( self.held_state.pos)) > self.held_state.radius: self.transitionline.update_line(self.held_state.pos, world_pos, False) # Need to remove the held state and re-add it self.update_graphic_ins(self.state_group.remove_group, [self.held_state.get_unique_id()]) self.add_at_index(self.transitionline.get_instruction_group(), 3) self.add_at_index(self.held_state.get_instruction_group(), 4) self.transitionline.displayed = True def handle_scale(self): if len(self.handled_touches) == 2: new_dist = Vector(self.handled_touches[0].pos).distance( Vector(self.handled_touches[1].pos)) new_scale = (new_dist / self.initial_dist) * self.temp_scale if new_scale < .8: new_scale = .8 if new_scale > 3: new_scale = 3 pivot_pt = self.screen_to_world( (Vector(self.handled_touches[0].pos) + Vector(self.handled_touches[1].pos)) / 2) self.zoom(pivot_pt, new_scale / self.previous_scale) self.previous_scale = new_scale # If any states/transitions are selected removes the highlight and forgets # the object. def deselect_objects(self): if self.selected_state: # Stop the hold highlight disappearing. if self.selected_state is not self.held_state: self.update_graphic_ins(self.selected_state.set_highlight, (.8, .8, .8)) self.selected_state = None if self.selected_transition: self.update_graphic_ins(self.selected_transition.set_highlight, (.8, .8, .8)) self.selected_transition = None # Handles a touch up after a hold. # For now this is when a transition has tried to be created. # pos: (x, y) or None if no transition is wanted to be created. def unhold_state(self, pos): if self.transitionline.displayed: if pos: world_pos = self.screen_to_world(pos) for state in self.states: if Vector(world_pos).distance(Vector( state.pos)) < state.radius: # Create a new transition. self.add_transition(self.held_state, state, None, 'L', self.blank_char, self.blank_char) break # Remove the state from the main buffer self.update_graphic_ins(self.fbo.remove_group, [self.held_state.get_unique_id()]) # Add it back to the state_group instruction group self.update_graphic_ins(self.state_group.add, [self.held_state.get_instruction_group()]) # Remove the temporary transition line from the main buffer self.update_graphic_ins(self.fbo.remove_group, [self.transitionline.get_unique_id()]) self.transitionline.displayed = False if self.held_state: self.update_graphic_ins(self.held_state.set_highlight, (.8, .8, .8)) self.held_state = None def add_transition(self, from_state, to_state, anchor_offset, direction, read_sym, write_sym, unique_id=-1, from_undo=False): if from_state == to_state: if not anchor_offset: anchor_offset = Vector( self.transitionline.loop_ctrl_pt) - Vector(from_state.pos) else: if not anchor_offset: anchor_offset = (20, 20) if unique_id == -1: u_id = self.get_unique_id() else: u_id = unique_id new_transition = TuringTransition(from_state, to_state, anchor_offset, direction, read_sym, write_sym, u_id) self.transitions.append(new_transition) from_state.add_out_transition(new_transition) if from_state is not to_state: to_state.add_in_transition(new_transition) if not from_undo: self.undo_handler.add_action(Action_AddTransition(new_transition)) self.update_graphic_ins(self.transition_group.add, [new_transition.get_instruction_group()]) def delete_transition(self, transition, from_undo=False): if not from_undo: self.undo_handler.add_action(Action_DeleteTransition(transition)) self.update_graphic_ins(self.transition_group.remove_group, [transition.get_unique_id()]) transition.delete() self.transitions.remove(transition) def add_state(self, new_pos, absolute, name='', from_undo=False, unique_id=-1): if not absolute: world_pos = self.screen_to_world(new_pos) else: world_pos = new_pos if name == '': state_name = 'state' + str(len(self.states)) i = 1 while self.get_state_by_name(state_name): state_name = 'state' + str(len(self.states) + i) i += 1 else: state_name = name self.deselect_objects() if unique_id == -1: u_id = self.get_unique_id() else: u_id = unique_id new_state = TuringState(world_pos, state_name, u_id) self.states.append(new_state) self.update_graphic_ins(self.state_group.add, [new_state.get_instruction_group()]) if not from_undo: self.undo_handler.add_action( Action_AddState(state_name, world_pos, new_state.get_unique_id())) return new_state def delete_state(self, state, from_undo=False): # Create the undo action now (before transitions are lost etc) if not from_undo: print('handle meh') self.undo_handler.add_action( Action_DeleteState(state.name, state.pos, state.final_state, state.start_state, state.out_transitions, state.in_transitions, state.get_unique_id())) # Makes sure we remove all references of the state if state == self.start_state: self.start_state = None # Remove connected transitions invalid_transitions = state.delete() # Prepare the fbo for a redraw. self.fbo.bind() self.fbo.clear_buffer() # Remove invalid transitions from display and local storage. for transition in invalid_transitions: self.transition_group.remove_group(transition.get_unique_id()) self.transitions.remove(transition) # Remove from the display list self.state_group.remove_group(state.get_unique_id()) # Remove from the state list self.states.remove(state) self.fbo.release() def get_state_by_name(self, name): for state in self.states: if state.name == name: return state return None def get_obj_by_id(self, unique_id): for state in self.states: if state.get_unique_id() == unique_id: return state for transition in self.transitions: if transition.get_unique_id() == unique_id: return transition return None
def _update_labels(self): xlabel = self._xlabel ylabel = self._ylabel x = self.x y = self.y width = self.width height = self.height padding = self.padding x_next = padding + x y_next = padding + y xextent = x + width yextent = y + height ymin = self.ymin ymax = self.ymax xmin = self.xmin precision = self.precision x_overlap = False y_overlap = False # set up x and y axis labels if xlabel: xlabel.text = self.xlabel xlabel.texture_update() xlabel.size = xlabel.texture_size xlabel.pos = (x + width / 2. - xlabel.width / 2., padding + y) y_next += padding + xlabel.height if ylabel: ylabel.text = self.ylabel ylabel.texture_update() ylabel.size = ylabel.texture_size ylabel.x = padding + x - (ylabel.width / 2. - ylabel.height / 2.) x_next += padding + ylabel.height xpoints = self._ticks_majorx xlabels = self._x_grid_label xlabel_grid = self.x_grid_label ylabel_grid = self.y_grid_label ypoints = self._ticks_majory ylabels = self._y_grid_label # now x and y tick mark labels if len(ylabels) and ylabel_grid: # horizontal size of the largest tick label, to have enough room ylabels[0].text = precision % ypoints[0] ylabels[0].texture_update() y1 = ylabels[0].texture_size y_start = y_next + ( padding + y1[1] if len(xlabels) and xlabel_grid else 0 ) + (padding + y1[1] if not y_next else 0) yextent = y + height - padding - y1[1] / 2. if self.ylog: ymax = log10(ymax) ymin = log10(ymin) ratio = (yextent - y_start) / float(ymax - ymin) y_start -= y1[1] / 2. func = (lambda x: 10 ** x) if self.ylog else lambda x: x y1 = y1[0] for k in xrange(len(ylabels)): ylabels[k].text = precision % func(ypoints[k]) ylabels[k].texture_update() ylabels[k].size = ylabels[k].texture_size y1 = max(y1, ylabels[k].texture_size[0]) ylabels[k].pos = (x_next, y_start + (ypoints[k] - ymin) * ratio) if len(ylabels) > 1 and ylabels[0].top > ylabels[1].y: y_overlap = True else: x_next += y1 + padding if len(xlabels) and xlabel_grid: func = log10 if self.xlog else lambda x: x # find the distance from the end that'll fit the last tick label xlabels[0].text = precision % func(xpoints[-1]) xlabels[0].texture_update() xextent = x + width - xlabels[0].texture_size[0] / 2. - padding # find the distance from the start that'll fit the first tick label if not x_next: xlabels[0].text = precision % func(xpoints[0]) xlabels[0].texture_update() x_next = padding + xlabels[0].texture_size[0] / 2. xmin = func(xmin) ratio = (xextent - x_next) / float(func(self.xmax) - xmin) func = (lambda x: 10 ** x) if self.xlog else lambda x: x right = -1 for k in xrange(len(xlabels)): xlabels[k].text = precision % func(xpoints[k]) # update the size so we can center the labels on ticks xlabels[k].texture_update() xlabels[k].size = xlabels[k].texture_size xlabels[k].pos = (x_next + (xpoints[k] - xmin) * ratio - xlabels[k].texture_size[0] / 2., y_next) if xlabels[k].x < right: x_overlap = True break right = xlabels[k].right if not x_overlap: y_next += padding + xlabels[0].texture_size[1] # now re-center the x and y axis labels if xlabel: xlabel.x = x_next + (xextent - x_next) / 2. - xlabel.width / 2. if ylabel: ylabel.y = y_next + (yextent - y_next) / 2. - ylabel.height / 2. t = Matrix().translate(ylabel.center[0], ylabel.center[1], 0) t = t.multiply(Matrix().rotate(-radians(270), 0, 0, 1)) ylabel.transform = t.multiply(Matrix().translate(-ylabel.center[0], -ylabel.center[1], 0)) if x_overlap: for k in xrange(len(xlabels)): xlabels[k].text = '' if y_overlap: for k in xrange(len(ylabels)): ylabels[k].text = '' return x_next, y_next, xextent, yextent
def apply_transform(self, rescale, anchor=(0, 0)): trans = Matrix().scale(rescale, rescale, rescale) t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) self.transform.matrix = t
class Camera(Object3D): """ Base camera class """ scale = NumericProperty(1.0) _right = ObjectProperty(Vector3(1, 0, 0)) _up = ObjectProperty(Vector3(0, 1, 0)) _back = ObjectProperty(Vector3(0, 0, 1)) up = ObjectProperty(Vector3(0, 1, 0)) def __init__(self): super(Camera, self).__init__() self.projection_matrix = Matrix() self.modelview_matrix = Matrix() self.model_matrix = Matrix() self.viewport_matrix = (0, 0, 0, 0) self.renderer = None # renderer camera is bound to self._look_at = None self.look_at(Vector3(0, 0, -1)) def _set_position(self, val): super(Camera, self).on_pos_changed(val) self.look_at(self._look_at) self.update() def on_pos_changed(self, coord, v): """ Camera position was changed """ self.look_at(self._look_at) self.update() def on_up(self, instance, up): """ Camera up vector was changed """ pass def on_scale(self, instance, scale): """ Handler for change scale parameter event """ def look_at(self, *v): if len(v) == 1: v = v[0] m = Matrix() pos = self._position m = m.look_at(pos[0], pos[1], pos[2], v[0], v[1], v[2], self.up[0], self.up[1], self.up[2]) m = m.rotate(radians(self.rot.x), 1.0, 0.0, 0.0) m = m.rotate(radians(self.rot.y), 0.0, 1.0, 0.0) m = m.rotate(radians(self.rot.z), 0.0, 0.0, 1.0) self.modelview_matrix = m # set camera vectors from view matrix self._right = Vector3(m[0], m[1], m[2]) self._up = Vector3(m[4], m[5], m[6]) self._back = Vector3(m[8], m[9], m[10]) self._look_at = v self.update() def bind_to(self, renderer): """ Bind this camera to renderer """ self.renderer = renderer def update(self): if self.renderer: self.viewport_matrix = (self.renderer._viewport.pos[0], self.renderer._viewport.pos[1], self.renderer._viewport.size[0], self.renderer._viewport.size[1]) model_matrix = self.modelview_matrix.multiply( self.renderer.fbo['view_mat'].inverse()) self.model_matrix = model_matrix self.renderer._update_matrices() def update_projection_matrix(self): """ This function should be overridden in the subclasses
def apply_transform(self, rescale, anchor=(0, 0)): trans = Matrix().scale(rescale, rescale, rescale) t = Matrix().translate(anchor[0], anchor[1], 0) t = t.multiply(trans) t = t.multiply(Matrix().translate(-anchor[0], -anchor[1], 0)) self.transform.matrix = t
def _update_labels(self): xlabel = self._xlabel ylabel = self._ylabel x = self.x y = self.y width = self.width height = self.height padding = self.padding x_next = padding + x y_next = padding + y xextent = width + x yextent = height + y ymin = self.ymin ymax = self.ymax xmin = self.xmin x_overlap = False y_overlap = False # set up x and y axis labels if xlabel: xlabel.text = self.xlabel xlabel.texture_update() xlabel.size = xlabel.texture_size xlabel.pos = int(x + width / 2. - xlabel.width / 2.), int(padding + y) y_next += padding + xlabel.height if ylabel: ylabel.text = self.ylabel ylabel.texture_update() ylabel.size = ylabel.texture_size ylabel.x = padding + x - (ylabel.width / 2. - ylabel.height / 2.) x_next += padding + ylabel.height xpoints = self._ticks_majorx xlabels = self._x_grid_label xlabel_grid = self.x_grid_label ylabel_grid = self.y_grid_label ypoints = self._ticks_majory ylabels = self._y_grid_label # now x and y tick mark labels if len(ylabels) and ylabel_grid: # horizontal size of the largest tick label, to have enough room funcexp = exp10 if self.ylog else identity funclog = log10 if self.ylog else identity ylabels[0].text = self._format_ylabel_text(funcexp(ypoints[0])) ylabels[0].texture_update() y1 = ylabels[0].texture_size y_start = y_next + (padding + y1[1] if len(xlabels) and xlabel_grid else 0) + \ (padding + y1[1] if not y_next else 0) yextent = y + height - padding - y1[1] / 2. ymin = funclog(ymin) ratio = (yextent - y_start) / float(funclog(ymax) - ymin) y_start -= y1[1] / 2. y1 = y1[0] for k in range(len(ylabels)): ylabels[k].text = self._format_ylabel_text(funcexp(ypoints[k])) ylabels[k].texture_update() ylabels[k].size = ylabels[k].texture_size y1 = max(y1, ylabels[k].texture_size[0]) ylabels[k].pos = tuple( map(int, (x_next, y_start + (ypoints[k] - ymin) * ratio))) if len(ylabels) > 1 and ylabels[0].top > ylabels[1].y: y_overlap = True x_next += y1 + padding if len(xlabels) and xlabel_grid: funcexp = exp10 if self.xlog else identity funclog = log10 if self.xlog else identity # find the distance from the end that'll fit the last tick label xlabels[0].text = self._format_xlabel_text(funcexp(xpoints[-1])) xlabels[0].texture_update() xextent = x + width - xlabels[0].texture_size[0] / 2. - padding # find the distance from the start that'll fit the first tick label if not x_next: xlabels[0].text = self._format_label_text(funcexp(xpoints[0])) xlabels[0].texture_update() x_next = padding + xlabels[0].texture_size[0] / 2. xmin = funclog(xmin) ratio = (xextent - x_next) / float(funclog(self.xmax) - xmin) right = -1 for k in range(len(xlabels)): xlabels[k].text = self._format_xlabel_text(funcexp(xpoints[k])) # update the size so we can center the labels on ticks xlabels[k].texture_update() xlabels[k].size = xlabels[k].texture_size xlabels[k].pos = tuple( map(int, (x_next + (xpoints[k] - xmin) * ratio - xlabels[k].texture_size[0] / 2., y_next))) if xlabels[k].x < right: x_overlap = True right = xlabels[k].right y_next += padding + xlabels[0].texture_size[1] # now re-center the x and y axis labels if xlabel: xlabel.x = int(x_next + (xextent - x_next) / 2. - xlabel.width / 2.) if ylabel: ylabel.y = int(y_next + (yextent - y_next) / 2. - ylabel.height / 2.) t = Matrix().translate(ylabel.center[0], ylabel.center[1], 0) t = t.multiply(Matrix().rotate(-radians(270), 0, 0, 1)) ylabel.transform = t.multiply(Matrix().translate( -int(ylabel.center_x), -int(ylabel.center_y), 0)) if x_overlap: for k in range(1, len(xlabels) - 1): xlabels[k].text = '' if y_overlap: for k in range(1, len(ylabels) - 1): ylabels[k].text = '' return x_next, y_next, xextent, yextent