Example #1
0
def draw_player(cr: cairo.Context, player: Player) -> None:
    with save_context(cr):
        cr.translate(player.x, player.y)

        with save_context(cr):
            cr.rotate(player.rotation)

            # draw player
            cr.set_source_rgb(0, 0, 0)
            cr.arc(0, 0, 10, 0, math.tau)
            cr.fill()

            # draw arms
            cr.move_to(2, -10)
            cr.rel_line_to(12, 0)
            cr.move_to(2, 10)
            cr.rel_line_to(12, 0)
            cr.stroke()

        # draw health
        cr.set_source_rgb(1, 1, 1)
        cr.rectangle(-20, -35, 40, 8)
        cr.fill()
        # cr.set_source_rgb(.1, .9, .2)
        cr.set_source_rgb(2 * (1 - player.health), 2 * player.health, 0)
        cr.rectangle(-20, -35, 40 * player.health, 8)
        cr.fill()
        cr.set_line_width(1)
        cr.set_source_rgb(0, 0, 0)
        cr.rectangle(-20, -35, 40, 8)
        cr.stroke()
Example #2
0
def rotation(ctx: cairo.Context, radians: float):
    ctx.rotate(radians)

    try:
        yield
    finally:
        ctx.rotate(-radians)
    def draw_trains(self, layout: Layout, cr: Context):
        for train in layout.trains.values():
            car_start = train.position

            annotation = train.meta.get("annotation")

            for i, car in enumerate(train.cars):
                front_bogey_offset, rear_bogey_offset = car.bogey_offsets
                bogey_spacing = rear_bogey_offset - front_bogey_offset
                front_bogey_position = car_start - front_bogey_offset
                front_bogey_xy = self.transform_track_point(front_bogey_position)
                rear_bogey_position, rear_bogey_xy = self.point_back(
                    front_bogey_position, bogey_spacing
                )

                cr.save()
                cr.translate(front_bogey_xy[0], front_bogey_xy[1])
                cr.rotate(
                    math.pi
                    + math.atan2(
                        front_bogey_xy[1] - rear_bogey_xy[1],
                        front_bogey_xy[0] - rear_bogey_xy[0],
                    )
                )

                cr.set_source_rgb(*hex_to_rgb(train.meta.get("color", "#a0a0ff")))

                if i == 0 and annotation:
                    cr.move_to(0, -10)
                    cr.set_font_size(5)
                    cr.show_text(annotation)

                cr.set_line_width(4)
                cr.move_to(-front_bogey_offset, 0)
                cr.line_to(car.length - front_bogey_offset, 0)
                cr.stroke()

                cr.set_line_width(6)
                cr.move_to(1 - front_bogey_offset, 0)
                cr.line_to(car.length - front_bogey_offset - 1, 0)
                cr.stroke()

                if i == 0 and train.lights_on:
                    cr.set_source_rgba(1, 1, 0.2, 0.5)
                    for y in (-2.5, 2.5):
                        cr.move_to(-front_bogey_offset - 1, y)
                        cr.arc(
                            -front_bogey_offset - 1,
                            y,
                            10,
                            6 / 7 * math.pi,
                            math.pi * 8 / 7,
                        )
                        cr.close_path()
                        cr.fill()

                cr.restore()
                car_start = rear_bogey_position - (car.length - rear_bogey_offset + 1)
Example #4
0
 def illustrate(self, surface):
     if self.art != None:
         illustration = Context(surface)
         illustration.scale(0.6, 0.6)
         illustration.translate(self.w / 6, self.h / 6)
         self.art.render_cairo(illustration)
         illustration.translate(self.w * 4 / 3, self.h * 4 / 3)
         illustration.rotate(pi)
         self.art.render_cairo(illustration)
    def draw_piece(self, piece: Piece, cr: Context, drawing_options: DrawingOptions):
        if not piece.position:
            return

        cr.save()

        cr.translate(piece.position.x, piece.position.y)
        cr.rotate(piece.position.angle)

        piece.draw(cr, drawing_options)

        relative_positions = piece.relative_positions()

        for anchor_name, anchor in piece.anchors.items():
            # if anchor.position != piece.position + relative_positions[anchor_name] + Position(0, 0, math.pi):
            #     cr.move_to(0, 0)
            #     cr.line_to(anchor.position.x, anchor.position.y)
            #     cr.stroke()

            cr.save()

            cr.translate(
                relative_positions[anchor_name].x, relative_positions[anchor_name].y
            )
            cr.rotate(relative_positions[anchor_name].angle)

            if len(anchor) == 2:
                cr.set_source_rgb(1, 0.5, 0.5)
            else:
                cr.set_source_rgb(0.5, 1, 0.5)

            next_piece, next_anchor_name = anchor.next(piece)

            cr.arc(
                0,
                0,
                3
                if (piece.placement and anchor_name == piece.anchor_names[0])
                or (
                    next_piece
                    and next_piece.placement
                    and next_anchor_name == next_piece.anchor_names[0]
                )
                else 1,
                0,
                math.tau,
            )
            cr.fill()

            cr.restore()
        cr.restore()
