Exemple #1
0
class Path(object):
    '''
    id    : string, copied from the svg tag's id attribute, or an autogenerated
            int if there is no id attr.
    loops : a list of loops. A loop is a list of vertices. A vertex is a pair
            of floats or ints.
    color : triple of unsigned bytes, (r, g, b)
    A Path corresponds to a single SVG path tag. It may contain many
    independant loops which represent either disjoint polygons or holes.
    '''

    next_id = 1

    def __init__(self, tag=None):
        self.id = None
        self.loops = []
        self.color = (0, 0, 0)
        self.bounds = Bounds()
        self.triangles = None

        if tag:
            self.parse(tag)


    def get_id(self, attributes):
        if 'id' in attributes.keys():
            return attributes['id'].value
        else:
            self.next_id += 1
            return self.next_id - 1


    def parse_color(self, color):
        '''
        color : string, eg: '#rrggbb' or 'none'
        (where rr, gg, bb are hex digits from 00 to ff)
        returns a triple of unsigned bytes, eg: (0, 128, 255)
        '''
        if color == 'none':
            return None
        return (
            int(color[1:3], 16),
            int(color[3:5], 16),
            int(color[5:7], 16))


    def parse_style(self, style):
        '''
        style : string, eg:
            fill:#ff2a2a;fill-rule:evenodd;stroke:none;stroke-width:1px;
            stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1
        returns color as a triple of unsigned bytes: (r, g, b), or None
        '''
        style_elements = style.split(';')
        while style_elements:
            element = style_elements.pop()
            if element.startswith('fill:'):
                return self.parse_color(element[5:])
        return None


    def parse(self, tag):
        self.id = self.get_id(tag.attributes)
        if 'style' in tag.attributes.keys():
            style_data = tag.attributes['style'].value
            self.color = self.parse_style(style_data)
        
        path_data = tag.attributes['d'].value
        parser = PathDataParser()
        path_tuple = parser.to_tuples(path_data)
        tracer = SvgLoopTracer()
        self.loops = tracer.to_loops(path_tuple)
        self.bounds.add_bounds(tracer.bounds)


    def offset(self, x, y):
        for loop in self.loops:
            for idx, vert in enumerate(loop):
                loop[idx] = (vert[0] + x, vert[1] + y)
        self.bounds.offset(x, y)


    def tessellate(self):
        if self.color:
            self.triangles = tesselate(self.loops)


    def _serialise_verts(self):
        for vert in self.triangles:
            yield vert[0]
            yield vert[1]


    def add_to_batch(self, batch):
        '''
        Adds itself to the given batch, as as single primitive of indexed
        GL_TRIANGLES. Note that Batch will aggregate all such additions into
        a single large primitive.
        '''
        if self.triangles:
            num_verts = len(self.triangles)
            serial_verts = list(self._serialise_verts())
            colors = self.color * num_verts
            indices = range(num_verts)
            batch.add_indexed(
                num_verts,
                GL_TRIANGLES,
                None,
                indices,
                ('v2f/static', serial_verts),
                ('c3B/static', colors),
            )
Exemple #2
0
class SvgBatch(object):
    '''
    Maintains an ordered list of paths, each one corresponding to a path tag
    from an SVG file. Creates a pylget Batch containing all these paths, for
    rendering as a single OpenGL GL_TRIANGLES indexed vert primitive.
    '''
    def __init__(self, filename=None):
        '''
        filename: string, absolute or relative filename of an SVG file
        '''
        self.filename = filename
        self.paths = []
        self.path_by_id = {}
        self.bounds = Bounds()
        self.batch = None

    @property
    def width(self):
        return self.bounds.width

    @property
    def height(self):
        return self.bounds.height


    def __getattr__(self, name):
        if hasattr(self.path_by_id, name):
            return getattr(self.path_by_id, name)
        raise AttributeError(name)

    def __getitem__(self, index):
        return self.path_by_id[index]


    def parse_svg(self):
        '''
        Populates self.paths from the <path> tags in the svg file.
        '''
        doc = xml.dom.minidom.parse(self.filename)       
        path_tags = doc.getElementsByTagName('path')
        for path_tag in path_tags:
            path = Path(path_tag)
            self.paths.append(path)
            self.path_by_id[path.id] = path
            self.bounds.add_bounds(path.bounds)


    def center(self):
        '''
        Offset all verts put center of svg at the origin
        '''
        center = self.bounds.get_center()
        for path in self.paths:
            path.offset(-center[0], -center[1])
        self.bounds.offset(-center[0], -center[1])


    def create_batch(self):
        '''
        Returns a new pyglet Batch object populated with indexed GL_TRIANGLES
        '''
        if self.batch is None:
            self.batch = Batch()
            self.parse_svg()
            self.center()
            for path in self.paths:
                path.tessellate()
                path.add_to_batch(self.batch)
        return self.batch