示例#1
0
    def plot(self, svg: Drawing) -> None:

        recolor: Optional[str] = None

        if isinstance(self.color, list):
            linear_gradient: LinearGradient = svg.linearGradient(
                self.map_((0, self.max_y)),
                self.map_((0, 1)),
                gradientUnits="userSpaceOnUse",
            )
            for index, color in enumerate(self.color):
                linear_gradient.add_stop_color(
                    index / (len(self.color) - 1), color.hex
                )

            gradient: LinearGradient = svg.defs.add(linear_gradient)
            recolor = gradient.get_funciri()

        if isinstance(self.color, Color):
            recolor = self.color.hex

        svg.add(
            svg.rect(
                insert=(0, 0),
                size=("100%", "100%"),
                rx=None,
                ry=None,
                fill=self.background_color.hex,
            )
        )
        self.draw_grid(svg)
        last_text_y = 0

        for xs, ys, color, title in self.data:

            if recolor:
                color = recolor

            assert len(xs) == len(ys)

            xs_second: list[float] = [
                (x - self.min_x).total_seconds() for x in xs
            ]
            points = []

            for index, x in enumerate(xs_second):
                y = ys[index]
                mapped: np.ndarray = map_array(
                    np.array((x, y)),
                    np.array((self.min_x_second, self.max_y)),
                    np.array((self.max_x_second, self.min_y)),
                    self.canvas.workspace[0],
                    self.canvas.workspace[1],
                )
                points.append(mapped)
            previous_point: Optional[np.ndarray] = None

            for point in points:
                if previous_point is not None:
                    line: Line = svg.line(
                        (previous_point[0], previous_point[1]),
                        (point[0], point[1]),
                        stroke=self.background_color.hex,
                        stroke_width=6,
                    )
                    svg.add(line)
                    line: Line = svg.line(
                        (previous_point[0], previous_point[1]),
                        (point[0], point[1]),
                        stroke=color,
                        stroke_width=2,
                    )
                    svg.add(line)
                previous_point = point

            for point in points:
                svg.add(
                    svg.circle(
                        (point[0], point[1]),
                        5.5,
                        fill=self.background_color.hex,
                    )
                )
                svg.add(svg.circle((point[0], point[1]), 3.5, fill=color))

            title: str
            text_y = max(last_text_y + 20, point[1] + 6)
            self.text(svg, (point[0] + 15, text_y), title.upper(), color)
            last_text_y = text_y

        with Path(svg.filename).open("w+") as output_file:
            svg.write(output_file)
