class FractalCanvas(Canvas): @property def fragment_shader(self): replacements = { '#define FUNCTION(z) (VECTOR2(0.0, 0.0))': f'#define FUNCTION(z) ({self.functions[self.function_index].function_gl})', '#define DERIVATIVE(z) (VECTOR2(1.0, 0.0))': f'#define DERIVATIVE(z) ({self.functions[self.function_index].derivative_gl})', '#define ROOTS (VECTOR2[](VECTOR2(0.0, 0.0)))': f'#define ROOTS (VECTOR2[]({self.functions[self.function_index].roots_gl}))' } return re.compile('|'.join( re.escape(key) for key in replacements.keys())).sub( lambda match: replacements[match.group(0)], self.fragment_shader_template) @property def function_info(self): return f'f(z) = {self.functions[self.function_index].function_py.replace(" ** ", "^").replace("*", "·")}\n' + \ f'f\'(z) = {self.functions[self.function_index].derivative_py.replace(" ** ", "^").replace("*", "·")}' @property def pixel_to_complex_transform(self): return array([[ self.scale / self.size[0], 0.0, self.center[0] - 0.5 * self.scale ], [ 0.0, -self.scale / self.size[0], 0.5 * self.size[1] / self.size[0] * self.scale + self.center[1] ], [0.0, 0.0, 1.0]]) @property def complex_to_pixel_transform(self): return inv(self.pixel_to_complex_transform) # noinspection PyShadowingNames def __init__(self, vertex_shader, fragment_shader_template, functions, *args, **kwargs): super().__init__(*args, **kwargs) self.vertex_shader = vertex_shader self.fragment_shader_template = fragment_shader_template self.functions = functions self.function_index = 0 self.program = Program(self.vertex_shader, self.fragment_shader) self.program['position'] = [(-1.0, -1.0), (-1.0, 1.0), (1.0, 1.0), (-1.0, -1.0), (1.0, 1.0), (1.0, -1.0)] self.program['resolution'] = self.size self.center = array([0.0, 0.0]) self.program['center'] = array( [*unpack_double(self.center[0]), *unpack_double(self.center[1])]) self.center_min, self.center_max = array([-10.0, -10.0]), array([10.0, 10.0]) self.scale = 2.5 self.program['scale'] = unpack_double(self.scale) self.scale_min, self.scale_max = 10.0**-10.0, 10.0**2.0 self.line = LinePlotVisual(array([[-10, -10]]), color='white') self.position_text = TextVisual('', color='white', font_size=10, anchor_x='right', anchor_y='top') self.iterations_text = TextVisual('', color='white', font_size=10, anchor_x='left', anchor_y='top') self.info_text = TextVisual(self.function_info, pos=(5, 5), color='white', font_size=10, anchor_x='left', anchor_y='bottom') if use_app().backend_name == 'PyQt5': self._backend.leaveEvent = self.on_mouse_exit self.timer = Timer(connect=self.update, start=True) self.show() def on_draw(self, event): self.program.draw() self.line.draw() self.position_text.draw() self.iterations_text.draw() self.info_text.draw() def on_resize(self, event): self.program['resolution'] = self.size self.line.transforms.configure(canvas=self, viewport=(0, 0, *self.size)) self.position_text.transforms.configure(canvas=self, viewport=(0, 0, *self.size)) self.iterations_text.transforms.configure(canvas=self, viewport=(0, 0, *self.size)) self.info_text.transforms.configure(canvas=self, viewport=(0, 0, *self.size)) def on_mouse_exit(self, event): self.on_mouse_handler('mouse_exit', event) def on_mouse_move(self, event): self.on_mouse_handler('mouse_move', event) def on_mouse_release(self, event): self.on_mouse_handler('mouse_release', event) def on_mouse_wheel(self, event): self.on_mouse_handler('mouse_wheel', event) def on_mouse_handler(self, event_type, event): if event_type == 'mouse_move' or event_type == 'mouse_wheel': if event.type == 'mouse_wheel': self.zoom(0.9 if event.delta[1] > 0.0 else 1.0 / 0.9, event.pos) self.newton_method(event.pos) if event.is_dragging and event.buttons[0] == 1: new_position_complex = dot( self.pixel_to_complex_transform, array([[event.pos[0]], [event.pos[1]], [1.0]])) old_position_complex = dot( self.pixel_to_complex_transform, array([[event.last_event.pos[0]], [event.last_event.pos[1]], [1.0]])) self.translate((new_position_complex - old_position_complex)[:2].flatten()) elif event_type == 'mouse_release': if event.last_event.is_dragging: return old_function_index = self.function_index self.function_index = (self.function_index + (1 if event.button == 1 else (-1 if event.button == 2 else 0))) % len( self.functions) new_function_index = self.function_index if new_function_index != old_function_index: self.program.set_shaders(vert=self.vertex_shader, frag=self.fragment_shader) self.newton_method(event.pos) self.info_text.text = self.function_info elif event_type == 'mouse_exit': self.line.set_data(array([[-10, -10]])) self.position_text.pos = (0, 0) self.iterations_text.pos = (0, 0) def newton_method(self, position_pixel): position_complex = dot( self.pixel_to_complex_transform, array([[position_pixel[0]], [position_pixel[1]], [1.0]])) z_0 = complex(*position_complex[:2].flatten()) z_n, iterations = newton_method( z=z_0, function_string=self.functions[self.function_index].function_py, derivative_string=self.functions[ self.function_index].derivative_py) # noinspection PyTypeChecker self.line.set_data(array([ dot(self.complex_to_pixel_transform, array([[z[0]], [z[1]], [1.0]]))[:2].flatten() for z in z_n ]), edge_width=0) self.position_text.text = '{:.3e}\n{:.3e}'.format( *position_complex[:2].flatten()) self.position_text.pos = position_pixel + array([-5, -5]) self.iterations_text.text = f'\n{iterations}' self.iterations_text.pos = dot( self.complex_to_pixel_transform, array([[z_n[-1][0]], [z_n[-1][1]], [1.0]]))[:2].flatten() + array( [5, -5]) def translate(self, delta_complex): self.center = clip(self.center - delta_complex, self.center_min, self.center_max) self.program['center'] = array( [*unpack_double(self.center[0]), *unpack_double(self.center[1])]) def zoom(self, factor, position_pixel): old_position_complex = dot( self.pixel_to_complex_transform, array([[position_pixel[0]], [position_pixel[1]], [1.0]])) self.scale = clip(self.scale * factor, self.scale_min, self.scale_max) self.program['scale'] = unpack_double(self.scale) new_position_complex = dot( self.pixel_to_complex_transform, array([[position_pixel[0]], [position_pixel[1]], [1.0]])) self.translate( (new_position_complex - old_position_complex)[:2].flatten())
class Canvas(app.Canvas): def __init__(self): app.Canvas.__init__(self, size=(512, 512), title='Particle Renderer', keys='interactive') # enable geometry shader gloo.gl.use_gl('gl+') # load and compile shader with open('shader/particleRenderer.frag', 'r') as file: fragmentString = file.read() with open('shader/particleRenderer.geom', 'r') as file: geomString = file.read() with open('shader/particleRenderer.vert', 'r') as file: vertexString = file.read() self.program = Program() self.program.set_shaders(vertexString, fragmentString, geomString, True) ###################################### # settings # when changing near/far or fov you have to call resetProjection() for the changes to take effect # everything closer to the camera than near ot further away than far will not be drawn self.nearDistance = 0.1 self.farDistance = 20 self.fov = 45.0 # field of view in degree # initial camera position # call resetCamera or press "R" to go the initial position self.initialCamPosition = glm.vec3(3, 3, 3) self.initialCamTarget = glm.vec3(0, 0, 0) # you can change settings of the camera yourself with self.cam.xxx = yyy # you can also move the camera by calling setPosition / setTarget and # thereby predefine an animation (eg for videos/talks) # you can also change the camera control mode 1 = fly camera, 0 = trackball camera self.cam = Camera(1, self.initialCamPosition, self.initialCamTarget) # the CameraInputHandler links the camera to keybiard and mouse input, you can also change keybindings there self.camInputHandler = CameraInputHandler(self.cam) # // positions where spheres are rendered self.program['input_position'] = [(0, 0, 0), (1, 1, 1), (1, 1, -1), (1, -1, 1), (1, -1, -1), (-1, 1, 1), (-1, 1, -1), (-1, -1, 1), (-1, -1, -1)] # vector field for color # self.program['input_vector'] = # scalar field, used for color in color mode 3 self.program['input_scalar'] = np.array( [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9], dtype=np.float32) # radius per particle, set self.program['enableSizePerParticle'] = True to enable self.program['input_radius'] = [[0.15], [0.1], [0.2], [0.1], [0.2], [0.05], [0.1], [0.15], [0.1]] # size self.program[ 'enableSizePerParticle'] = False # enable this and set a size for each particle above self.program[ 'sphereRadius'] = 0.15 # radius of the spheres if "size per particle" is disabled # background gloo.set_state(clear_color=(0.30, 0.30, 0.35, 1.00)) # background color # transfer function / color self.program[ 'colorMode'] = 3 # 1: color by vector field direction, 2: color by vector field magnitude, 3: color by scalar field, 0: constant color self.program['defaultColor'] = (1, 1, 1 ) # particle color in color mode 0 self.program[ 'lowerBound'] = 0.0 # lowest value of scalar field / vector field magnitude self.program[ 'upperBound'] = 1.0 # lowest value of scalar field / vector field magnitude self.program[ 'customTransferFunc'] = True # set to true to use a custom transfer function # Transfer function uses a 1D Texture. # Provide 1D list of colors (r,g,b) as the textures data attribute, colors will be evenly spread over # the range [lowerBound,upperBound]. Meaning particles where the scalar is equal to lowerBound will # have the first specified color, the one with a scalar equal to lowerBound will have the last. Values that # lie in between the colors are interpolated linearly self.program['transferFunc'] = gloo.Texture1D( format='rgba', interpolation='linear', internalformat='rgba8', data=cm.viridis(range(256)).astype(np.float32)) # sphere look self.program['brightness'] = 1 # additional brightness control self.program[ 'materialAlpha'] = 1.0 # set lower than one to make spheres self.program['materialShininess'] = 4.0 # material shininess self.program[ 'useTexture'] = False # use the below texture for coloring (will be tinted according to set color) # provide a numpy array of shaper (x,y,3) for rgb pixels of the 2d image self.program['colorTexture'] = gloo.Texture2D( format='rgb', interpolation='linear', internalformat='rgb8', data=_checkerboard().astype(np.float32)) self.program[ 'uvUpY'] = False # rotates texture by 90 degrees so that sphere poles are along the Y axis # light settings self.program['lightPosition'] = (500, 500, 1000 ) # position of the ligth self.program['lightDiffuse'] = (0.4, 0.4, 0.4 ) # diffuse color of the light self.program['lightSpecular'] = (0.3, 0.3, 0.3 ) # specular color of the light self.program['ambientLight'] = (0.1, 0.1, 0.1) # ambient light color self.program[ 'lightInViewSpace'] = True # should the light move around with the camera? # settings for flat shading self.program[ 'renderFlatDisks'] = False # render flat discs instead of spheres self.program[ 'flatFalloff'] = False # when using flat discs, enable this darken the edges # settings for additional depth cues self.program[ 'enableEdgeHighlights'] = False # add black edge around the particles for better depth perception # style of blending self.useAdditiveBlending(False) # orientation helper self.enableOrientationIndicator = True # enable / disable orientation indicator in the lower left self.enableOriginIndicator = True # enable / disable orientation indicator at the origin self.originIndicatorSize = 1.0 # change size of the origin indicator # other self.program[ 'spriteScale'] = 1.1 # increase this if spheres appear to have cut off edges ############################################ # setup orientation indicator # load and compile shader with open('shader/oriIndicator.frag', 'r') as file: oriFragmentString = file.read() with open('shader/oriIndicator.vert', 'r') as file: oriVertexString = file.read() self._oriProgram = Program(oriVertexString, oriFragmentString) self._oriProgram['input_position'] = [(0, 0, 0), (1, 0, 0), (0, 0, 0), (0, 1, 0), (0, 0, 0), (0, 0, 1)] # model matrix model = np.eye(4, dtype=np.float32) self.program['model'] = model # camera and view matrix self.program['view'] = self.cam.viewMatrix # projection matrix self._projection = glm.mat4(1.0) self._oriProjection = glm.mat4(1.0) self.resetProjection() # timing self._lastTime = time.time() # show window self.show() def resetCamera(self): self.cam.setPosition(self.initialCamPosition, True) self.cam.setTarget(self.initialCamTarget, True) def useAdditiveBlending(self, enable): if enable: gloo.set_blend_func('one', 'one') gloo.set_blend_equation('func_add') gloo.set_state(depth_test=False, blend=True) else: gloo.set_state(depth_test=True, blend=False) def on_mouse_move(self, event): self.camInputHandler.on_mouse_move(event) def on_key_press(self, event): if event.key == 'R': self.resetCamera() event.handled = True else: self.camInputHandler.on_key_pressed(event) def on_key_release(self, event): self.camInputHandler.on_key_released(event) def on_mouse_wheel(self, event): self.camInputHandler.on_mouse_wheel(event) def _drawOrientationIndicator(self): gloo.set_state(line_width=5) # we want the rotation of the camera but not the translation view = glm.inverse( glm.mat4_cast(self.cam._currentTransform.orientation)) view = glm.scale(view, glm.vec3(0.25)) self._oriProgram['projection'] = self._oriProjection self._oriProgram['view'] = view self._oriProgram.draw('lines') def _drawOriginIndicator(self): gloo.set_state(line_width=2) self._oriProgram['projection'] = self._projection self._oriProgram['view'] = glm.scale( self.cam.viewMatrix, glm.vec3(self.originIndicatorSize)) self._oriProgram.draw('lines') def on_draw(self, event): # clear window content gloo.clear(color=True, depth=True) # calculate dt (time since last frame) newTime = time.time() dt = newTime - self._lastTime self._lastTime = newTime # update camera and view matrix self.camInputHandler.on_draw(dt) self.cam.update(dt) self.program['view'] = self.cam.viewMatrix # draw particles self.program.draw('points') # draw orientation indicator if self.enableOrientationIndicator: self._drawOrientationIndicator() if self.enableOriginIndicator: self._drawOriginIndicator() # update window content self.update() def on_resize(self, event): self.resetProjection() def resetProjection(self): # calculate projection gloo.set_viewport(0, 0, *self.physical_size) aspect = self.size[0] / float(self.size[1]) self._projection = perspective(self.fov, aspect, self.nearDistance, self.farDistance) self.program['projection'] = self._projection # calculate projection for the orientation indicator in the lower left orthoScale = 1 / 500 orthoProjection = glm.ortho(-self.size[0] * orthoScale, self.size[0] * orthoScale, -self.size[1] * orthoScale, self.size[1] * orthoScale) self._oriProjection = glm.translate( orthoProjection, glm.vec3(-self.size[0] * orthoScale + 0.28, -self.size[1] * orthoScale + 0.28, 0))
class Map2D(app.Canvas): def __init__(self, data_handler, parent): app.Canvas.__init__(self, size=(512, 512), title='map', keys='interactive') self.__parent__ = parent self.__data_handler = data_handler ##read building data ##fetch single building #building1 = self.__sh.get_single_at_id(1595) #nya #building2 = self.__sh.get_single_at_id(1999) #stryk #building3 = self.__sh.get_single_at_id(1886) #strykbrada #building4 = self.__sh.get_single_at_id(1722) #Gula vid bron #building5 = self.__sh.get_single_at_id(1723) # ##and its vertices #verts1 = building1.gl_ready_vertices() #verts2 = building2.gl_ready_vertices() #verts3 = building3.gl_ready_vertices() #verts4 = building4.gl_ready_vertices() #verts5 = building5.gl_ready_vertices() self.all_buildings = self.__data_handler.get_all_buildings() all_pos = self.all_buildings[0].gl_ready_vertices() for building in self.all_buildings[1:]: all_pos = np.vstack((all_pos, building.gl_ready_vertices())) abets = self.__data_handler.get_single_at_id(1886) abets = abets.gl_ready_vertices() #Problem: 977, 1061, 1342(tva stora hal), 1389(hal), 1393(hal), 1434(hal), 1327(rese) #Error 1072, 1327(rese). 1472(?!?!?), 1504(!?!?!) #for j in range(100000): # for i in range(1503, 1504): #print("----------------\n----------------",i) #all_pos = all_buildings[i].gl_ready_vertices() self.translate_by = np.mean(abets[:], 0) self.scale = 1.0 all_pos = (all_pos - self.translate_by) * self.scale allverts = np.zeros(len(all_pos), [('position', np.float32, 2)]) allverts['position'] = all_pos self.selectedverts = np.zeros(len(abets), [('position', np.float32, 2)]) self.selectedverts['position'] = (abets - self.translate_by) * self.scale self.vertices = VertexBuffer(allverts) self.selectedvertices = VertexBuffer(self.selectedverts) #self.indices = IndexBuffer(I) # Build program self.program = Program(vertex, fragment) self.program.bind(self.vertices) self.cam2 = Cam3D.Camera([0, 0, 250], [0, 0, 0], [0, 1, 0], 45.0, self.size[0] / float(self.size[1]), 0.001, 10000.0, is_2d=True) # Build view, model, projection & normal self.program['model'] = np.eye( 4, dtype=np.float32) #models are at palce from beginnings? self.program['model'] = np.transpose(vut.rotx(-10)) self.program['view'] = self.cam2.view projection = perspective(45.0, self.size[0] / float(self.size[1]), 1.0, 1000.0) self.program['projection'] = projection self.phi, self.theta = 0, 0 gloo.set_state(clear_color=(0.70, 0.70, 0.7, 1.00), depth_test=True) self.set_selected([1999]) self.activate_zoom() self.timer = app.Timer('auto', self.on_timer, start=True) self.show() def set_selected(self, ids): #building = self.__data_handler.get_single_at_id(id) #verts2 = building.gl_ready_vertices() self.selectedverts = np.zeros(0, [('position', np.float32, 2)]) for id in ids: building = self.__data_handler.get_single_at_id(id) verts = building.gl_ready_vertices() data = np.zeros(len(verts), [('position', np.float32, 2)]) data['position'] = (verts - self.translate_by) * self.scale self.selectedverts = np.concatenate([self.selectedverts, data]) #self.selectedverts['position'] = (verts2 - self.translate_by) * self.scale self.selectedvertices = VertexBuffer(self.selectedverts) selected_pos = np.mean(self.all_buildings[id].points, 0) - self.translate_by self.cam2.translate_to(selected_pos) self.program['view'] = self.cam2.view self.update() def on_mouse_press(self, event): if event.button == 1: self.cam2.is_move = True self.cam2.is_rotate = False if event.button == 2: self.cam2.is_move = False self.cam2.is_rotate = True self.cam2.prev_mouse_pos = event.pos def on_mouse_release(self, event): self.cam2.is_move = False self.cam2.is_rotate = False def on_mouse_move(self, event): if self.cam2.is_move: self.cam2.translate2d(-1 * np.array( event.pos - self.cam2.prev_mouse_pos, dtype=np.float32)) self.program['view'] = self.cam2.view self.update() elif self.cam2.is_rotate: self.cam2.rotx(-1 * np.array( (event.pos - self.cam2.prev_mouse_pos))[1:2]) self.cam2.roty(-1 * np.array( (event.pos - self.cam2.prev_mouse_pos))[0:1]) self.program['view'] = self.cam2.view self.update() self.cam2.prev_mouse_pos = event.pos def on_mouse_wheel(self, event): self.cam2.scale(event.delta[1]) self.program['view'] = self.cam2.view self.update() def on_draw(self, event): gloo.clear(color=True, depth=True) self.program.set_shaders(vertex, fragment_active) self.program.bind(self.selectedvertices) self.program.draw('triangles') self.program.set_shaders(vertex, fragment) self.program.bind(self.vertices) self.program.draw('triangles') def on_resize(self, event): self.activate_zoom() def activate_zoom(self): gloo.set_viewport(0, 0, *self.physical_size) projection = perspective(45.0, self.size[0] / float(self.size[1]), 1.0, 10000.0) self.program['projection'] = projection self.update() def on_timer(self, event): self.update() def on_resize(self, event): self.activate_zoom() def on_key_press(self, event): modifiers = [key.name for key in event.modifiers] print('Key pressed - text: %r, key: %s, modifiers: %r' % (event.text, event.key.name, modifiers)) def on_key_release(self, event): modifiers = [key.name for key in event.modifiers] print('Key released - text: %r, key: %s, modifiers: %r' % (event.text, event.key.name, modifiers))