Example #6
0
 def illustrate(self, surface):
     if self.suit != None:
         suits = Context(surface)
         suits.scale(0.5, 0.5)
         suits.translate(self.w / 8, self.h / 12)
         self.suit.render_cairo(suits)
         suits.translate(self.w * 7 / 4, self.h * 11 / 6)
         suits.rotate(pi)
         self.suit.render_cairo(suits)
     if self.art != None:
         illustration = Context(surface)
         sf = self.w / self.art.get_dimension_data()[2]
         illustration.scale(sf, sf)
         self.art.render_cairo(illustration)
    def draw_sensor(
        self,
        sensor: Sensor,
        layout: Layout,
        cr: Context,
        drawing_options: DrawingOptions = None,
    ):
        if not sensor.position:
            return
        cr.save()
        cr.translate(sensor.position.x, sensor.position.y)
        cr.rotate(sensor.position.angle)

        sensor.draw(cr, drawing_options or self.drawing_options)

        cr.restore()
Example #8
0
def arrowhead(context: cairo.Context,
              size: float,
              position: Point,
              direction: Point = None,
              front_angle: float = math.pi / 4,
              back_angle: float = math.pi / 2):
    '''Add an arrow head to the current path, starting at the current point.'''
    if direction is None:
        direction = Point(0, 1)
    halfwidth = math.tan(front_angle / 2) * size
    back_height = halfwidth / math.tan(back_angle / 2)

    context.move_to(*position)
    context.rotate(-math.atan2(direction.x, direction.y))
    context.rel_line_to(halfwidth, -back_height)
    context.rel_line_to(-halfwidth, size)
    context.rel_line_to(-halfwidth, -size)
    context.fill()
Example #9
0
    def screen(self, base_w, base_h, ctx: cairo.Context, display_id: int):
        if display_id == 0:
            self.decode_screen()

        ctx.translate(base_w * self._scale / 2, base_h * self._scale / 2)
        ctx.rotate(-radians(self._screen_rotation_degrees))
        if self._screen_rotation_degrees == 90 or self._screen_rotation_degrees == 270:
            ctx.translate(-base_h * self._scale / 2, -base_w * self._scale / 2)
        else:
            ctx.translate(-base_w * self._scale / 2, -base_h * self._scale / 2)
        ctx.scale(self._scale, self._scale)
        if display_id == 0:
            ctx.set_source_surface(self._upper_image)
        else:
            ctx.set_source_surface(self._lower_image)
        ctx.get_source().set_filter(cairo.Filter.NEAREST)
        ctx.paint()

        if self._after_render_hook:
            self._after_render_hook(ctx, display_id)
def draw_speed_limit(ctx: Context, surface_marking: SurfaceMarking, limit: int,
                     start_zone: bool):
    """Draw a speed limit on the road. Add a cross if it is the end of a speed limit zone.

    Args:
        start_zone: Is this speed limit the start or end?
    """
    ctx.translate(surface_marking.center.x, surface_marking.center.y
                  )  # Translate to the center point of the surface marking
    ctx.rotate(surface_marking.orientation)

    ctx.translate(0, surface_marking.width / 2)  # Translate to the middle

    # Position text and scale it
    ctx.rotate(-math.pi / 2)  # rotate by 90deg
    ctx.scale(1, -1)  # mirror y-axis to display text correctly
    ctx.scale(0.7245, 1.8506)  # scale text to match rule book

    # Set font options
    ctx.select_font_face("OpenDinSchriftenEngshrift", cairo.FONT_SLANT_NORMAL)
    ctx.set_font_size(0.4)
    ctx.text_path(str(limit))
    ctx.set_line_width(0.01)

    # Draw text
    _, _, text_width, text_height, _, _ = ctx.text_extents(str(limit))
    ctx.fill_preserve()
    ctx.stroke()

    if not start_zone:
        # Draw cross
        padding = 0.025
        ctx.set_line_width(0.03)
        ctx.move_to(-padding, padding)
        ctx.line_to(text_width + padding, -text_height - padding)
        ctx.stroke()
        ctx.move_to(-padding, -text_height - padding)
        ctx.line_to(text_width + padding, padding)
        ctx.stroke()
