class CanvasFrame(Frame): """ Canvas Frame Class Description: Contains the loaded image and sets the mouse button click event """ def __init__(self, parent, inputimage, *args, **kwargs): Frame.__init__(self, parent.frame, bg="white") # Parent self.parent = parent # Load Image self.inputimage = inputimage filepath = 'lowpolypainter/resources/images/' + inputimage self.image = Image.open(filepath) self.background = ImageTk.PhotoImage(self.image) # Create Canvas self.width = self.background.width() self.height = self.background.height() self.canvas = Canvas(self, width=self.width, height=self.height, relief='flat', borderwidth='1', highlightbackground='#DADADA', highlightthickness='1') self.backgroundId = self.canvas.create_image(0, 0, image=self.background, anchor='nw') self.canvas.grid(row=0, column=0, sticky=NSEW) # Color Object self.color = Color(np.array(self.image), 0.5, 0.5) # Tuple of boolean for face existence and the belonging face id self.selectedFace = None # Mesh self.mesh = Mesh(self) # Selection self.selected = None # Mouse Event self.mouseEventHandled = False # Focus self.focus = True # Toggle Events self.fun = False self.vertsState = NORMAL self.edgesState = NORMAL self.faceState = NORMAL # Events self.canvas.bind("<Button-1>", self.click) self.canvas.bind("<Button-2>", self.click) self.canvas.bind("<Button-3>", self.click) self.canvas.bind("<Motion>", self.motion) self.canvas.bind_all("<space>", func=self.toggleFacesCheckbutton) self.canvas.bind_all("<BackSpace>", self.deleteSelected) self.canvas.bind_all("<Key-Delete>", self.deleteSelected) self.canvas.bind_all("<Up>", func=self.toggleVertsCheckbutton) self.canvas.bind_all("<Down>", func=self.toggleEdgesCheckbutton) """ EVENT """ def click(self, event): """ Canvas Click Event Description: Adds point to canvas, will draw line to last point if control mode is POINT_AND_LINE """ self.parent.root.focus() eventPoint = [event.x, event.y] if self.mouseEventHandled: self.mouseEventHandled = False return # deselect face if self.selectedFace is not None: self.selectedFace.deselect() self.selectedFace = None if self.parent.controlMode.mode == Mode.PIPETTE: self.parent.detailFrame.colorpicker._palette_cmd() self.parent.controlMode.changeMode(Mode.POINT_AND_LINE) elif self.inBounds(eventPoint) and \ ((self.parent.controlMode.mode == Mode.POINT) or (self.parent.controlMode.mode == Mode.POINT_AND_LINE) or (event.num == NUM_MIDDLE_CLICK)): self.parent.undoManager.do(self.parent) previousSelected = self.selected zoomedCoords = self.parent.zoom.FromViewport([event.x, event.y]) self.mesh.addVertex([int(zoomedCoords[0]), int(zoomedCoords[1])]) if (previousSelected is not None) and \ (isinstance(previousSelected, Vertex)) and \ (self.parent.controlMode.mode == Mode.POINT_AND_LINE) and \ not (event.num == NUM_MIDDLE_CLICK): self.mesh.addEdge(previousSelected, self.selected) self.mouseEventHandled = False def motion(self, event): """ Canvas Motion Event """ if self.parent.controlMode.mode == Mode.PIPETTE: try: pipetteColor = self.image.getpixel((event.x, event.y)) self.parent.detailFrame.colorpicker.setPipetteColor( pipetteColor) # next line enables square color update: works but takes too much time! # self.parent.detailFrame.colorpicker._palette_cmd() except IndexError: return """ VERTICES """ def toggleVerts(self, event=None): state = NORMAL if self.vertsState is NORMAL: state = HIDDEN self.canvas.itemconfigure("v", state=state) self.vertsState = state """ EDGES """ def toggleEdges(self, event=None): state = NORMAL if self.edgesState is NORMAL: state = HIDDEN self.canvas.itemconfigure("e", state=state) self.edgesState = state """ FACE """ def toggleFaces(self, event=None): state = NORMAL if self.faceState is NORMAL: state = HIDDEN self.canvas.itemconfigure("f", state=state) self.faceState = state """ checks Button for Vertices and Edges """ def toggleVertsCheckbutton(self, event=None): self.parent.zoomAndToggleFrame.toggleFrame.vertexCheckbox.toggle() self.toggleVerts(event) """ checks Button for Vertices and Edges """ def toggleEdgesCheckbutton(self, event=None): self.parent.zoomAndToggleFrame.toggleFrame.edgesCheckbox.toggle() self.toggleEdges(event) """checks Button for Faces """ def toggleFacesCheckbutton(self, event=None): self.parent.zoomAndToggleFrame.toggleFrame.facesCheckbox.toggle() self.toggleFaces(event) """ GENERAL """ def inBounds(self, point): x, y = point[0], point[1] return (x >= 0) and (y >= 0) and (x < self.width) and (y < self.height) def select(self, object): if (object != self.selected): self.deselect(self.selected) self.selected = object def deleteSelected(self, event): if not self.focus: return self.parent.undoManager.do(self.parent) if self.selected is not None: self.selected.delete() self.selected = None def deselect(self, object): if self.selected is not None: object.deselect() def clear(self): self.selectedFace = None self.mesh.clear() """ Border """ def border(self, triangulate=False, step=0): # generate border border = Border(self.width, self.height) if triangulate: if len(self.mesh.vertices) <= 4: return coords = [] for vert in self.mesh.vertices: coords.append(vert.coords) # generate convex hull border.generateConvexHull(coords) # draw border and connect edges for site_idx in range(len(border.sites)): # draw first corner point rp = border.sites[site_idx][0] rv = self.mesh.bvertices[rp[0]][rp[1]] bp = border.sites_orientation[site_idx][0] bv = self.mesh.bvertices[bp[0]][bp[1]] if bv == 0: bv = self.mesh.addVertex(bp) self.mesh.addEdge(rv, bv) past_rv = rv past_bv = bv # draw points inbetween for point_idx in range(1, len(border.sites[site_idx]) - 1): rp = border.sites[site_idx][point_idx] rv = self.mesh.bvertices[rp[0]][rp[1]] bp = border.sites_orientation[site_idx][point_idx] bv = self.mesh.addVertex(bp) self.mesh.addEdge(rv, bv) self.mesh.addEdge(rv, past_rv) self.mesh.addEdge(rv, past_bv) self.mesh.addEdge(bv, past_bv) past_rv = rv past_bv = bv # draw last corner point rp = border.sites[site_idx][-1] rv = self.mesh.bvertices[rp[0]][rp[1]] bp = border.sites_orientation[site_idx][-1] bv = self.mesh.bvertices[bp[0]][bp[1]] if bv == 0: bv = self.mesh.addVertex(bp) self.mesh.addEdge(rv, bv) self.mesh.addEdge(rv, past_rv) self.mesh.addEdge(rv, past_bv) self.mesh.addEdge(bv, past_bv) # mark hull points # for point in border.hull: # id = self.mesh.bvertices[point[0]][point[1]].id # self.canvas.itemconfigure(id, fill='yellow') else: # draw border points border.generatePoints(step) for point in border.points: self.mesh.addVertex([int(point[0]), int(point[1])]) """ Random """ def random(self, size=0): if size == 0: return w, h = self.width - 1, self.height - 1 random_points = np.zeros((size, 2), dtype=int) for i in range(size): point = [randint(0, w), randint(0, h)] if self.mesh.bvertices[point[0]][point[1]] == 0: self.mesh.addVertex(point) """ Triangulate """ def triangulate(self, size=0, mask=None): if mask is None or len(mask[mask != 0]) == 0: mask = np.ones([self.width, self.height], dtype=bool) # Get points in mask points = [] verts = np.asarray(self.mesh.bvertices)[mask] verts = verts[verts != 0.0] # Need min 4 points if len(verts) + size <= 3: return for vert in verts: vert.deconnect() points.append(vert.coords) points = np.asarray(points) triangulate = Triangulate(self.image, points) if size != 0: triangulate.generateCanny(mask=mask) triangle = triangulate.triangulate(size) # Generate Mesh Objects for tris in triangle: self.mesh.faceToVertexGeneration(triangulate.points[tris[0]], triangulate.points[tris[1]], triangulate.points[tris[2]]) # Draw for face in self.mesh.faces: face.draw(False) for edge in self.mesh.edges: edge.draw(False) for vert in self.mesh.vertices: vert.draw(False) # inserts an image into canvas Frame by path def insert(self, path, name): self.inputimage = name if isinstance(path, basestring): self.image = Image.open(path) else: self.image = path self.background = ImageTk.PhotoImage(self.image) self.width = self.background.width() self.height = self.background.height() self.canvas.configure(width=self.width, height=self.height) self.canvas.create_image(0, 0, image=self.background, anchor=NW) self.color = Color(np.array(self.image), 0.5, 0.5) self.selectedFace = None # Mesh self.mesh = Mesh(self) # Selection self.selected = None # Mouse Event self.mouseEventHandled = False self.faceState = NORMAL ''' FUN ''' def housePartyProtocol(self): up = random.randint(0, 1) # for face in self.mesh.faces: # color = self.canvas.itemcget(face.id, 'fill') # r,g,b = int(color[1:3], 16),int(color[3:5], 16),int(color[5:7], 16) # a,b,c = random.randint(0,10), random.randint(0,10), random.randint(0,10) # if up: # color = '#%02x%02x%02x' % (min(r+a, 200),min(g+b, 200), min(b+c, 200)) # else: # color = '#%02x%02x%02x' % (max(r-a, 50),max(g-b, 50), max(b-c, 50)) # face.color = color for vert in self.mesh.vertices: coords = vert.coords x, y = random.randint(0, 2), random.randint(0, 2) if up: coords = [coords[0] + x, coords[1] + y] else: coords = [coords[0] - x, coords[1] - y] vert.move(vert.moveInBounds(coords), low=True) if self.fun: self.parent.root.after(200, self.housePartyProtocol)