示例#2
0
def disvg(paths=None, colors=None,
          filename=os_path.join(getcwd(), 'disvg_output.svg'),
          stroke_widths=None, nodes=None, node_colors=None, node_radii=None,
          openinbrowser=True, timestamp=False,
          margin_size=0.1, mindim=600, dimensions=None,
          viewbox=None, text=None, text_path=None, font_size=None,
          attributes=None, svg_attributes=None, svgwrite_debug=False):
    """Takes in a list of paths and creates an SVG file containing said paths.
    REQUIRED INPUTS:
        :param paths - a list of paths

    OPTIONAL INPUT:
        :param colors - specifies the path stroke color.  By default all paths
        will be black (#000000).  This paramater can be input in a few ways
        1) a list of strings that will be input into the path elements stroke
            attribute (so anything that is understood by the svg viewer).
        2) a string of single character colors -- e.g. setting colors='rrr' is
            equivalent to setting colors=['red', 'red', 'red'] (see the
            'color_dict' dictionary above for a list of possibilities).
        3) a list of rgb 3-tuples -- e.g. colors = [(255, 0, 0), ...].

        :param filename - the desired location/filename of the SVG file
        created (by default the SVG will be stored in the current working
        directory and named 'disvg_output.svg').

        :param stroke_widths - a list of stroke_widths to use for paths
        (default is 0.5% of the SVG's width or length)

        :param nodes - a list of points to draw as filled-in circles

        :param node_colors - a list of colors to use for the nodes (by default
        nodes will be red)

        :param node_radii - a list of radii to use for the nodes (by default
        nodes will be radius will be 1 percent of the svg's width/length)

        :param text - string or list of strings to be displayed

        :param text_path - if text is a list, then this should be a list of
        path (or path segments of the same length.  Note: the path must be
        long enough to display the text or the text will be cropped by the svg
        viewer.

        :param font_size - a single float of list of floats.

        :param openinbrowser -  Set to True to automatically open the created
        SVG in the user's default web browser.

        :param timestamp - if True, then the a timestamp will be appended to
        the output SVG's filename.  This will fix issues with rapidly opening
        multiple SVGs in your browser.

        :param margin_size - The min margin (empty area framing the collection
        of paths) size used for creating the canvas and background of the SVG.

        :param mindim - The minimum dimension (height or width) of the output
        SVG (default is 600).

        :param dimensions - The display dimensions of the output SVG.  Using
        this will override the mindim parameter.

        :param viewbox - This specifies what rectangular patch of R^2 will be
        viewable through the outputSVG.  It should be input in the form
        (min_x, min_y, width, height).  This is different from the display
        dimension of the svg, which can be set through mindim or dimensions.

        :param attributes - a list of dictionaries of attributes for the input
        paths.  Note: This will override any other conflicting settings.

        :param svg_attributes - a dictionary of attributes for output svg.
        
        :param svgwrite_debug - This parameter turns on/off `svgwrite`'s 
        debugging mode.  By default svgwrite_debug=False.  This increases 
        speed and also prevents `svgwrite` from raising of an error when not 
        all `svg_attributes` key-value pairs are understood.

    NOTES:
        * The `svg_attributes` parameter will override any other conflicting 
        settings.

        * Any `extra` parameters that `svgwrite.Drawing()` accepts can be 
        controlled by passing them in through `svg_attributes`.

        * The unit of length here is assumed to be pixels in all variables.

        * If this function is used multiple times in quick succession to
        display multiple SVGs (all using the default filename), the
        svgviewer/browser will likely fail to load some of the SVGs in time.
        To fix this, use the timestamp attribute, or give the files unique
        names, or use a pause command (e.g. time.sleep(1)) between uses.
    """

    _default_relative_node_radius = 5e-3
    _default_relative_stroke_width = 1e-3
    _default_path_color = '#000000'  # black
    _default_node_color = '#ff0000'  # red
    _default_font_size = 12
    if isinstance(filename, str):
        # append directory to filename (if not included)
        if os_path.dirname(filename) == '':
            filename = os_path.join(getcwd(), filename)

        # append time stamp to filename
        if timestamp:
            fbname, fext = os_path.splitext(filename)
            dirname = os_path.dirname(filename)
            tstamp = str(time()).replace('.', '')
            stfilename = os_path.split(fbname)[1] + '_' + tstamp + fext
            filename = os_path.join(dirname, stfilename)

    # check paths and colors are set
    if isinstance(paths, Path) or is_path_segment(paths):
        paths = [paths]
    if paths:
        if not colors:
            colors = [_default_path_color] * len(paths)
        else:
            assert len(colors) == len(paths)
            if isinstance(colors, str):
                colors = str2colorlist(colors,
                                       default_color=_default_path_color)
            elif isinstance(colors, list):
                for idx, c in enumerate(colors):
                    if is3tuple(c):
                        colors[idx] = "rgb" + str(c)

    # check nodes and nodes_colors are set (node_radii are set later)
    if nodes:
        if not node_colors:
            node_colors = [_default_node_color] * len(nodes)
        else:
            assert len(node_colors) == len(nodes)
            if isinstance(node_colors, str):
                node_colors = str2colorlist(node_colors,
                                            default_color=_default_node_color)
            elif isinstance(node_colors, list):
                for idx, c in enumerate(node_colors):
                    if is3tuple(c):
                        node_colors[idx] = "rgb" + str(c)

    # set up the viewBox and display dimensions of the output SVG
    # along the way, set stroke_widths and node_radii if not provided
    assert paths or nodes
    stuff2bound = []
    if viewbox:
        szx, szy = viewbox[2:4]
    else:
        if paths:
            stuff2bound += paths
        if nodes:
            stuff2bound += nodes
        if text_path:
            stuff2bound += text_path
        xmin, xmax, ymin, ymax = big_bounding_box(stuff2bound)
        dx = xmax - xmin
        dy = ymax - ymin

        if dx == 0:
            dx = 1
        if dy == 0:
            dy = 1

        # determine stroke_widths to use (if not provided) and max_stroke_width
        if paths:
            if not stroke_widths:
                sw = max(dx, dy) * _default_relative_stroke_width
                stroke_widths = [sw]*len(paths)
                max_stroke_width = sw
            else:
                assert len(paths) == len(stroke_widths)
                max_stroke_width = max(stroke_widths)
        else:
            max_stroke_width = 0

        # determine node_radii to use (if not provided) and max_node_diameter
        if nodes:
            if not node_radii:
                r = max(dx, dy) * _default_relative_node_radius
                node_radii = [r]*len(nodes)
                max_node_diameter = 2*r
            else:
                assert len(nodes) == len(node_radii)
                max_node_diameter = 2*max(node_radii)
        else:
            max_node_diameter = 0

        extra_space_for_style = max(max_stroke_width, max_node_diameter)
        xmin -= margin_size*dx + extra_space_for_style/2
        ymin -= margin_size*dy + extra_space_for_style/2
        dx += 2*margin_size*dx + extra_space_for_style
        dy += 2*margin_size*dy + extra_space_for_style
        viewbox = "%s %s %s %s" % (xmin, ymin, dx, dy)
        if dimensions:
            szx, szy = dimensions
        else:
            if dx > dy:
                szx = str(mindim) + 'px'
                szy = str(int(ceil(mindim * dy / dx))) + 'px'
            else:
                szx = str(int(ceil(mindim * dx / dy))) + 'px'
                szy = str(mindim) + 'px'

    # Create an SVG file
    if svg_attributes is not None:
        szx = svg_attributes.get("width", szx)
        szy = svg_attributes.get("height", szy)
        debug = svg_attributes.get("debug", svgwrite_debug)
        dwg = Drawing(filename=filename, size=(szx, szy), debug=debug,
                      **svg_attributes)
    else:
        dwg = Drawing(filename=filename, size=(szx, szy), debug=svgwrite_debug,
                      viewBox=viewbox)

    # add paths
    if paths:
        for i, p in enumerate(paths):
            if isinstance(p, Path):
                ps = p.d()
            elif is_path_segment(p):
                ps = Path(p).d()
            else:  # assume this path, p, was input as a Path d-string
                ps = p

            if attributes:
                good_attribs = {'d': ps}
                for key in attributes[i]:
                    val = attributes[i][key]
                    if key != 'd':
                        try:
                            dwg.path(ps, **{key: val})
                            good_attribs.update({key: val})
                        except Exception as e:
                            warn(str(e))

                dwg.add(dwg.path(**good_attribs))
            else:
                dwg.add(dwg.path(ps, stroke=colors[i],
                                 stroke_width=str(stroke_widths[i]),
                                 fill='none'))

    # add nodes (filled in circles)
    if nodes:
        for i_pt, pt in enumerate([(z.real, z.imag) for z in nodes]):
            dwg.add(dwg.circle(pt, node_radii[i_pt], fill=node_colors[i_pt]))

    # add texts
    if text:
        assert isinstance(text, str) or (isinstance(text, list) and
                                         isinstance(text_path, list) and
                                         len(text_path) == len(text))
        if isinstance(text, str):
            text = [text]
            if not font_size:
                font_size = [_default_font_size]
            if not text_path:
                pos = complex(xmin + margin_size*dx, ymin + margin_size*dy)
                text_path = [Line(pos, pos + 1).d()]
        else:
            if font_size:
                if isinstance(font_size, list):
                    assert len(font_size) == len(text)
                else:
                    font_size = [font_size] * len(text)
            else:
                font_size = [_default_font_size] * len(text)
        for idx, s in enumerate(text):
            p = text_path[idx]
            if isinstance(p, Path):
                ps = p.d()
            elif is_path_segment(p):
                ps = Path(p).d()
            else:  # assume this path, p, was input as a Path d-string
                ps = p

            # paragraph = dwg.add(dwg.g(font_size=font_size[idx]))
            # paragraph.add(dwg.textPath(ps, s))
            pathid = 'tp' + str(idx)
            dwg.defs.add(dwg.path(d=ps, id=pathid))
            txter = dwg.add(dwg.text('', font_size=font_size[idx]))
            txter.add(txt.TextPath('#'+pathid, s))

    # save svg
    if isinstance(filename, str):
        if not os_path.exists(os_path.dirname(filename)):
            makedirs(os_path.dirname(filename))
        dwg.save()
    else:
        dwg.write(filename, pretty=False)
    if isinstance(filename, str):
        # re-open the svg, make the xml pretty, and save it again
        xmlstring = md_xml_parse(filename).toprettyxml()
        with open(filename, 'w') as f:
            f.write(xmlstring)

    # try to open in web browser
    if openinbrowser:
        try:
            open_in_browser(filename)
        except:
            print("Failed to open output SVG in browser.  SVG saved to:")
            print(filename)