Example #11
0
    def _to_png(self,
                font,
                font_position=None,
                dst=None,
                limit=800,
                size=1500,
                tab_width=1500,
                padding_characters=""):
        """Use HB, FreeType and Cairo to produce a png for a table.

        Parameters
        ----------
        font: DFont
        font_position: str
            Label indicating which font has been used. 
        dst: str
            Path to output image. If no path is given, return in-memory 
        """
        # TODO (M Foley) better packaging for pycairo, freetype-py
        # and uharfbuzz.
        # Users should be able to pip install these bindings without needing
        # to install the correct libs.

        # A special mention to the individuals who maintain these packages. Using
        # these dependencies has sped up the process of creating diff images
        # significantly. It's an incredible age we live in.
        y_tab = int(1500 / 25)
        x_tab = int(tab_width / 64)
        width, height = 1024, 200

        cells_per_row = int((width - x_tab) / x_tab)
        # Compute height of image
        x, y, baseline = x_tab, 0, 0
        for idx, row in enumerate(self._data[:limit]):
            x += x_tab

            if idx % cells_per_row == 0:
                y += y_tab
                x = x_tab
        height += y
        height += 100

        # draw image
        Z = ImageSurface(FORMAT_ARGB32, width, height)
        ctx = Context(Z)
        ctx.rectangle(0, 0, width, height)
        ctx.set_source_rgb(1, 1, 1)
        ctx.fill()

        # label image
        ctx.set_font_size(30)
        ctx.set_source_rgb(0.5, 0.5, 0.5)
        ctx.move_to(x_tab, 50)
        ctx.show_text("{}: {}".format(self.table_name, len(self._data)))
        ctx.move_to(x_tab, 100)
        if font_position:
            ctx.show_text("Font Set: {}".format(font_position))
        if len(self._data) > limit:
            ctx.set_font_size(20)
            ctx.move_to(x_tab, 150)
            ctx.show_text(
                "Warning: {} different items. Only showing most serious {}".
                format(len(self._data), limit))

        hb.ot_font_set_funcs(font.hbfont)

        # Draw glyphs
        x, y, baseline = x_tab, 200, 0
        x_pos = x_tab
        y_pos = 200
        for idx, row in enumerate(self._data[:limit]):
            string = "{}{}{}".format(padding_characters, row['string'],
                                     padding_characters)
            buf = self._shape_string(font, string, row['features'])
            char_info = buf.glyph_infos
            char_pos = buf.glyph_positions
            for info, pos in zip(char_info, char_pos):
                gid = info.codepoint
                font.ftfont.load_glyph(gid, flags=6)
                bitmap = font.ftslot.bitmap

                if bitmap.width > 0:
                    ctx.set_source_rgb(0, 0, 0)
                    glyph_surface = _make_image_surface(
                        font.ftfont.glyph.bitmap, copy=False)
                    ctx.set_source_surface(
                        glyph_surface,
                        x_pos + font.ftslot.bitmap_left + (pos.x_offset / 64.),
                        y_pos - font.ftslot.bitmap_top - (pos.y_offset / 64.))
                    glyph_surface.flush()
                    ctx.paint()
                x_pos += (pos.x_advance) / 64.
                y_pos += (pos.y_advance) / 64.

            x_pos += x_tab - (x_pos % x_tab)
            if idx % cells_per_row == 0:
                # add label
                if font_position:
                    ctx.set_source_rgb(0.5, 0.5, 0.5)
                    ctx.set_font_size(10)
                    ctx.move_to(width - 20, y_pos)
                    ctx.rotate(1.5708)
                    ctx.show_text(font_position)
                    ctx.set_source_rgb(0, 0, 0)
                    ctx.rotate(-1.5708)
                # Start a new row
                y_pos += y_tab
                x_pos = x_tab
        Z.flush()
        if dst:
            Z.write_to_png(dst)
        else:
            img = StringIO()
            Z.write_to_png(img)
            return Image.open(img)
