Source code for curver.application.pieces


import curver

DEFAULT_OBJECT_COLOUR = 'black'
DEFAULT_VERTEX_COLOUR = 'black'
DEFAULT_EDGE_COLOUR = 'black'
DEFAULT_TRIANGLE_COLOUR = 'gray80'
DEFAULT_CURVE_COLOUR = 'grey40'

ARROW_FRAC = 0.55

[docs]def dot(a, b): return a[0] * b[0] + a[1] * b[1]
[docs]def intersection(A, d, B, d2): # Find the intersection parameters of A + t d and B + t2 d2 # Want t & t2 such that: # A.x + t d.x = B.x + t2 d2.x # A.y + t d.y = B.y + t2 d2.y # So: # (d.x -d2.x) (t ) = (B.x - A.x) # (d.y -d2.y) (t2) (B.y - A.y) # The inverse of this matrix is: # (-d2.y d2.x) # ( -d.y d.x) / det # where: det = d2.cross(d) # So: t = ((B.x - A.x) * -d2.y + (B.y - A.y) * d2.x) / det t2 = ((B.x - A.x) * -d.y + (B.y - A.y) * d.x) / det return t, t2
[docs]def interpolate(A, B, C, r, s): # Given points A, B, C and parameters r, s # Let X := rB + (1-r)A and # Y := sB + (1-s)C d = A - B d2 = C - B X = B + r*d Y = B + s*d2 centroid = (A + B + C) / 3 d1a = intersection(X, d.rotate(90), B, centroid - B)[0] d1b = intersection(X, d.rotate(90), A, centroid - A)[0] t = min([x for x in [d1a, d1b] if x > 0]) / 2 d2a = intersection(Y, d2.rotate(-90), B, centroid - B)[0] d2b = intersection(Y, d2.rotate(-90), C, centroid - C)[0] t2 = min([x for x in [d2a, d2b] if x > 0]) / 2 P = X + t * d.rotate(90) Q = Y + t2 * d2.rotate(-90) return X, P, Q, Y
[docs]class DrawableObject(object): def __init__(self, canvas, vertices, options): self.options = options self.canvas = canvas self.vertices = vertices self.colour = DEFAULT_OBJECT_COLOUR self.drawn = None def __repr__(self): return str(self) # Note that this means that CanvasTriangle will NOT have the same convention as AbstractTriangle, # there iterating and index accesses return edges. def __getitem__(self, index): return self.vertices[index % len(self)] def __iter__(self): return iter(self.vertices) def __len__(self): return len(self.vertices)
[docs] def set_colour(self, colour=None): self.colour = colour self.canvas.itemconfig(self.drawn, fill=self.colour)
[docs] def centre(self): return sum([v.vector for v in self.vertices], curver.application.Vector2(0, 0)) * (1.0 / len(self))
[docs] def update(self): self.canvas.coords(self.drawn, *[c for v in self for c in v])
[docs]class CanvasVertex(DrawableObject): def __init__(self, canvas, vector, options): super(CanvasVertex, self).__init__(canvas, [self], options) self.colour = DEFAULT_VERTEX_COLOUR self.vector = vector self.drawn = self.canvas.create_oval( [p + scale*self.options.dot_size for scale in [-1, 1] for p in self], outline=self.colour, fill=self.colour, tag='oval' ) def __str__(self): return str(self.vector) def __sub__(self, other): return self.vector - other.vector # We have to redo these manually. def __iter__(self): return iter(self.vector) def __contains__(self, point): return all(abs(c - v) < self.options.epsilon for c, v in zip(point, self))
[docs] def update(self): self.canvas.coords(self.drawn, *[p + scale*self.options.dot_size for scale in [-1, 1] for p in self])
[docs]class CanvasEdge(DrawableObject): def __init__(self, canvas, vertices, label, colour, options): super(CanvasEdge, self).__init__(canvas, vertices, options) self.label = label self.colour = DEFAULT_EDGE_COLOUR if colour is None else colour m = (1-ARROW_FRAC)*self.vertices[0].vector + ARROW_FRAC*self.vertices[1].vector self.drawn = [ # Need two lines really so arrows work correctly. self.canvas.create_line( [c for v in [self.vertices[0], m] for c in v], width=self.options.line_size, fill=self.colour, tags=['line', 'line_start'], arrowshape=self.options.arrow_shape ), self.canvas.create_line( [c for v in self.vertices for c in v], width=self.options.line_size, fill=self.colour, tags=['line', 'line_end'], arrowshape=self.options.arrow_shape ) ] self.in_triangles = [] def __str__(self): return str(self.vertices)
[docs] def hide(self, hide=False): for drawn in self.drawn: self.canvas.itemconfig(drawn, state='hidden' if hide else 'normal')
[docs] def update(self): m = (1-ARROW_FRAC)*self.vertices[0].vector + ARROW_FRAC*self.vertices[1].vector self.canvas.coords(self.drawn[0], *[c for v in [self.vertices[0], m] for c in v]) self.canvas.coords(self.drawn[1], *[c for v in self.vertices for c in v])
[docs]class CanvasTriangle(DrawableObject): def __init__(self, canvas, edges, options): super(CanvasTriangle, self).__init__(canvas, list(set(v for e in edges for v in e)), options) self.colour = DEFAULT_TRIANGLE_COLOUR self.edges = edges # We reorder the vertices to guarantee that the vertices are cyclically ordered anticlockwise in the plane. if (self[1] - self[0]).cross(self[2] - self[0]) > 0: self.vertices = [self[0], self[2], self[1]] # Now we reorder the edges such that edges[i] does not meet vertices[i]. self.edges = [edge for vertex in self for edge in self.edges if vertex not in edge.vertices] # And check to make sure everyone made it through alive. assert len(self.edges) == 3 assert self[0] != self[1] and self[1] != self[2] and self[2] != self[0] assert self.edges[0] != self.edges[1] and self.edges[1] != self.edges[2] and self.edges[2] != self.edges[0] self.drawn = self.canvas.create_polygon([c for v in self for c in v], fill=self.colour, tag='polygon') # Add this triangle to each edge involved. for edge in self.edges: edge.in_triangles.append(self) def __str__(self): return str(self.vertices)
[docs]class CurveComponent(DrawableObject): def __init__(self, canvas, vertices, options, thin=True, smooth=False): super(CurveComponent, self).__init__(canvas, vertices, options) self.colour = DEFAULT_CURVE_COLOUR if thin: self.drawn = self.canvas.create_line( [c for v in self.vertices for c in v], width=self.options.line_size, fill=self.colour, tag='curve', smooth=smooth, splinesteps=50 ) else: self.drawn = self.canvas.create_polygon( [c for v in self.vertices for c in v], fill=self.colour, tag='curve', outline=self.colour, smooth=smooth, splinesteps=50 )