示例#3
0
def create_svg(file, surface, controls_sequence = None, sn = None):
	min_x, max_x = None, None
	min_y, max_y = None, None
	
	for polygon in surface.polygons:
		for vertex in polygon.vertices:
			if min_x is None:
				min_x, max_x = vertex[0], vertex[0]
				min_y, max_y = vertex[1], vertex[1]
			else:
				min_x, max_x = min(vertex[0], min_x), max(vertex[0], max_x)
				min_y, max_y = min(vertex[1], min_y), max(vertex[1], max_y)
				
				
	a = (min_x, max_x, min_y, max_y)
	b = (max_x - min_x, max_y - min_y, 10.0)


	surface_view = Drawing(size = (max_x - min_x, max_y - min_y))
	
	
	def correct_vertex(vertex, surface_bounds, canvas_bounds):
		x_coordinate, y_coordinate                       = vertex
		left_bound, right_bound, bottom_bound, top_bound = surface_bounds
		canvas_width, canvas_height, canvas_padding      = canvas_bounds
		
		
		x_coordinate_scaling = \
			(canvas_width - 2.0 * canvas_padding) \
				/ (right_bound - left_bound)
				
		y_coordinate_scaling = \
			(canvas_height - 2.0 * canvas_padding) \
				/ (top_bound - bottom_bound)
				
				
		x_coordinate = \
			x_coordinate_scaling * (x_coordinate - left_bound) \
				+ canvas_padding
				
		y_coordinate = \
			y_coordinate_scaling * (y_coordinate - bottom_bound) \
				+ canvas_padding
				
		y_coordinate = canvas_height - y_coordinate
		
		
		return x_coordinate, y_coordinate
		
		
	for polygon in surface.polygons:
		vertices = \
			[correct_vertex((x_coordinate, y_coordinate), a, b) \
				for x_coordinate, y_coordinate, z_coordinate \
				in  polygon.vertices]
				
				
		if surface.maximal_impossibility - surface.minimal_impossibility > 0.0:
			relative_impossibility = \
				(polygon.impossibility - surface.minimal_impossibility) \
					/ (surface.maximal_impossibility - surface.minimal_impossibility)
		else:
			relative_impossibility = 0.0
			
		fill_color_red_component   = round(255.0 * relative_impossibility)
		fill_color_green_component = round(255.0 - 255.0 * relative_impossibility)
		fill_color_blue_component  = 0
		fill_color                 = \
			rgb(
				fill_color_red_component,
				fill_color_green_component,
				fill_color_blue_component
			)
			
		polygon_view = \
			Polygon(
				vertices,
				stroke_width = 1,
				stroke       = rgb(0, 0, 0),
				fill         = fill_color
			)
			
		surface_view.add(polygon_view)
		
		
	if sn is not None:
		for polygon_index, polygon in enumerate(surface.polygons):
			polygon_index_view = \
				Text(
					str(polygon_index),
					insert = \
						correct_vertex(
							(polygon.center[0], polygon.center[1]),
							a, b
						)
				)
				
			surface_view.add(polygon_index_view)
			
			
	# if sn is not None:
	# 	from svgwrite.text import Text
	# 	for state in sn:
	# 		polygon = state.polygon
	# 		polygon_number = sn[state]
			
	# 		polygon_center      = polygon.center[0], polygon.center[1]
	# 		q,w = correct_vertex(polygon_center, a, b)
	# 		polygon_center_view = \
	# 			Text(
	# 				str(polygon_number),
	# 				insert = (q+2,w+2),
	# 				style = "font-size: 50%; font-color: #808080"
	# 			)
				
	# 		surface_view.add(polygon_center_view)
			
			
	if controls_sequence is not None:
		last_state          = None
		last_polygon_center = None
		smoother            = Smoother(surface = surface, smoothing_depth = 1)
		
		
		for state in controls_sequence:
			polygon = state.polygons_sequence[-1]
			
			polygon_center      = polygon.center[0], polygon.center[1]
			polygon_center_view = \
				Circle(
					correct_vertex(polygon_center, a, b),
					2,
					stroke_width = 0,
					fill         = rgb(0, 0, 0),
				)
				
			surface_view.add(polygon_center_view)
			
			
			if last_state is not None:
				smoother.push_transfer(
					last_state.get_transfer(state)
				)
				
				first_transfer_point, second_transfer_point = \
					smoother.transfers_points_sequence[0]
					
					
				first_trek_view = \
					Line(
						correct_vertex(last_polygon_center, a, b),
						correct_vertex(
							(first_transfer_point.coordinates[0],
								first_transfer_point.coordinates[1]),
							a,
							b
						),
						stroke_width = 1,
						stroke       = rgb(0, 0, 0),
					)
					
				first_trek_end_view = \
					Circle(
						correct_vertex(
							(first_transfer_point.coordinates[0],
								first_transfer_point.coordinates[1]),
							a,
							b
						),
						2,
						stroke_width = 0,
						fill         = rgb(0, 0, 0),
					)
					
				second_trek_view = \
					Line(
						correct_vertex(
							(second_transfer_point.coordinates[0],
								second_transfer_point.coordinates[1]),
							a,
							b
						),
						correct_vertex(polygon_center, a, b),
						stroke_width = 1,
						stroke       = rgb(0, 0, 0),
					)
					
				second_trek_start_view = \
					Circle(
						correct_vertex(
							(second_transfer_point.coordinates[0],
								second_transfer_point.coordinates[1]),
							a,
							b
						),
						2,
						stroke_width = 0,
						fill         = rgb(0, 0, 0),
					)
					
					
				surface_view.add(first_trek_view)
				surface_view.add(first_trek_end_view)
				surface_view.add(second_trek_view)
				surface_view.add(second_trek_start_view)
				
				
			last_state          = state
			last_polygon_center = polygon_center
			
			
	surface_view.write(file)
	