Example #12
0
class Canvas:
    def __init__(self,
                 width: int,
                 height: Optional[int] = None,
                 *,
                 normalise: bool = True):
        if height is None:
            height = width
        self._width = width
        self._height = height
        self._surface = ImageSurface(FORMAT_ARGB32, width, height)
        self._context = Context(self._surface)
        self._normalise = normalise
        if self._normalise:
            self.scale(self._width, self._height)

    @property
    def width(self) -> int:
        return self._width if not self._normalise else 1

    @property
    def height(self) -> int:
        return self._height if not self._normalise else 1

    # TODO depreciate
    @property
    def context(self) -> Context:  # pragma: no cover
        return self._context

    def save(self) -> None:
        self._context.save()

    def restore(self) -> None:
        self._context.restore()

    # Transformation
    def rotate(self, angle: float) -> None:
        self._context.rotate(angle)

    def translate(self, x: float, y: float) -> None:
        self._context.translate(x, y)

    def scale(self, width: int, height: int) -> None:
        self._context.scale(width, height)

    def set_line_width(self, width: float) -> None:
        self._context.set_line_width(width)

    # Colour functionality
    def set_colour(self, colour: Colour) -> None:
        self._context.set_source_rgba(colour.red, colour.blue, colour.green,
                                      colour.alpha)

    def set_grey(self, colour_value: float, alpha: float = 1) -> None:
        colour = Colour(*(colour_value, ) * 3, alpha)
        self.set_colour(colour)

    def set_black(self) -> None:
        self.set_colour(BLACK)

    def set_white(self) -> None:
        self.set_colour(WHITE)

    def set_background(self, colour: Colour) -> None:
        self._context.rectangle(0, 0, self.width, self.height)
        self.set_colour(colour)
        self._context.fill()

    def set_line_cap(self, line_cap: LineCap) -> None:
        self._context.set_line_cap(line_cap.value)

    def set_line_join(self, line_join: LineJoin) -> None:
        self._context.set_line_join(line_join.value)

    def set_fill_rule(self, file_rule: FillRule) -> None:
        self._context.set_fill_rule(file_rule.value)

    # Render methods
    def fill(self, preserve: bool = False) -> None:
        if not preserve:
            self._context.fill()
        else:
            self._context.fill_preserve()

    def stroke(self, preserve: bool = False) -> None:
        if not preserve:
            self._context.stroke()
        else:
            self._context.stroke_preserve()

    def clip(self) -> None:
        self._context.clip()

    def _draw_path(self, path: Iterable[Point], close_path: bool) -> None:
        self._context.new_sub_path()
        for p in path:
            self._context.line_to(*p)
        if close_path:
            self._context.close_path()

    def draw_path(self,
                  path: Iterable[PointType],
                  close_path: bool = False) -> None:
        points = (p if isinstance(p, Point) else Point(*p) for p in path)
        self._draw_path(points, close_path)

    def draw_polygon(self, polygon: Polygon) -> None:
        self._draw_path(polygon.points, close_path=True)

    def draw_circle(self,
                    circle: Circle,
                    fill: bool = False,
                    stroke: bool = True) -> None:
        self._context.new_sub_path()
        self._context.arc(*circle.centre, circle.radius, 0, 2 * pi)

    def write_to_png(self, file_name: str) -> None:
        self._surface.write_to_png(file_name)
