def test_canvas_delete_shape(): canvas = Canvas(10, 10) line = Line(Point(0, 1), Point(9, 1)) expected_cells_after_delete = deepcopy(canvas.cells) canvas.draw_line(line) canvas.delete(Point(0, 1)) assert canvas.cells == expected_cells_after_delete
def test_draw_rectangle_command_execute(): canvas = Mock(spec=Canvas) command = DrawRectangleCommand(lambda: canvas) x1, y1, x2, y2 = 1, 1, 10, 10 rectangle = Rectangle(Point(x1, y1), Point(x2, y2)) command.execute(x1, y1, x2, y2) canvas.draw_rectangle.assert_called_once_with(rectangle)
def test_draw_line_command_execute(): canvas = Mock(spec=Canvas) command = DrawLineCommand(lambda: canvas) x1, y1, x2, y2 = 1, 1, 1, 10 line = Line(Point(x1, y1), Point(x2, y2)) command.execute(x1, y1, x2, y2) canvas.draw_line.assert_called_once_with(line)
def test_canvas_undo_bucket_fill(): canvas = Canvas(10, 10) line = Line(Point(0, 2), Point(9, 2)) canvas.draw_line(line) expected_cells_after_undo = deepcopy(canvas.cells) canvas.bucket_fill(Point(0, 0), 'o') canvas.undo() assert canvas.cells == expected_cells_after_undo
def test_canvas_draw_line_vertical(): canvas = Canvas(50, 50) from_point = Point(3, 3) to_point = Point(3, 35) line = Line(from_point, to_point) canvas.draw_line(line) for point in line.get_points(): assert canvas.cells[point.x][point.y] == (CanvasCellContentType.Line, 'x')
def test_canvas_undo_draw_rectangle(): canvas = Canvas(10, 10) line = Line(Point(0, 2), Point(9, 2)) canvas.draw_line(line) expected_cells_after_undo = deepcopy(canvas.cells) rectangle = Rectangle(Point(1, 1), Point(3, 3)) canvas.draw_rectangle(rectangle) canvas.undo() assert canvas.cells == expected_cells_after_undo
def test_canvas_undo_draw_line(): canvas = Canvas(10, 10) line1 = Line(Point(0, 2), Point(9, 2)) canvas.draw_line(line1) expected_cells_after_undo = deepcopy(canvas.cells) line2 = Line(Point(0, 5), Point(9, 5)) canvas.draw_line(line2) canvas.undo() assert canvas.cells == expected_cells_after_undo
def test_canvas_delete_colour(): width, height = 10, 10 canvas = Canvas(width, height) line = Line(Point(0, 1), Point(9, 1)) canvas.draw_line(line) expected_cells_after_delete = deepcopy(canvas.cells) canvas.bucket_fill(Point(0, 0), 'o') canvas.delete(Point(0, 0)) assert canvas.cells == expected_cells_after_delete
def test_canvas_draw_rectangle(): canvas = Canvas(50, 50) top_left = Point(3, 3) bottom_right = Point(10, 10) rectangle = Rectangle(top_left, bottom_right) canvas.draw_rectangle(rectangle) for line in rectangle.get_lines(): for point in line.get_points(): assert canvas.cells[point.x][point.y] == ( CanvasCellContentType.Line, 'x')
def test_line_get_points_for_vertical_line(): from_point = Point(1, 1) to_point = Point(5, 1) expected_points = [ from_point, Point(2, 1), Point(3, 1), Point(4, 1), to_point ] line = Line(from_point, to_point) points = line.get_points() assert points == expected_points
def test_line_get_points_for_horizontal_line(): from_point = Point(1, 1) to_point = Point(1, 5) expected_points = [ from_point, Point(1, 2), Point(1, 3), Point(1, 4), to_point ] line = Line(from_point, to_point) points = line.get_points() assert points == expected_points
def test_canvas_draw_line_fails_when_a_point_is_out_of_bounds(): canvas = Canvas(50, 50) from_point1 = Point(3, 3) to_point1 = Point(3, 350) line1 = Line(from_point1, to_point1) with pytest.raises(OutOfCanvasBoundError): canvas.draw_line(line1) from_point2 = Point(3, 300) to_point2 = Point(3, 35) line2 = Line(from_point2, to_point2) with pytest.raises(OutOfCanvasBoundError): canvas.draw_line(line2)
def test_canvas_draw_rectangle_fails_when_a_point_is_out_of_bounds(): canvas = Canvas(50, 50) top_left1 = Point(3, 350) bottom_right1 = Point(10, 10) rectangle1 = Rectangle(top_left1, bottom_right1) with pytest.raises(OutOfCanvasBoundError): canvas.draw_rectangle(rectangle1) top_left2 = Point(3, 3) bottom_right2 = Point(10, 100) rectangle2 = Rectangle(top_left2, bottom_right2) with pytest.raises(OutOfCanvasBoundError): canvas.draw_rectangle(rectangle2)
def test_rectangle_get_lines(): top_right = Point(1, 1) top_left = Point(5, 1) bottom_left = Point(5, 5) bottom_right = Point(1, 5) rectangle = Rectangle(top_left, bottom_right) expected_lines = [ Line(top_left, top_right), Line(top_left, bottom_left), Line(top_right, bottom_right), Line(bottom_left, bottom_right) ] lines = rectangle.get_lines() assert lines == expected_lines
def execute(self, *args): #pylint: disable=invalid-name """ Draw a line on the canvas Args: x1, y1, x2, y2: the int coordinates of (x1, y1) and (x2, y2) between which the line will be drawn """ if len(args) < 4: raise ValueError("4 arguments expected (x1, y1, x2, y2)") x1, y1, x2, y2 = args[0], args[1], args[2], args[3] line = Line(Point(x1, y1), Point(x2, y2)) self.get_canvas_fn().draw_line(line)
def execute(self, *args): #pylint: disable=invalid-name """ Draw a rectangle on the canvas Args: x1, y1, x2, y2: the int coordinates of (x1, y1), the top-left corner of the rectangle and (x2, y2), the bottom-right corner of the rectangle """ if len(args) < 4: raise ValueError("4 arguments expected (x1, y1, x2, y2)") x1, y1, x2, y2 = args[0], args[1], args[2], args[3] rectangle = Rectangle(Point(x1, y1), Point(x2, y2)) self.get_canvas_fn().draw_rectangle(rectangle)
def test_canvas_bucket_fill_shape(): width, height = 10, 10 canvas = Canvas(width, height) line = Line(Point(0, 1), Point(9, 1)) canvas.draw_line(line) canvas.bucket_fill(Point(0, 1), 'o') #line has been filled for point in line.get_points(): assert canvas.cells[point.x][point.y] == (CanvasCellContentType.Line, 'o') #rest have not been filled for x in range(width): for y in range(height): if Point(x, y) not in line.get_points(): assert canvas.cells[x][y] == (CanvasCellContentType.Empty, ' ')
def test_bucket_fill_command_execute(): canvas = Mock(spec=Canvas) command = BucketFillCommand(lambda: canvas) x, y = 42, 69 target_point, colour = Point(x, y), 'c' command.execute(x, y, colour) canvas.bucket_fill.assert_called_once_with(target_point, colour)
def test_delete_command_execute(): canvas = Mock(spec=Canvas) command = DeleteCommand(lambda: canvas) x1, y1 = 42, 69 target_point = Point(x1, y1) command.execute(x1, y1) canvas.delete.assert_called_once_with(target_point)
def test_canvas_draw_point(): width, height = 50, 50 canvas = Canvas(width, height) x, y = 2, 3 point = Point(x, y) canvas._draw_point(point) assert canvas.cells[x][y] == (CanvasCellContentType.Line, 'x')
def test_canvas_bucket_fill_area(): width, height = 10, 10 canvas = Canvas(width, height) line = Line(Point(0, 2), Point(9, 2)) canvas.draw_line(line) canvas.bucket_fill(Point(0, 0), 'o') #top area have been filled for x in range(width): assert (canvas.cells[x][0] == (CanvasCellContentType.Empty, 'o') and canvas.cells[x][1] == (CanvasCellContentType.Empty, 'o')) #bottom area have not been filled for y in range(3, height): assert canvas.cells[x][y] == (CanvasCellContentType.Empty, ' ') #line have not been filled for point in line.get_points(): assert canvas.cells[point.x][point.y] == (CanvasCellContentType.Line, 'x')
def test_canvas_str(): expected_canvas_str = (" ----- " "\n" "| |" "\n" "|xxxxx|" "\n" "|ooooo|" "\n" "|xxxxx|" "\n" "| |" "\n" " ----- ") canvas = Canvas(5, 5) canvas.draw_line(Line(Point(0, 1), Point(4, 1))) canvas.draw_line(Line(Point(0, 3), Point(4, 3))) canvas.bucket_fill(Point(2, 2), 'o') assert str(canvas) == expected_canvas_str
def execute(self, *args): #pylint: disable=invalid-name """ Delete a shape or reset a zone colour Args: x, y: the int coordinates of the point from which to delete the connected shape or zone """ if len(args) < 2: raise ValueError("2 arguments expected (x, y)") x, y = args[0], args[1] self.get_canvas_fn().delete(Point(x, y))
def execute(self, *args): #pylint: disable=invalid-name """ Paint a shape or fill a zone with a given colour Args: x, y: the int coordinates of the point from which to paint the connected shape or zone colour: the colour to paint with """ if len(args) < 2: raise ValueError("3 arguments expected (x, y, colour)") x, y, colour = args[0], args[1], args[2] self.get_canvas_fn().bucket_fill(Point(x, y), colour)
def test_rectangle_initialize(): top_left = Point(1, 1) bottom_right = Point(5, 5) rectangle = Rectangle(top_left, bottom_right) assert rectangle.top_left_point == top_left and rectangle.bottom_right_point == bottom_right
def mouse_local_position(event): return Point(event.x_root - widget.winfo_rootx(), event.y_root - widget.winfo_rooty())
def points(type): for x, c in enumerate(compound_samples): yield Point( x, _apply_scale( self.raw_from_value(self.value_from_raw(c[type]))))
def render(self, target_canvas, canvas_offset, canvas_size): target_canvas.begin(canvas_size) #x = canvas_offset.x #y = canvas_offset.y # create interaction callbacks class PlotArea: def wheel(local_pos, scroll_direction): center = local_pos.x if scroll_direction < 0: self.view_position -= center * self.view_zoom self.view_zoom *= 2 else: self.view_zoom //= 2 self.view_position += center * self.view_zoom self.on_change_callback() class LeftAxis: def wheel(local_pos, scroll_direction): if scroll_direction < 0: self.scale_zoom /= 2 else: self.scale_zoom *= 2 self.on_change_callback() def L_start_drag(initial_mouse_pos): # store the initial position as a starting point for later starting_point = self.scale_position # create drag handler function def L_drag(local_pos): self.scale_position = starting_point + ( local_pos.y - initial_mouse_pos.y) / self.scale_zoom self.on_change_callback() return L_drag class BottomAxis: def wheel(local_pos, scroll_direction): center = local_pos.x if scroll_direction < 0: self.view_position -= center * self.view_zoom self.view_zoom *= 2 else: self.view_zoom //= 2 self.view_position += center * self.view_zoom self.on_change_callback() def L_start_drag(initial_mouse_pos): # store the initial position as a starting point for later starting_point = self.view_position # create drag handler function def L_drag(local_pos): self.view_position = starting_point - ( local_pos.x - initial_mouse_pos.x) * self.view_zoom self.on_change_callback() return L_drag self.mouse_event_dispatcher = MouseEventDispatcher() self.mouse_event_dispatcher.add_handler(PlotArea) self.mouse_event_dispatcher.add_handler(LeftAxis) self.mouse_event_dispatcher.add_handler(BottomAxis) # split render area into segments border = 30 left_margin = 80 bottom_margin = 50 plot_height = canvas_size.y - border * 2 - bottom_margin PlotArea.rect = Rectangle( Point(border + left_margin, border), Point(canvas_size.x - border, border + plot_height)) LeftAxis.rect = Rectangle( Point(border, border), Point(border + left_margin, border + plot_height)) BottomAxis.rect = Rectangle( Point(border + left_margin, border + plot_height), Point(canvas_size.x - border, border + plot_height + bottom_margin)) # limit the zoom and scroll interaction if self.scale_position < 0: self.scale_position = 0 if self.view_zoom < 1: self.view_zoom = 1 self.view_position = min( self.view_position, self.header.sample_count - canvas_size.x * self.view_zoom) self.view_position = max(self.view_position, 0) # setup source of data compounder = self.data_source.get_level(self.view_zoom) # render line of signal compounder.goto_sample_index(self.view_position) def _apply_scale(value): value = (value - self.scale_position) * self.scale_zoom if value < 0: value = 0 if value > PlotArea.rect.height: value = PlotArea.rect.height return value # generate compound(min, avg, max) sample list compound_samples = [] x = 0 try: while x < PlotArea.rect.width: x += 1 compound_samples.append( compounder.get_next_compound(self.view_zoom)) except EOFError: pass # extracting data from the list def points(type): for x, c in enumerate(compound_samples): yield Point( x, _apply_scale( self.raw_from_value(self.value_from_raw(c[type])))) plot_canvas = canvas.CanvasTransform(target_canvas, offset=Point( PlotArea.rect.left, PlotArea.rect.bottom), scale=Point(1, -1)) # draw data plot_canvas.pen(dash=False) plot_canvas.pen(color=(255, 192, 192), width=1) plot_canvas.draw_polygon( itertools.chain(points(0), reversed(list(points(2))))) # draw grid try: def float_range(start, end, step): while start <= end: yield start start += step # minimal distance between two vertical grid lines dx_min = 70 value_start = self.value_from_x(0) value_end = self.value_from_x(PlotArea.rect.width) dvalue_min = self.value_from_x(dx_min) - value_start precision = -int(math.floor(math.log(dvalue_min, 10))) # find the best decimal step decimal_value_step = 10**-precision value_step = first(lambda x: x >= dvalue_min, [ 1 * decimal_value_step, 2 * decimal_value_step, 5 * decimal_value_step, 10 * decimal_value_step ]) for value in float_range(round(value_start - 0.5, precision - 1), value_end, value_step): if value < value_start or value > value_end: continue x = self.x_from_value(value) plot_canvas.pen(color=(0, 0, 0), width=0.5, dash=True) plot_canvas.draw_line( [Point(x, 0), Point(x, PlotArea.rect.height)]) plot_canvas.pen(color=(0, 0, 0), width=0.5, dash=False) plot_canvas.draw_line([Point(x, 0), Point(x, -10)]) if self.raw_labels: segment_label = "#{}".format( self.sample_number_from_value(value)) else: segment_label = "{}".format(round(value, precision) + 0) target_canvas.draw_text(BottomAxis.rect.p1 + Point(x, 10), segment_label, size=16, anchor=(0, -1)) # determine plot boundaries value_start = self.value_from_y(0) value_end = self.value_from_y(PlotArea.rect.height) # minimal vertical distance between two horizontal grid lines dy_min = 35 while True: # minimal value step between two horizontal grid lines dvalue_min = self.value_from_y(dy_min) - value_start if dvalue_min > 0: break # if we zoomed in too much, draw lines less often dy_min = dy_min * 2 # number of digits displayed in decimal representation of the values precision = -int(math.floor(math.log(dvalue_min, 10))) # find the best decimal step decimal_value_step = 10**-precision value_step = first(lambda x: x >= dvalue_min, [ 1 * decimal_value_step, 2 * decimal_value_step, 5 * decimal_value_step, 10 * decimal_value_step ]) for value in float_range(ceil_by_multiple(value_start, value_step), value_end + value_step, value_step): if value < epsilon: value = 0 y = self.y_from_value(value) if y < 0 or y > PlotArea.rect.height: continue plot_canvas.pen(color=(0, 0, 0), width=0.5, dash=True) plot_canvas.draw_line( [Point(0, y), Point(PlotArea.rect.width, y)]) plot_canvas.pen(color=(0, 0, 0), width=0.5, dash=False) plot_canvas.draw_line([Point(0, y), Point(-10, y)]) if self.raw_labels: segment_label = "{:04X}".format(self.raw_from_value(value)) else: segment_label = int(round(value, precision) * 1000) target_canvas.draw_text(LeftAxis.rect.p2 - Point(10, y), segment_label, size=16, anchor=(1, 0)) target_canvas.draw_text(BottomAxis.rect.p1 + Point(BottomAxis.rect.width / 2, 48), "seconds", size=16, anchor=(0, -1)) target_canvas.draw_text(LeftAxis.rect.p1 + Point(0, LeftAxis.rect.height / 2), "mA", size=16, anchor=(0, 0)) except ZeroDivisionError: pass # some values were missing to calculate, nothing can be done plot_canvas.pen(dash=False) plot_canvas.pen(color=(0, 0, 0), width=2) plot_canvas.draw_line([ Point(0, 0), Point(0, PlotArea.rect.height), Point(PlotArea.rect.width, PlotArea.rect.height), Point(PlotArea.rect.width, 0), Point(0, 0) ]) plot_canvas.pen(color=(255, 0, 0), width=1) plot_canvas.draw_line(points(1)) target_canvas.end()
# themselves. from canvas import Canvas, Point, Vector import numpy as np # Each Canvas object represents a single display of axes, similar to a # matplotlib "Figure" object. The size is specified in inches by # default, but this can be changed to other units, such as cm or pt. # We also specify 8 pt font instead of the default of 12. For # demonstration, we uset he font "Impact", but the default is the much # more sensible Helvetica Neue LT. c = Canvas(5, 3, "inches", fontsize=8, font="Impact") # Create a square scatterplot axis, which we will name "scatter". Put # it on the left side with left and bottom margin of .55 in and top margin of .2 in c.add_axis("scatter", Point(.55, .55, "inches"), Point(2.8, 2.8, "inches")) # Now generate some data which we can plot on this axis. np.random.seed(0) data = np.random.random((2, 10)) # To plot the data, first we need to get the matplotlib axis object # which corresponds to the "scatter" axis. Once we have it, we can # use it like any ordinary matplotlib axis. ax = c.ax("scatter") ax.scatter(data[0], data[1]) ax.set_xlabel("$\\sum_i \\theta_i$") ax.set_ylabel("$\\Delta$Y") # To see what we have so far, we can use the show() function. Note # that, unlike matplotlib's show() function, this will not clear the
def test_line_initialize(): point1 = Point(1, 1) point2 = Point(5, 1) line = Line(point1, point2) assert line.from_point == point1 and line.to_point == point2