示例#4
0
	def visualize(self, trajectory, output_file):
		drawing = Drawing()
		
		
		
		# Определение параметров образов состояний аппарата
		machine_view_length, machine_view_width = self.__machine_view_size
		
		coordinates_scaling = machine_view_length / self.__machine_length
		
		machine_diameter = \
			((machine_view_length * 2.0) ** 2.0 + machine_view_width ** 2.0) \
				** 0.5
				
		machine_radius = machine_diameter / 2.0
		
		
		
		# Создание последовательности записываемых состояний аппарата
		def generate_states_sequence():
			spawn_time = 0.0
			
			for trajectory_time, state in trajectory:
				if trajectory_time >= spawn_time:
					spawn_time += self.__time_interval
					
					yield state
					
					
		states_sequence = generate_states_sequence()
		
		
		
		# Запись последовательности состояний аппарата
		is_view_box_initialized                = False
		view_box_minimal_x, view_box_minimal_y = 0.0, 0.0
		view_box_maximal_x, view_box_maximal_y = 0.0, 0.0
		
		
		for state in states_sequence:
			# Создание образа состояния аппарата
			state_view_angle  = - state.coordinates[2] / math.pi * 180.0
			state_view_center = \
				state.coordinates[0] * coordinates_scaling, \
					- state.coordinates[1] * coordinates_scaling
					
			state_view_position = \
				state_view_center[0], \
					state_view_center[1] - machine_view_width / 2.0
					
			state_view = \
				Rect(
					insert       = state_view_position,
					size         = self.__machine_view_size,
					fill         = rgb(255, 255, 255),
					stroke       = rgb(0, 0, 0),
					stroke_width = 1
				)
				
			state_view.rotate(
				state_view_angle,
				center = state_view_center
			)
			
			
			# Добавление образа состояния аппарата к образу траектории
			drawing.add(state_view)
			
			if is_view_box_initialized:
				view_box_minimal_x, view_box_minimal_y = \
					min(state_view_center[0], view_box_minimal_x), \
						min(state_view_center[1], view_box_minimal_y)
						
				view_box_maximal_x, view_box_maximal_y = \
					max(state_view_center[0], view_box_maximal_x), \
						max(state_view_center[1], view_box_maximal_y)
			else:
				is_view_box_initialized = True
				
				view_box_minimal_x, view_box_minimal_y = \
					state_view_center[0], \
						state_view_center[1]
						
				view_box_maximal_x, view_box_maximal_y = \
					state_view_center[0], \
						state_view_center[1]
						
						
						
		# Настройка отображения образа траектории
		drawing.viewbox(
			minx   = view_box_minimal_x - machine_radius,
			miny   = view_box_minimal_y - machine_radius,
			width  = view_box_maximal_x - view_box_minimal_x + machine_diameter,
			height = view_box_maximal_y - view_box_minimal_y + machine_diameter
		)
		
		
		
		# Запись образа траектории в файл
		try:
			drawing.write(output_file)
		except:
			raise Exception() #!!!!! Генерировать хорошие исключения
