Beispiel #1
0
    def Single_Room(self, Room, fig=None):
        room_w = Room.x_delta  * self.scale
        room_h = Room.y_delta  * self.scale
        wall_width = self.wall_th * self.scale
        canvas_w = room_w + 2*wall_width
        canvas_h = room_h + 2*wall_width

        if fig==None:
            canvas = Canvas(width=canvas_w, height=canvas_h)
        else:
            canvas = fig

        # Draw Room
        canvas.fill_rect(0, 0, canvas_w, height=canvas_h)
        canvas.clear_rect(wall_width, wall_width, room_w, height=room_h)

        if Room.type_r == 'Initial':
            charging = Room.charging * self.scale
            trash_bin = Room.bin * self.scale
            m_w = Room.x_delta/10 
            m_h = 2*m_w
            canvas.fill_style = 'green'
            canvas.fill_rect(charging[0]+wall_width, charging[1]+wall_width, m_w*self.scale, height=m_w*self.scale)
            canvas.fill_style = 'blue'
            canvas.fill_rect(trash_bin[0]+wall_width, trash_bin[1]+wall_width, m_w*self.scale, height=m_h*self.scale)
        else:
            canvas.fill_style = 'brown'
            p_r = 0.1
            x, y, radius = [], [], []
            for i in range(0,Room.max_piece):
                x.append(Room.pieces_loc[i,0]*self.scale + wall_width)
                y.append(Room.pieces_loc[i,1]*self.scale + wall_width)
                radius.append(p_r*self.scale)
            canvas.fill_circles(x, y, radius)
        for i in Room.connections['Location']:
            size = 4*wall_width
            canvas.fill_style = 'red' 
            ncoord = Room.Location_2_norm_coordinate[i]
            x = (ncoord[0]*(room_w-size)) 
            y = (ncoord[1]*(room_h-size)) 
            canvas.fill_rect(x+wall_width, y+wall_width, size)
        return canvas