Example #13
0
    def draw(self, ctx: cairo.Context, **kwargs):
        layer = kwargs.get('layer', 0)
        opacity = kwargs.get('opacity', 1.0)
        debug = kwargs.get('debug', False)

        ctx.save()
        pos, angle, width, length = self.pos, self.angle, self.width, self.length

        # move origin to position of branch
        ctx.translate(pos.x, pos.y)

        # draw rectangle
        ctx.save()
        ctx.rotate(angle)
        ctx.rectangle(0, -width / 2, length, width)

        r, g, b = _get_branch_color(self)
        ctx.set_source_rgba(r, g, b, opacity)
        ctx.fill()
        ctx.restore()

        if debug:
            # draw label
            label = '{:d}'.format(self.index)
            ctx.save()
            ctx.set_source_rgba(1, 0, 0, opacity)
            ctx.set_font_size(width * 0.1)
            extents = ctx.text_extents(label)

            ctx.rotate(angle)
            ctx.translate(length / 2 - extents.width / 2, 0)
            ctx.rotate(-angle)

            ctx.show_text(label)
            ctx.restore()

        msg = '{}'.format(' '.join(self.text.upper()))
        filler = '—  '
        if msg:
            # draw message
            ctx.save()
            ft_size = width * 0.6
            ctx.set_font_size(ft_size)
            ctx.set_line_width(0.0002)
            ctx.select_font_face('Impact', cairo.FontSlant.NORMAL,
                                 cairo.FontWeight.BOLD)

            max_width = 0.6 * length
            msg_extents = ctx.text_extents(msg)
            if msg_extents.width > max_width:
                num_fillers = 0
                while msg_extents.width > max_width:
                    if ft_size < 0.0001:
                        break
                    ft_size -= 0.00001
                    ctx.set_font_size(ft_size)
                    msg_extents = ctx.text_extents(msg)
            else:
                filler_extents = ctx.text_extents(filler)
                num_fillers = max(
                    math.floor((length - msg_extents.width) /
                               filler_extents.x_advance / 2) - 2, 0)

            full_msg = '{}    {}    {}'.format(filler * num_fillers, msg,
                                               filler * num_fillers).strip()
            extents = ctx.text_extents(full_msg)

            if math.pi / 2 <= angle <= 3 * math.pi / 2:
                ctx.rotate(angle + math.pi)
                ctx.translate(-length, 0)
                ctx.scale(1, 1)
                ctx.translate(length / 2 - extents.width / 2,
                              extents.height / 2)
            else:
                ctx.rotate(angle)
                ctx.translate(length / 2 - extents.width / 2,
                              extents.height / 2)

            ctx.set_source_rgba(1, 1, 1, opacity)
            ctx.text_path(full_msg)
            ctx.fill()
            ctx.stroke()

            # give border
            ctx.set_source_rgba(0, 0, 0, opacity)
            ctx.text_path(full_msg)
            ctx.stroke()

            ctx.restore()

        # draw point
        # ctx.rotate(-angle)
        # ctx.arc(0, 0, 0.004, 0, 2 * math.pi)
        # ctx.set_source_rgb(0, 1, 0)
        # ctx.fill()

        # restore out of branch coordinates
        ctx.restore()
Example #14
0
def main(marker, paper, format, bbox, emergency, place, recipient, sender, text, sender_is_recipient, map_href):
    """
    """
    mark = Location(*marker)
    
    handle, filename = mkstemp(prefix='safetymap-', suffix='.pdf')
    close(handle)

    if paper == 'a4':
        surf = PDFSurface(filename, 210*ptpmm, 297*ptpmm)
    
    elif paper == 'letter':
        surf = PDFSurface(filename, 8.5*ptpin, 11*ptpin)
    
    ctx = Context(surf)
    
    ctx.scale(ptpmm, ptpmm)

    set_font_face_from_file(ctx, 'assets/HelveticaNeue.ttc')
    
    if paper == 'a4':
        draw_a4_master(ctx, format, map_href)
        ctx.translate(19, 24)
    
    elif paper == 'letter':
        draw_letter_master(ctx, format, map_href)
        ctx.translate(21, 18)

    ctx.set_line_width(.25 * mmppt)
    ctx.set_source_rgb(*md_gray)
    ctx.set_dash([3 * mmppt])

    reps = {'4up': 4, '2up-fridge': 2, 'poster': 0}
    
    if reps[format]:
        card_img, mark_point = get_map_image(bbox, 84, 39, mark)
        
    for i in range(reps[format]):
    
        # dashed outlines
        ctx.move_to(0, 61)
        ctx.line_to(0, 0)
        ctx.line_to(173, 0)
        ctx.line_to(173, 61)
        #ctx.move_to(86, 0)
        #ctx.line_to(86, 61)
        ctx.stroke()
    
        # two card sides and contents
        draw_card_left(ctx, recipient, sender, text, sender_is_recipient)
        ctx.translate(86.5, 0)

        draw_card_right(ctx, card_img, mark_point, emergency, place)
        ctx.translate(-86.5, 61)

    if format == '4up':
        # bottom dashed outline
        ctx.move_to(0, 0)
        ctx.line_to(172, 0)
        ctx.stroke()

    elif format == '2up-fridge':
        # prepare to draw sideways
        ctx.translate(0, 122.5)
        ctx.rotate(-pi/2)

        ctx.rectangle(0, 0, 122.5, 173)
        ctx.stroke()

        poster_img, mark_point = get_map_image(bbox, 109, 77, mark)
        draw_small_poster(ctx, poster_img, mark_point, emergency, place, recipient, sender, text, sender_is_recipient)

    elif format == 'poster':
        ctx.rectangle(0, 0, 173, 245)
        ctx.stroke()

        poster_img, mark_point = get_map_image(bbox, 153, 108, mark)
        draw_large_poster(ctx, poster_img, mark_point, emergency, place, recipient, sender, text, sender_is_recipient)

    surf.finish()
    chmod(filename, 0644)
    return filename