示例#5
0
    def generate(self):

        # Concrete execution

        self.graph = CFGRepresentation()

        self.line(self.graph, 2, 2, "s", ["0", "1", "...", "t", "..."])
        self.line(self.graph, 2, 8, "s", ["0", "1", "...", "t"])
        self.draw("concrete_execution", np.array((7 * 4 + 4, 10)))

        # Symbolic execution

        self.graph = CFGRepresentation(12.5)

        def se(x, y, vstep, hstep, level, i):
            point = v(x, y)
            self.add(
                Node(point,
                     name="s",
                     index=i,
                     is_terminal=False,
                     is_feasible=True))
            point_ = v(x, y)
            point_1 = v(x + hstep, y - vstep)
            self.add(Arrow(point_, point_1, is_feasible=True))
            point_2 = v(x, y)
            point_3 = v(x + hstep, y + vstep)
            self.add(Arrow(point_2, point_3, is_feasible=True))
            point_4 = v(x, y)
            point_5 = v(x + hstep, y)
            self.add(Arrow(point_4, point_5, is_feasible=True))
            self.add(Ellipsis(v(x + hstep, y), v(0, 1)))
            if level == 2:
                return
            next = ",l" if i == "0" else (",m" if i == "0,0" else ",n")
            se(x + hstep, y - vstep, vstep / 2.0, hstep, level + 1, i + ",0")
            se(x + hstep, y + vstep, vstep / 2.0, hstep, level + 1, i + next)

        se(3, 20, 10, 9, 0, "0")
        self.draw("symbolic_execution", np.array((9 * 3 + 6, 40)))

        # Simple program

        self.graph = CFGRepresentation()
        x, y = 2, 4

        self.add(
            Text(
                svgwrite.text.Text("CFG",
                                   np.array((2.5, 2.5)) + np.array(
                                       (x, y)) * 5)))
        self.line(self.graph, x, y + 4, "s", ["0", "1"], 5, True, True, True)

        x += 12
        self.add(
            Text(
                svgwrite.text.Text(
                    "symbolic",
                    np.array((2.5, 2.5)) + np.array((x, y - 2)) * 5)))
        self.add(
            Text(
                svgwrite.text.Text("execution tree",
                                   np.array((2.5, 2.5)) + np.array(
                                       (x, y)) * 5)))
        self.line(self.graph, x, y + 4, "s", ["0", "1"], 5, True, True)

        x += 12
        text_wrap = svgwrite.text.Text(
            "",
            np.array((2.5, 2.5)) + np.array((x, y)) * 5)
        text_wrap.add(svgwrite.text.TSpan("P", font_style="italic"))
        text_wrap.add(
            svgwrite.text.TSpan("0", font_size="65%", baseline_shift="sub"))
        self.add(Text(text_wrap))
        self.line(self.graph, x, y + 4, "s", ["0", "1"], 5, True, True)

        self.draw("simple", np.array((x + 2, 18)))

        # Branch program

        c = CFG()
        s = Drawing()
        x, y = 2, 6
        c.add_vertex(Vertex("0", np.array((x + 4, y))))
        c.add_vertex(Vertex("1", np.array((x, y + 4)), is_terminal=True))
        c.add_vertex(Vertex("2", np.array((x + 8, y + 4)), is_terminal=True))
        c.add_edges([("0", "1"), ("0", "2")])
        c.draw_cfg(s,
                   title=(Text(
                       svgwrite.text.Text(
                           "CFG",
                           np.array((2.5, 2.5)) + np.array((6, 2)) * 5))))
        x += 10
        c.draw_paths(s, np.array((x, y)))

        with open(os.path.join(self.directory_name, "branch_paths.svg"),
                  "w+") as output_file:
            s.write(output_file)

        # Branch program

        self.graph = CFGRepresentation()
        x, y = 6, 2

        v0, v1, v2 = v(x, y), v(x - 4, y + 4), v(x + 4, y + 4)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="1", is_terminal=True))
        self.add(Node(v2, index="2", is_terminal=True))
        self.add(Arrow(v0, v1))
        self.add(Arrow(v0, v2))
        angle = math.pi / 2
        self.add(Loop(v1, angle))
        angle1 = math.pi / 2
        self.add(Loop(v2, angle1))

        x += 10
        v0, v1 = v(x, y), v(x, y + 5)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="1", is_terminal=True))
        self.add(Arrow(v0, v1))

        x += 5
        v0, v1 = v(x, y), v(x, y + 5)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="2", is_terminal=True))
        self.add(Arrow(v0, v1, is_feasible=True))

        x += 10
        v0, v1, v2 = v(x, y), v(x - 4, y + 4), v(x + 4, y + 4)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="1", is_terminal=True))
        self.add(Node(v2, index="2", is_terminal=True))
        self.add(Arrow(v0, v1))
        self.add(Arrow(v0, v2))

        self.draw("branch2")

        # Cycle program

        self.graph = CFGRepresentation()
        x, y = 2, 2

        v0, v1 = v(x, y), v(x, y + 5)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="1", is_terminal=True))
        self.add(Arrow(v0, v1))
        angle2 = math.pi / 2
        self.add(Loop(v1, angle2))
        self.add(Loop(v0, 0))

        x += 10
        v0, v1 = v(x, y), v(x, y + 5)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="1", is_terminal=True))
        self.add(Arrow(v0, v1))

        x += 5
        v0, v1, v2 = v(x, y), v(x, y + 5), v(x, y + 10)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="0"))
        self.add(Node(v2, index="1", is_terminal=True))
        self.add(Arrow(v0, v1))
        self.add(Arrow(v1, v2))

        x += 5
        v0, v1, v2, v3 = v(x, y), v(x, y + 5), v(x, y + 10), v(x, y + 15)
        self.add(Node(v0, index="0"))
        self.add(Node(v1, index="0"))
        self.add(Node(v2, index="0"))
        self.add(Node(v3, index="1", is_terminal=True))
        self.add(Arrow(v0, v1))
        self.add(Arrow(v1, v2))
        self.add(Arrow(v2, v3))

        x += 10
        v0, v1, v2 = v(x, y), v(x - 4, y + 4), v(x + 4, y + 4)
        for i in range(3):
            self.add(Node(v0, index="0"))
            self.add(Node(v1, index="1", is_terminal=True))
            self.add(Arrow(v0, v1))
            self.add(Arrow(v0, v2))
            v0 = v0 + v(4, 4)
            v1 = v1 + v(4, 4)
            v2 = v2 + v(4, 4)

        self.draw("cycle")

        # Control flow dependence

        g = CFG()
        g.add_vertices([("0", 6, 2), ("1", 2, 6), ("2", 2, 11), ("3", 10, 6),
                        ("4", 10, 11), ("5", 6, 15)])
        g.add_edges([("0", "1"), ("1", "2"), ("0", "3"), ("3", "4"),
                     ("2", "5"), ("4", "5")])
        s = Drawing()
        g.draw_cfg(s)
        with open(os.path.join(self.directory_name, "implicit.svg"),
                  "w+") as output_file:
            s.write(output_file)

        # Sequence comparison

        self.graph = CFGRepresentation()
        x, y = 2, 2

        v0, v1, v2 = v(x, y), v(x + 4, y + 4), v(x, y + 8)
        for i in range(4):
            id_ = str(i * 2)
            self.add(Node(v0, index=id_))
            id_1 = str(i * 2 + 1)
            self.add(Node(v1, index=id_1))
            self.add(Arrow(v0, v1))
            self.add(Arrow(v0, v2))
            self.add(Arrow(v1, v2))
            v0 = v0 + v(0, 8)
            v1 = v1 + v(0, 8)
            v2 = v2 + v(0, 8)
        self.add(Node(v0, index="8", is_terminal=True))

        self.draw("classic_cfg", (v0 + v(2 + 4, 2)))

        self.graph = CFGRepresentation()
        x, y = 2, 2

        for i in range(16):
            k = "0" * (6 - len(bin(i))) + bin(i)[2:]
            chain = ["0"]
            if k[3] == "1":
                chain.append("1")
            chain.append("2")
            if k[2] == "1":
                chain.append("3")
            chain.append("4")
            if k[1] == "1":
                chain.append("5")
            chain.append("6")
            if k[0] == "1":
                chain.append("7")
            chain.append("8")
            self.graph.add_chain(v(x, y), chain, is_terminated=True)
            x += 5

        self.draw("classic_paths")

        self.graph = CFGRepresentation()
        x, y = 2, 40 + 2 - 2.5

        def dr(index, x, y, step, count, f):
            point = v(x, y)
            id_ = str(index)
            t = index == 8
            self.add(Node(point, index=id_, is_terminal=t, is_feasible=f))
            if index == 8:
                return
            if index in [1, 3, 5]:
                count += 1
            if index % 2 == 0:
                point_ = v(x, y)
                point_1 = v(x + 7, y - step)
                f1 = count != 3
                self.add(Arrow(point_, point_1, is_feasible=f1))
                dr(index + 2, x + 7, y - step, step / 2.0, count, f=count != 3)
                point_2 = v(x, y)
                point_3 = v(x + 7, y + step)
                f2 = index < 6 or count == 3
                self.add(Arrow(point_2, point_3, is_feasible=f2))
                dr(index + 1,
                   x + 7,
                   y + step,
                   step / 2.0,
                   count,
                   f=index < 6 or count == 3)
            else:
                point_4 = v(x, y)
                point_5 = v(x + 7, y)
                f3 = index < 6 or count == 3
                self.add(Arrow(point_4, point_5, is_feasible=f3))
                dr(index + 1, x + 7, y, step, count, f=index < 6 or count == 3)

        dr(0, x, y, 20, 0, True)

        self.draw("classic_symbolic_tree", np.array((60, 80 + 4 - 5)))

        # Sequence comparison 2

        self.graph = CFGRepresentation()
        x, y = 2, 2

        v0, v1, v2 = v(x, y), v(x + 4, y + 4), v(x + 4, y + 9)
        v3 = v(x, y + 4 * 9)
        for i in range(4):
            id_2 = str(i * 2)
            self.add(Node(v0, index=id_2))
            id_3 = str(i * 2 + 1)
            self.add(Node(v1, index=id_3))
            self.add(Arrow(v0, v1))
            self.add(Arrow(v0, v3))
            self.add(Arrow(v1, v2))
            v0 = v0 + v(4, 9)
            v1 = v1 + v(4, 9)
            v2 = v2 + v(4, 9)
        self.add(Node(v0, index="8"))
        self.add(Arrow(v0, v3))
        self.add(Node(v3, index="9", is_terminal=True))

        self.draw("cascade_cfg")

        with open("result.html", "w+") as debug_file:
            debug_file.write("".join(
                map(lambda x: f"<img src=\"image/{x}.svg\">", self.ids)))
