def viewport(self) -> Rect: widget = self.builder.get_object('drawing_area') return Rect(min=Vec2(0, 0), max=Vec2( widget.get_allocated_width(), widget.get_allocated_height(), )).with_margin(10)
def liang_barsky_line_clip(line: Line) -> Optional[Line]: start, end = line.vertices_ndc p1 = start.x - end.x p2 = -p1 p3 = start.y - end.y p4 = -p3 q1 = start.x - (-1) q2 = 1 - start.x q3 = start.y - (-1) q4 = 1 - start.y posarr = [1.0 for _ in range(5)] negarr = [0.0 for _ in range(5)] if (p1 == 0 and q1 < 0 or p3 == 0 and q3 < 0): return None if p1 != 0: r1 = q1 / p1 r2 = q2 / p2 if p1 < 0: negarr.append(r1) posarr.append(r2) else: negarr.append(r2) posarr.append(r1) if p3 != 0: r3 = q3 / p3 r4 = q4 / p4 if p3 < 0: negarr.append(r3) posarr.append(r4) else: negarr.append(r4) posarr.append(r3) rn1 = max(negarr) rn2 = min(posarr) if rn1 > rn2: return None xn1 = start.x + p2 * rn1 yn1 = start.y + p4 * rn1 xn2 = start.x + p2 * rn2 yn2 = start.y + p4 * rn2 new_line = copy.deepcopy(line) new_line.vertices_ndc = [Vec2(xn1, yn1), Vec2(xn2, yn2)] return new_line
def on_resize(self, widget: Gtk.Widget, allocation: Gdk.Rectangle): if self.scene.window is None: w, h = allocation.width, allocation.height self.old_size = allocation self.scene.window = Window(Vec2(-w / 2, -h / 2), Vec2(w / 2, h / 2)) w_proportion = allocation.width / self.old_size.width h_proportion = allocation.height / self.old_size.height self.scene.window.max = Vec2(self.scene.window.max.x * w_proportion, self.scene.window.max.y * h_proportion) self.scene.window.min = Vec2(self.scene.window.min.x * w_proportion, self.scene.window.min.y * h_proportion) self.old_size = allocation self.scene.update_ndc()
def on_ok(self, widget): window = self.builder.get_object('new_object_window') notebook = self.builder.get_object('notebook1') page_num = notebook.get_current_page() name = entry_text(self, 'entry_name') if NB_PAGES[page_num] == 'point': x = float(entry_text(self, 'entryX')) y = float(entry_text(self, 'entryY')) self.dialog.new_object = Point(Vec2(x, y), name=name) elif NB_PAGES[page_num] == 'line': y2 = float(entry_text(self, 'entryY2')) x1 = float(entry_text(self, 'entryX1')) y1 = float(entry_text(self, 'entryY1')) x2 = float(entry_text(self, 'entryX2')) self.dialog.new_object = Line(Vec2(x1, y1), Vec2(x2, y2), name=name) elif NB_PAGES[page_num] == 'polygon': if len(self.vertices) >= 3: filled = self.builder.get_object('switch_filled').get_active() self.dialog.new_object = Polygon(self.vertices, name=name, filled=filled) elif NB_PAGES[page_num] == 'curve': if self.builder.get_object('btn_bezier').get_active(): type = 'bezier' elif self.builder.get_object('btn_b-spline').get_active(): type = 'b-spline' if len(self.vertices) >= 4: self.dialog.new_object = Curve.from_control_points( self.vertices, type=type, name=name, ) else: raise ValueError('No page with given index.') window.destroy()
def on_key_press(self, widget, event): ''' Returns: False if event can propagate, True otherwise. ''' DIRECTIONS = { Gdk.KEY_Up: Vec2(0, -10), Gdk.KEY_Down: Vec2(0, 10), Gdk.KEY_Left: Vec2(10, 0), Gdk.KEY_Right: Vec2(-10, 0), } self.pressed_keys |= {event.keyval} for key in self.pressed_keys: if key in DIRECTIONS: self.world_window.offset(DIRECTIONS[key]) self.window.queue_draw() return True
def cohen_sutherland_line_clip(line: Line) -> Optional[Line]: new_line = copy.deepcopy(line) start, end = new_line.vertices_ndc regions = [CohenRegion.region_of(v) for v in (start, end)] while True: # Both inside if all([r == CohenRegion.INSIDE for r in regions]): return new_line # Both outside (and in the same side) elif regions[0] & regions[1] != 0: return None clip_index = 0 if regions[0] != CohenRegion.INSIDE else 1 dx, dy, _ = end - start m = dx / dy if regions[clip_index] & CohenRegion.TOP != 0: x = start.x + m * (1 - start.y) y = 1 elif regions[clip_index] & CohenRegion.BOTTOM != 0: x = start.x + m * (-1 - start.y) y = -1 elif regions[clip_index] & CohenRegion.RIGHT != 0: x = 1 y = start.y + (1 - start.x) / m elif regions[clip_index] & CohenRegion.LEFT != 0: x = -1 y = start.y + (-1 - start.x) / m if clip_index == 0: start = Vec2(x, y) new_line.vertices_ndc[0] = start regions[0] = CohenRegion.region_of(start) else: end = Vec2(x, y) new_line.vertices_ndc[1] = end regions[1] = CohenRegion.region_of(end)
def clip_region(vertices, clipping_region): clipped = [] for i in range(len(vertices) - 1): v1 = vertices[i] v2 = vertices[i + 1] regions = [ CohenRegion.region_of(v) & clipping_region for v in [v1, v2] ] if all([region != clipping_region for region in regions]): clipped.extend([v1, v2]) elif all([region == clipping_region for region in regions]): continue elif any([region == clipping_region for region in regions]): clip_index = 0 if regions[0] == clipping_region else 1 dx, dy, _ = v2 - v1 m = dx / dy if clipping_region == CohenRegion.TOP: x = v1.x + m * (1 - v1.y) y = 1 elif clipping_region == CohenRegion.BOTTOM: x = v1.x + m * (-1 - v1.y) y = -1 elif clipping_region == CohenRegion.RIGHT: x = 1 y = v1.y + (1 - v1.x) / m elif clipping_region == CohenRegion.LEFT: x = -1 y = v1.y + (-1 - v1.x) / m if clip_index == 0: v1 = Vec2(x, y) else: v2 = Vec2(x, y) clipped.extend([v1, v2]) return clipped
def on_add_point(self, widget): notebook = self.builder.get_object('notebook1') page_num = notebook.get_current_page() vertice_store = self.builder.get_object('vertice_store') if NB_PAGES[page_num] == 'polygon': x = float(entry_text(self, 'entryX3')) y = float(entry_text(self, 'entryY3')) elif NB_PAGES[page_num] == 'curve': x = float(entry_text(self, 'entryX4')) y = float(entry_text(self, 'entryY4')) vertice_store.append([x, y, 1]) self.vertices.append(Vec2(x, y))
def decode(cls, obj_file: str) -> 'Scene': from scene import Scene # Returns a Scene with the window and objects found vertices = [] objs: List[GraphicObject] = [] window = None current_name = '' filled = False for line in obj_file.splitlines(): cmd, *args = line.split(' ') if cmd == 'v': vertices.append(Vec2(float(args[0]), float(args[1]))) elif cmd == 'o': current_name = ' '.join(args) elif cmd == 'usemtl': if args[0] == 'filled': filled = True elif cmd == 'p': objs.append( Point(pos=vertices[int(args[0]) - 1], name=current_name)) elif cmd == 'l': if len(args) == 2: objs.append( Line(start=vertices[int(args[0]) - 1], end=vertices[int(args[1]) - 1], name=current_name)) elif args[0] == args[-1]: objs.append( Polygon( vertices=[vertices[int(i) - 1] for i in args[:-1]], name=current_name, filled=filled)) filled = False else: objs.append( Curve( vertices=[vertices[int(i) - 1] for i in args], name=current_name, )) elif cmd == 'w': window = Window(min=vertices[int(args[0]) - 1], max=vertices[int(args[1]) - 1]) return Scene(objs=objs, window=window)
def on_press_navigation_button(self, widget): TRANSFORMATIONS = { 'nav-move-up': ('translate', Vec2(0, 10)), 'nav-move-down': ('translate', Vec2(0, -10)), 'nav-move-left': ('translate', Vec2(-10, 0)), 'nav-move-right': ('translate', Vec2(10, 0)), 'nav-rotate-left': ('rotate', -5), 'nav-rotate-right': ('rotate', 5), 'nav-zoom-in': ('scale', Vec2(1.1, 1.1)), 'nav-zoom-out': ('scale', Vec2(0.9, 0.9)), } op, *args = TRANSFORMATIONS[widget.get_name()] if op == 'translate': args[0] = (args[0] @ rotation_matrix(self.scene.window.angle)) for obj in self.selected_objs(): if op == 'translate': obj.translate(*args) elif op == 'scale': obj.scale(*args) elif op == 'rotate': try: abs_x = int(entry_text(self, 'rotation-ref-x')) abs_y = int(entry_text(self, 'rotation-ref-y')) except ValueError: abs_x = 0 abs_y = 0 ref = { RotationRef.CENTER: obj.centroid, RotationRef.ORIGIN: Vec2(0, 0), RotationRef.ABSOLUTE: Vec2(float(abs_x), float(abs_y)), }[self.rotation_ref] if isinstance(obj, GraphicObject3D): obj.rotate(args[0], 0, 0, ref) else: obj.rotate(*args, ref) obj.update_ndc(self.scene.window) self.window.queue_draw()
def on_motion(self, widget, event): def viewport_to_window(v: Vec2): viewport = self.viewport() return Vec2((v.x / viewport.width) * self.scene.window.width, (v.y / viewport.height) * self.scene.window.height) # register x, y # translate window if self.dragging: current = Vec2(-event.x, event.y) delta = viewport_to_window(current - self.press_start) window = self.scene.window m = rotation_matrix(window.angle) delta = delta @ m self.scene.translate_window(delta) self.press_start = current widget.queue_draw()
def viewport_to_window(v: Vec2): viewport = self.viewport() return Vec2((v.x / viewport.width) * self.scene.window.width, (v.y / viewport.height) * self.scene.window.height)
def on_button_press(self, widget, event): if BUTTON_EVENTS[event.button] == 'left': # register x, y self.press_start = Vec2(-event.x, event.y) self.dragging = True