Beispiel #2
0
class Core:
    # All constants that will be injected into global scope in the user"s cell
    global_constants = {
        "pi": pi
    }

    # All methods/fields from this class that will be exposed as global in user"s scope
    global_fields = {
        "canvas", "size", "width", "height",
        "mouse_x", "mouse_y", "mouse_is_pressed",
        "fill_style", "stroke_style",
        "clear", "background",
        "rect", "square", "fill_rect", "stroke_rect", "clear_rect",
        "fill_text", "stroke_text", "text_align",
        "draw_line",
        "circle", "fill_circle", "stroke_circle", "fill_arc", "stroke_arc",
        "print"
    }

    # All methods that user will be able to define and override
    global_methods = {
        "draw", "setup",
        "mouse_down", "mouse_up", "mouse_moved"
    }

    def __init__(self, globals_dict):
        self.status_text = display(Code(""), display_id=True)
        self._globals_dict = globals_dict
        self._methods = {}

        self.stop_button = Button(description="Stop")
        self.stop_button.on_click(self.on_stop_button_clicked)

        self.canvas = Canvas()
        self.output_text = ""
        self.width, self.height = DEFAULT_CANVAS_SIZE
        self.mouse_x = 0
        self.mouse_y = 0
        self.mouse_is_pressed = False

    ### Properties ###

    @property
    def mouse_x(self):
        return self._globals_dict["mouse_x"]

    @mouse_x.setter
    def mouse_x(self, val):
        self._globals_dict["mouse_x"] = val

    @property
    def mouse_y(self):
        return self._globals_dict["mouse_y"]

    @mouse_y.setter
    def mouse_y(self, val):
        self._globals_dict["mouse_y"] = val

    @property
    def mouse_is_pressed(self):
        return self._globals_dict["mouse_is_pressed"]

    @mouse_is_pressed.setter
    def mouse_is_pressed(self, val):
        self._globals_dict["mouse_is_pressed"] = val

    @property
    def width(self):
        return self._globals_dict["width"]

    @width.setter
    def width(self, val):
        self._globals_dict["width"] = val
        self.canvas.width = val

    @property
    def height(self):
        return self._globals_dict["height"]

    @height.setter
    def height(self, val):
        self._globals_dict["height"] = val
        self.canvas.height = val

    ### Library init ###

    # Updates last activity time
    @staticmethod
    def refresh_last_activity():
        global _sparkplug_last_activity
        _sparkplug_last_activity = time.time()

    # Creates canvas and starts thread
    def start(self, methods):
        self._methods = methods
        draw = self._methods.get("draw", None)
        
        if draw:
            self.print_status("Running...")
            display(self.stop_button)
        else:
            self.print_status("Done drawing")

        display(self.canvas)
        
        self.output_text_code = display(Code(self.output_text), display_id=True)

        self.canvas.on_mouse_down(self.on_mouse_down)
        self.canvas.on_mouse_up(self.on_mouse_up)
        self.canvas.on_mouse_move(self.on_mouse_move)

        thread = threading.Thread(target=self.loop)
        thread.start()

    def stop(self, message="Stopped"):
        global _sparkplug_running

        if not _sparkplug_running:
            return

        _sparkplug_running = False
        self.print_status(message)
        # Assuming we're using IPython to draw the canvas through the display() function.
        # Commenting this out for now, it throws exception since it does not derive BaseException
        # raise IpyExit

    # Loop method that handles drawing and setup
    def loop(self):
        global _sparkplug_active_thread_id, _sparkplug_running

        # Set active thread to this thread. This will stop any other active thread.
        current_thread_id = threading.current_thread().native_id
        _sparkplug_active_thread_id = current_thread_id
        _sparkplug_running = True
        self.refresh_last_activity()

        draw = self._methods.get("draw", None)
        setup = self._methods.get("setup", None)

        if setup:
            try:
                setup()
            except Exception as e:
                self.print_status("Error in setup() function: " + str(e))
                return

        while _sparkplug_running:
            if _sparkplug_active_thread_id != current_thread_id or time.time() - _sparkplug_last_activity > NO_ACTIVITY_THRESHOLD:
                self.stop("Stopped due to inactivity")
                return

            if not draw:
                return

            with hold_canvas(self.canvas):
                try:
                    draw()
                except Exception as e:
                    self.print_status("Error in draw() function: " + str(e))
                    return

            time.sleep(1 / FRAME_RATE)

    # Prints status to embedded error box
    def print_status(self, msg):
        self.status_text.update(Code(msg))
    
    # Prints output to embedded output box
    def print(self, msg):
        global _sparkplug_running
        self.output_text += str(msg) + "\n"

        if _sparkplug_running:
            self.output_text_code.update(Code(self.output_text))

    # Update mouse_x, mouse_y, and call mouse_down handler
    def on_mouse_down(self, x, y):
        self.refresh_last_activity()
        self.mouse_x, self.mouse_y = int(x), int(y)
        self.mouse_is_pressed = True

        mouse_down = self._methods.get("mouse_down", None)
        if mouse_down:
            mouse_down()

    # Update mouse_x, mouse_y, and call mouse_up handler
    def on_mouse_up(self, x, y):
        self.refresh_last_activity()
        self.mouse_x, self.mouse_y = int(x), int(y)
        self.mouse_is_pressed = False

        mouse_up = self._methods.get("mouse_up", None)
        if mouse_up:
            mouse_up()

    # Update mouse_x, mouse_y, and call mouse_moved handler
    def on_mouse_move(self, x, y):
        self.refresh_last_activity()
        self.mouse_x, self.mouse_y = int(x), int(y)

        mouse_moved = self._methods.get("mouse_moved", None)
        if mouse_moved:
            mouse_moved()
    
    def on_stop_button_clicked(self, button):
        self.stop()

    ### Global functions ###

    # Sets canvas size
    def size(self, *args):
        if len(args) == 2:
            self.width = args[0]
            self.height = args[1]

    # Sets fill style
    # 1 arg: HTML string value
    # 3 args: r, g, b are int between 0 and 255
    # 4 args: r, g, b, a, where r, g, b are ints between 0 and 255, and  a (alpha) is a float between 0 and 1.0
    def fill_style(self, *args):
        self.canvas.fill_style = self.parse_color("fill_style", *args)

    def stroke_style(self, *args):
        self.canvas.stroke_style = self.parse_color("stroke_style", *args)

    # Combines fill_rect and stroke_rect into one wrapper function
    def rect(self, *args):
        self.check_coords("rect", *args)
        
        self.canvas.fill_rect(*args)
        self.canvas.stroke_rect(*args)

    # Similar to self.rect wrapper, except only accepts x, y and size
    def square(self, *args):
        self.check_coords("square", *args, width_only=True)
        rect_args = (*args, args[2]) # Copy the width arg into the height
        self.rect(*rect_args)

    # Draws filled rect
    def fill_rect(self, *args):
        self.check_coords("fill_rect", *args)
        self.canvas.fill_rect(*args)
    
    # Strokes a rect
    def stroke_rect(self, *args):
        self.check_coords("stroke_rect", *args)
        self.canvas.stroke_rect(*args)

    #Clears a rect
    def clear_rect(self, *args):
        self.check_coords('clear_rect', *args)
        self.canvas.clear_rect(*args)

    # Draws circle at given coordinates
    def circle(self, *args):
        self.check_coords("circle", *args, width_only=True)
        arc_args = self.arc_args(*args)
        self.canvas.fill_arc(*arc_args)
        self.canvas.stroke_arc(*arc_args)

    # Draws filled circle
    def fill_circle(self, *args):
        self.check_coords("fill_circle", *args, width_only=True)
        arc_args = self.arc_args(*args)
        self.canvas.fill_arc(*arc_args)

    # Draws circle stroke
    def stroke_circle(self, *args):
        self.check_coords("stroke_circle", *args, width_only=True)
        arc_args = self.arc_args(*args)
        self.canvas.stroke_arc(*arc_args)
        
    def fill_arc(self, *args):
        self.canvas.fill_arc(*args)

    def stroke_arc(self, *args):
        self.canvas.stroke_arc(*args)
    
    def fill_text(self, *args):
        self.canvas.font = "{px}px sans-serif".format(px = args[4])
        self.canvas.fill_text(args[0:3])
        self.canvas.font = "12px sans-serif"

    def stroke_text(self, *args):
        self.canvas.font = "{px}px sans-serif".format(px = args[4])
        self.canvas.stroke_text(args[0:3])
        self.canvas.font = "12px sans-serif"

    def text_align(self, *args):
        self.canvas.text_align(*args)

    def draw_line(self, *args):
        if len(args) == 4:
            self.canvas.line_width = args[4]
        else:
            self.canvas.line_width = 1
            
        self.canvas.begin_path()
        self.canvas.move_to(args[0],args[1])
        self.canvas.line_to(args[2],args[4])
        self.canvas.close_path()

    # Clears canvas
    def clear(self, *args):
        self.canvas.clear()

    
    # Draws background on canvas
    def background(self, *args):
        old_fill = self.canvas.fill_style
        argc = len(args)

        if argc == 3:
            if ((not type(args[0]) is int) or (not type(args[1]) is int) or (not type(args[2]) is int)):
                raise TypeError("Enter Values between 0 and 255(integers only) for all 3 values")
            elif (not (args[0] >= 0 and args[0] <= 255) or not (args[1] >= 0 and args[1] <= 255) or not (
                args[2] >= 0 and args[2] <= 255)):
                raise TypeError("Enter Values between 0 and 255(integers only) for all 3 values")
            self.clear()
            self.fill_style(args[0], args[1], args[2])
            self.fill_rect(0, 0, self.width, self.height)
        elif argc == 1:
            if (not type(args[0]) is str):
                raise TypeError("Enter colour value in Hex i.e #000000 for black and so on")
            self.clear()
            self.fill_style(args[0])
            self.fill_rect(0, 0, self.width, self.height)
        elif argc == 4:
            if ((not type(args[0]) is int) or (not type(args[1]) is int) or (not type(args[2]) is int) or (
            not type(args[3]) is float)):
                raise TypeError("Enter Values between 0 and 255(integers only) for all 3 values")
            elif (not (args[0] >= 0 and args[0] <= 255) or not (args[1] >= 0 and args[1] <= 255) or not (
                args[2] >= 0 and args[2] <= 255) or not (args[3] >= 0.0 and args[3] <= 1.0)):
                raise TypeError(
                "Enter Values between 0 and 255(integers only) for all 3 values and a value between 0.0 and 1.0 for opacity(last argument")
            self.clear()
            self.fill_style(args[0], args[1], args[2], args[3])
            self.fill_rect(0, 0, self.width, self.height)
        
        self.canvas.fill_style = old_fill
    
    ### Helper Functions ###

    # Tests if input is numeric
    # Note: No support for complex numbers
    def check_type_is_num(self, n, func_name=None):
        if not isinstance(n, (int, float)):
            msg = "Expected {} to be a number".format(n)
            if func_name:
                msg = "{} expected {} to be a number".format(func_name, self.quote_if_string(n))
            raise TypeError(msg)

    # Tests if input is an int
    def check_type_is_int(self, n, func_name=None):
        if type(n) is not int:
            msg = "Expected {} to be an int".format(n)
            if func_name:
                msg = "{} expected {} to be an int".format(func_name, self.quote_if_string(n))
            raise TypeError(msg)

    # Tests if input is a float
    # allow_int: Set to True to allow ints as a float. Defaults to True.
    def check_type_is_float(self, n, func_name=None, allow_int=True):
        if type(n) is not float:
            if not allow_int or type(n) is not int:
                msg = "Expected {} to be a float".format(n)
                if func_name:
                    msg = "{} expected {} to be a float".format(func_name, self.quote_if_string(n))
                raise TypeError(msg)

    @staticmethod
    def quote_if_string(val):
        if type(val) is str:
            return "\"{}\"".format(val)
        else:
            return val
    
    # Parse a string, rgb or rgba input into an HTML color string
    def parse_color(self, func_name, *args):
        argc = len(args)

        if argc == 1:
            return args[0]
        elif argc == 3 or argc == 4:
            color_args = args[:3]
            for col in color_args:
                self.check_type_is_int(col, func_name)
            color_args = np.clip(color_args, 0, 255)

            if argc == 3:
                return "rgb({}, {}, {})".format(*color_args)
            else:
                # Clip alpha between 0 and 1
                alpha_arg = args[3]
                self.check_type_is_float(alpha_arg, func_name)
                alpha_arg = np.clip(alpha_arg, 0, 1.0)
                return "rgba({}, {}, {}, {})".format(*color_args, alpha_arg)
        else:
            raise TypeError("{} expected {}, {} or {} arguments, got {}".format(func_name, 1, 3, 4, argc))

    # Check a set of 4 args are valid coordinates
    # x, y, w, h
    def check_coords(self, func_name, *args, width_only=False):
        argc = len(args)
        if argc != 4 and not width_only:
            raise TypeError("{} expected {} arguments for x, y, w, h, got {} arguments".format(func_name, 4, argc))
        elif argc != 3 and width_only:
            raise TypeError("{} expected {} arguments for x, y, size, got {} arguments".format(func_name, 3, argc))

        for arg in args:
            self.check_type_is_float(arg, func_name)

    # Convert a tuple of circle args into arc args 
    def arc_args(self, *args):
        return (args[0], args[1], args[2] / 2, 0, 2 * pi)