示例#6
0
class SvgRenderer(StreamRenderer):
    """
    Draws the board like an SVG image (best representation for web)
    """

    __rend_name__ = 'svg'

    DEFAULT_CELL_SIZE_IN_PIXELS = 15
    BOLD_EVERY = 5

    GRID_STROKE_WIDTH = 1
    GRID_BOLD_STROKE_WIDTH = 2

    @property
    def clues_font_size(self):
        """The size of the descriptions text"""
        return self.cell_size * 0.6

    def __init__(self,
                 board=None,
                 stream=stdout,
                 size=DEFAULT_CELL_SIZE_IN_PIXELS):
        super(SvgRenderer, self).__init__(board, stream)

        # decrease startup time when do not need this renderer
        from svgwrite import Drawing

        self.cell_size = size
        self.color_symbols = dict()
        self.drawing = Drawing(size=(self.full_width + self.cell_size,
                                     self.full_height + self.cell_size))
        self._add_definitions()

    def _color_id_by_name(self, color):
        color_id = self.board.color_id_by_name(color)
        if color_id:
            return color_id

        if is_list_like(color):
            return from_two_powers(
                self.board.color_id_by_name(single_color)
                for single_color in color)

        return None

    def _add_symbol(self, id_, color, *parts, **kwargs):
        drawing = self.drawing
        symbol = drawing.symbol(id_=id_, **kwargs)
        for part in parts:
            symbol.add(part)

        if color is not None:
            # SPACE is already an ID
            if color not in (SPACE, SPACE_COLORED):
                if self.is_colored:
                    color = self._color_id_by_name(color)

            self.color_symbols[color] = id_

        drawing.defs.add(symbol)

    def _add_definitions(self):
        drawing = self.drawing

        # dynamic style rules
        drawing.defs.add(
            drawing.style(
                'g.grid-lines line {stroke-width: %i} '
                'g.grid-lines line.bold {stroke-width: %i} '
                'g.header-clues text, g.side-clues text {font-size: %f} ' % (
                    self.GRID_STROKE_WIDTH,
                    self.GRID_BOLD_STROKE_WIDTH,
                    self.clues_font_size,
                )))

        self._add_colors_def()

        self._add_symbol('check',
                         None,
                         drawing.circle(r=40, stroke_width=10,
                                        center=(50, 50)),
                         drawing.polyline(stroke_width=12,
                                          points=[(35, 35), (35, 55),
                                                  (75, 55)],
                                          transform='rotate(-45 50 50)'),
                         stroke='green',
                         fill='none')

        self.check_icon_size = 100

    def _add_colors_def(self):
        drawing = self.drawing
        white_color = Color.white().name

        cell_size = self.cell_size
        rect_size = (cell_size, cell_size)

        upper_triangle_points = ((0, 0), (0, cell_size), (cell_size, 0))
        lower_triangle_points = ((0, cell_size), (cell_size, 0), (cell_size,
                                                                  cell_size))

        # three_colored_flag_rect_size = (cell_size / 3, cell_size)
        # three_colored_flag_insert_points = [(0, 0), (cell_size / 3, 0), (2 * cell_size / 3, 0)]
        three_color_triangle_size = round(cell_size * ((1 / 2)**0.5), 2)
        three_color_triangle_coord = round(
            cell_size - three_color_triangle_size, 2)

        three_colors_upper_points = [(0, 0), (0, three_color_triangle_size),
                                     (three_color_triangle_size, 0)]
        three_colors_lower_points = [
            (cell_size, three_color_triangle_coord),
            (three_color_triangle_coord, cell_size),
            (cell_size, cell_size),
        ]

        # rendering should be predictable
        colors = []
        if self.is_colored:
            for color_name in sorted(self.board.color_map):
                colors.append((color_name, self._color_from_name(color_name)))

            space_color = SPACE_COLORED
        else:
            colors.append((BOX, 'black'))
            space_color = SPACE

        for color_name, fill_color in colors:
            if color_name != white_color:
                self._add_symbol(
                    'color-%s' % color_name, color_name,
                    drawing.rect(
                        size=rect_size,
                        fill=fill_color,
                    ))

        if self.is_colored:
            for (color_name,
                 fill_color), (color_name2,
                               fill_color2) in combinations(colors, 2):
                LOG.info('Transient symbol: %s, %s + %s, %s', color_name,
                         fill_color, color_name2, fill_color2)
                color_tuple = (color_name, color_name2)

                self._add_symbol(
                    'x2-%s' % '-'.join(map(str, color_tuple)),
                    color_tuple,
                    drawing.polygon(
                        points=upper_triangle_points,
                        fill=fill_color,
                    ),
                    drawing.polygon(
                        points=lower_triangle_points,
                        fill=fill_color2,
                    ),
                )

            for (color_name,
                 fill_color), (color_name2,
                               fill_color2), (color_name3,
                                              fill_color3) in combinations(
                                                  colors, 3):
                LOG.info('Transient symbol: %s, %s + %s, %s + %s, %s',
                         color_name, fill_color, color_name2, fill_color2,
                         color_name3, fill_color3)
                color_tuple = (color_name, color_name2, color_name3)

                self._add_symbol(
                    'x3-%s' % '-'.join(map(str, color_tuple)),
                    color_tuple,
                    # drawing.rect(
                    #     insert=three_colored_flag_insert_points[0],
                    #     size=three_colored_flag_rect_size,
                    #     fill=fill_color,
                    # ),
                    # drawing.rect(
                    #     insert=three_colored_flag_insert_points[1],
                    #     size=three_colored_flag_rect_size,
                    #     fill=fill_color2,
                    # ),
                    # drawing.rect(
                    #     insert=three_colored_flag_insert_points[2],
                    #     size=three_colored_flag_rect_size,
                    #     fill=fill_color3,
                    # ),
                    drawing.rect(
                        size=rect_size,
                        fill=fill_color,
                    ),
                    drawing.polygon(
                        points=three_colors_upper_points,
                        fill=fill_color2,
                    ),
                    drawing.polygon(
                        points=three_colors_lower_points,
                        fill=fill_color3,
                    ),
                )

        # it's a little circle
        self._add_symbol('space', space_color,
                         drawing.circle(r=cell_size / 10))

    @property
    def pixel_side_width(self):
        """Horizontal clues side width in pixels"""
        return self.side_width * self.cell_size

    @property
    def pixel_header_height(self):
        """Vertical clues header height in pixels"""
        return self.header_height * self.cell_size

    @property
    def pixel_board_width(self):
        """The width of the main area in pixels"""
        return self.board.width * self.cell_size

    @property
    def pixel_board_height(self):
        """The height of the main area in pixels"""
        return self.board.height * self.cell_size

    @property
    def full_width(self):
        """Full width of the SVG board representation"""
        return self.pixel_side_width + self.pixel_board_width

    @property
    def full_height(self):
        """Full height of the SVG board representation"""
        return self.pixel_header_height + self.pixel_board_height

    def _color_from_name(self, color_name):
        return self.board.rgb_for_color_name(color_name)

    def block_svg(self, value, is_column, clue_number, block_number):
        """
        Return the SVG element for the clue number (colored case included)
        """
        # left to right, bottom to top
        block_number = -block_number

        shift = (0.85, -0.3) if is_column else (-0.3, 0.75)
        i, j = (clue_number, block_number) if is_column else (block_number,
                                                              clue_number)

        if isinstance(value, (list, tuple)):
            # colored board
            value, color_id = value[:2]
        else:
            color_id = None

        block_color = None
        if color_id is not None:
            id_ = self.color_symbols[color_id]

            if is_column:
                color_box = (i, j - 1)
            else:
                color_box = (i - 1, j)

            # drawing.g(class_=id_)
            insert_point = (self.pixel_side_width +
                            (color_box[0] * self.cell_size),
                            self.pixel_header_height +
                            (color_box[1] * self.cell_size))

            block_color = (id_, insert_point)

        extra = dict()
        if color_id == Color.black().id_:
            extra['fill'] = 'white'

        if value == BlottedBlock:
            text_value = ClueCell.BLOTTED_SYMBOL
        else:
            text_value = str(value)

        block_text = self.drawing.text(
            text_value,
            insert=(
                self.pixel_side_width + (i + shift[0]) * self.cell_size,
                self.pixel_header_height + (j + shift[1]) * self.cell_size,
            ),
            **extra)

        return block_color, block_text

    def draw_header(self):
        drawing = self.drawing

        drawing.add(
            drawing.rect(size=(self.pixel_side_width,
                               self.pixel_header_height),
                         class_='nonogram-thumbnail'))
        drawing.add(
            drawing.rect(insert=(self.pixel_side_width, 0),
                         size=(self.pixel_board_width,
                               self.pixel_header_height),
                         class_='nonogram-header'))

        header_group = drawing.g(class_='header-clues')
        for i, col_desc in enumerate(self.board.columns_descriptions):
            if self.board.column_solution_rate(i) == 1:
                x_pos = self.pixel_side_width + (i * self.cell_size)
                header_group.add(
                    drawing.rect(insert=(x_pos, 0),
                                 size=(self.cell_size,
                                       self.pixel_header_height),
                                 class_='solved'))

            for j, desc_item in enumerate(reversed(col_desc)):
                color, text = self.block_svg(desc_item, True, i, j)

                # color first, text next (to write on color)
                if color:
                    id_, insert_point = color
                    icon = drawing.use(
                        href='#' + id_,
                        insert=insert_point,
                    )

                    header_group.add(icon)

                header_group.add(text)

        drawing.add(header_group)

    def draw_side(self):
        drawing = self.drawing

        drawing.add(
            drawing.rect(insert=(0, self.pixel_header_height),
                         size=(self.pixel_side_width, self.pixel_board_height),
                         class_='nonogram-side'))

        side_group = drawing.g(class_='side-clues')
        for j, row_desc in enumerate(self.board.rows_descriptions):
            if self.board.row_solution_rate(j) == 1:
                y_pos = self.pixel_header_height + (j * self.cell_size)
                side_group.add(
                    drawing.rect(insert=(0, y_pos),
                                 size=(self.pixel_side_width, self.cell_size),
                                 class_='solved'))

            for i, desc_item in enumerate(reversed(row_desc)):
                color, text = self.block_svg(desc_item, False, j, i)

                # color first, text next (to write on color)
                if color:
                    id_, insert_point = color
                    icon = drawing.use(
                        href='#' + id_,
                        insert=insert_point,
                    )

                    side_group.add(icon)

                side_group.add(text)

        drawing.add(side_group)

        if self.board.is_solved_full:
            self._insert_solved_symbol()

    def _insert_solved_symbol(self):
        drawing = self.drawing

        check_icon_size = self.check_icon_size
        left_padding = (self.pixel_side_width - check_icon_size) / 2
        top_padding = (self.pixel_header_height - check_icon_size) / 2
        left_padding = max(left_padding, 0)
        top_padding = max(top_padding, 0)

        drawing.add(drawing.use('#check', insert=(left_padding, top_padding)))

    @classmethod
    def _color_code(cls, cell):
        if is_color_cell(cell):
            single_colors = two_powers(cell)
            if len(single_colors) > 3:  # allow two and three colors
                # multiple colors
                return UNKNOWN

        return cell

    def draw_grid(self, cells=None):
        if cells is None:
            cells = self.board.cells

        drawing = self.drawing

        drawing.add(
            drawing.rect(insert=(self.pixel_side_width,
                                 self.pixel_header_height),
                         size=(self.pixel_board_width,
                               self.pixel_board_height),
                         class_='nonogram-grid'))

        cell_groups = dict()
        for cell_value, id_ in iteritems(self.color_symbols):
            cell_groups[cell_value] = drawing.g(class_=id_)

        space_cell = SPACE_COLORED if self.is_colored else SPACE

        for j, row in enumerate(cells):
            for i, cell in enumerate(row):
                cell = self._color_code(cell)

                if cell == UNKNOWN:
                    continue

                if cell == space_cell:
                    insert_point = (self.pixel_side_width +
                                    (i + 0.5) * self.cell_size,
                                    self.pixel_header_height +
                                    (j + 0.5) * self.cell_size)
                else:
                    # for boxes colored and black
                    insert_point = (self.pixel_side_width +
                                    (i * self.cell_size),
                                    self.pixel_header_height +
                                    (j * self.cell_size))

                id_ = self.color_symbols[cell]
                icon = drawing.use(href='#' + id_, insert=insert_point)
                cell_groups[cell].add(icon)

        # to get predictable order
        for cell_value, group in sorted(iteritems(cell_groups),
                                        key=lambda x: x[0]):
            drawing.add(group)

        # write grid on top of the colors
        self._insert_grid_lines()

    def _insert_grid_lines(self):
        drawing = self.drawing

        grid_lines = drawing.g(class_='grid-lines')
        for line in self._get_grid_lines():
            grid_lines.add(line)

        drawing.add(grid_lines)

    def _get_grid_lines(self):
        drawing = self.drawing

        # draw horizontal lines
        for i in range(self.board.height + 1):
            extra = dict()

            if i % self.BOLD_EVERY == 0 or i == self.board.height:
                extra['class'] = 'bold'

            y_pos = self.pixel_header_height + (i * self.cell_size)
            yield drawing.line(start=(0, y_pos),
                               end=(self.full_width, y_pos),
                               **extra)

        # draw vertical lines
        for i in range(self.board.width + 1):
            extra = dict()

            if i % self.BOLD_EVERY == 0 or i == self.board.width:
                extra['class'] = 'bold'

            x_pos = self.pixel_side_width + (i * self.cell_size)
            yield drawing.line(start=(x_pos, 0),
                               end=(x_pos, self.full_height),
                               **extra)

    def render(self):
        self.drawing.write(self.stream)
        # self._print(self.drawing.tostring())

    def draw(self, cells=None):
        self.drawing.elements = []
        self.drawing.add(self.drawing.defs)

        super(SvgRenderer, self).draw(cells=cells)