コード例 #1
0
    def run(self):
        """Main entry point for Inkscape extensions.
        """
        # Set up debug SVG output context.
        geom.debug.set_svg_context(self.debug_svg)

        selected_elements = self.get_elements()
        if not len(selected_elements):
            # Nothing selected or document is empty
            return

        # Create a new layer for the SVG output.
        layer = None
        if self.options.new_layer:
            layer = self.svg.create_layer(self._LAYER_NAME, incr_suffix=True)

        for n in range(self.options.copies):
            for element in selected_elements:
                m_elem = self.svg.parse_transform_attr(
                    element.get('transform'))
                v = geom.P.from_polar(self.options.interval * (n + 1),
                                      -self.options.angle)
                m_translate = transform2d.matrix_translate(v.x, v.y)
                m_transform = transform2d.compose_transform(
                    m_elem, m_translate)
                transform_attr = svg.transform_attr(m_transform)
                elem_copy = deepcopy(element)
                elem_copy.set('transform', transform_attr)
                #                elem_copy.set('id', element.get('id') + '_r')
                self.svg.add_elem(elem_copy, parent=layer)
コード例 #2
0
ファイル: svg.py プロジェクト: sod4602/tcnc
    def parse_transform_attr(self, transform_attr):
        """Parse an SVG transform attribute.

        Args:
            transform_attr: A string containing the SVG transform list.

        Returns:
            A single affine transform matrix.
        """
        if (transform_attr is None or not transform_attr
                or transform_attr.isspace()):
            return transform2d.IDENTITY_MATRIX
        transform_attr = transform_attr.strip()
        transforms = self._TRANSFORM_RE.findall(transform_attr)
        matrices = []
        for transform, args in transforms:
            matrix = None
            values = [float(n) for n in args.replace(',', ' ').split()]
            num_values = len(values)
            if transform == 'translate':
                x = values[0]
                y = values[1] if num_values > 1 else 0.0
                matrix = transform2d.matrix_translate(x, y)
            if transform == 'scale':
                x = values[0]
                y = values[1] if num_values > 1 else x
                matrix = transform2d.matrix_scale(x, y)
            if transform == 'rotate':
                a = math.radians(values[0])
                cx = values[1] if num_values > 1 else 0.0
                cy = values[2] if num_values > 2 else 0.0
                matrix = transform2d.matrix_rotate(a, (cx, cy))
            if transform == 'skewX':
                a = math.radians(values[0])
                matrix = transform2d.matrix_skew_x(a)
            if transform == 'skewY':
                a = math.radians(values[0])
                matrix = transform2d.matrix_skew_y(a)
            if transform == 'matrix':
                matrix = ((values[0], values[2], values[4]),
                          (values[1], values[3], values[5]))
            if matrix is not None:
                matrices.append(matrix)

        # Compose all the tranforms into one matrix
        result_matrix = transform2d.IDENTITY_MATRIX
        for matrix in matrices:
            result_matrix = transform2d.compose_transform(
                result_matrix, matrix)

        return result_matrix
コード例 #3
0
ファイル: svg.py プロジェクト: utlco/tcnc
    def parse_transform_attr(self, transform_attr):
        """Parse an SVG transform attribute.

        Args:
            transform_attr: A string containing the SVG transform list.

        Returns:
            A single affine transform matrix.
        """
        if (transform_attr is None or not transform_attr or
            transform_attr.isspace()):
            return transform2d.IDENTITY_MATRIX
        transform_attr = transform_attr.strip()
        transforms = self._TRANSFORM_RE.findall(transform_attr)
        matrices = []
        for transform, args in transforms:
            matrix = None
            values = [float(n) for n in args.replace(',', ' ').split()]
            num_values = len(values)
            if transform == 'translate':
                x = values[0]
                y = values[1] if num_values > 1 else 0.0
                matrix = transform2d.matrix_translate(x, y)
            if transform == 'scale':
                x = values[0]
                y = values[1] if num_values > 1 else x
                matrix = transform2d.matrix_scale(x, y)
            if transform == 'rotate':
                a = math.radians(values[0])
                cx = values[1] if num_values > 1 else 0.0
                cy = values[2] if num_values > 2 else 0.0
                matrix = transform2d.matrix_rotate(a, (cx, cy))
            if transform == 'skewX':
                a = math.radians(values[0])
                matrix = transform2d.matrix_skew_x(a)
            if transform == 'skewY':
                a = math.radians(values[0])
                matrix = transform2d.matrix_skew_y(a)
            if transform == 'matrix':
                matrix = ((values[0], values[2], values[4]),
                          (values[1], values[3], values[5]))
            if matrix is not None:
                matrices.append(matrix)

        # Compose all the tranforms into one matrix
        result_matrix = transform2d.IDENTITY_MATRIX
        for matrix in matrices:
            result_matrix = transform2d.compose_transform(result_matrix, matrix)

        return result_matrix
コード例 #4
0
ファイル: svg.py プロジェクト: sod4602/tcnc
    def get_element_transform(self, node, root=None):
        """Get the combined transform of the element and it's combined parent
        transforms.
        
        Args:
            node: The element node.
            root: The document root or where to stop searching.

        Returns:
            The combined transform matrix or the identity matrix
            if none found.
        """
        matrix = self.get_parent_transform(node, root)
        transform_attr = node.get('transform')
        if transform_attr is not None and transform_attr:
            node_transform = self.parse_transform_attr(transform_attr)
            matrix = transform2d.compose_transform(matrix, node_transform)
        return matrix
コード例 #5
0
ファイル: svg.py プロジェクト: utlco/tcnc
    def get_element_transform(self, node, root=None):
        """Get the combined transform of the element and it's combined parent
        transforms.
        
        Args:
            node: The element node.
            root: The document root or where to stop searching.

        Returns:
            The combined transform matrix or the identity matrix
            if none found.
        """
        matrix = self.get_parent_transform(node, root)
        transform_attr = node.get('transform')
        if transform_attr is not None and transform_attr:
            node_transform = self.parse_transform_attr(transform_attr)
            matrix = transform2d.compose_transform(matrix, node_transform)
        return matrix
コード例 #6
0
ファイル: svg.py プロジェクト: utlco/tcnc
    def get_parent_transform(self, node, root=None):
        """Get the combined transform of the node's parents.

        Args:
            node: The child node.
            root: The document root or where to stop searching.

        Returns:
            The parent transform matrix or the identity matrix
            if none found.
        """
        matrix = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
        parent = node.getparent()
        while parent is not root:
            parent_transform_attr = parent.get('transform')
            if parent_transform_attr is not None:
                parent_matrix = self.parse_transform_attr(parent_transform_attr)
                matrix = transform2d.compose_transform(parent_matrix, matrix)
            parent = parent.getparent()
        return matrix
コード例 #7
0
ファイル: svg.py プロジェクト: sod4602/tcnc
    def get_parent_transform(self, node, root=None):
        """Get the combined transform of the node's parents.

        Args:
            node: The child node.
            root: The document root or where to stop searching.

        Returns:
            The parent transform matrix or the identity matrix
            if none found.
        """
        matrix = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]
        parent = node.getparent()
        while parent is not root:
            parent_transform_attr = parent.get('transform')
            if parent_transform_attr is not None:
                parent_matrix = self.parse_transform_attr(
                    parent_transform_attr)
                matrix = transform2d.compose_transform(parent_matrix, matrix)
            parent = parent.getparent()
        return matrix
コード例 #8
0
ファイル: inksvg.py プロジェクト: sod4602/tcnc
    def _get_shape_nodes_recurs(self, node, shapetags, parent_transform,
                                check_parent, skip_layers,
                                accumulate_transform):
        """Recursively traverse an SVG node tree and flatten it to a list of
        tuples containing an SVG shape element and its accumulated transform.

        This does a depth-first traversal of <g> and <use> elements.
        Anything besides paths, rectangles, circles, ellipses, lines, polygons,
        and polylines are ignored.

        Hidden elements are ignored.

        Args:
            node: The root of the node tree to traverse and flatten.
            shapetags: List of shape element tags that can be fetched.
            parent_transform: Transform matrix to add to each node's transforms.
            check_parent: Check parent visibility
            skip_layers: A list of layer names (as regexes) to ignore
            accumulate_transform: Apply parent transform(s) to element node
                if True.

        Returns:
            A possibly empty list of 2-tuples consisting of
            SVG element and transform.
        """
        if not self.node_is_visible(node, check_parent=check_parent):
            return []
        if parent_transform is None:
            parent_transform = self.get_parent_transform(node)
        nodelist = []
        # first apply the current transform matrix to this node's tranform
        node_transform = self.parse_transform_attr(node.get('transform'))
        if accumulate_transform:
            node_transform = transform2d.compose_transform(
                parent_transform, node_transform)
        if self.node_is_group(node):
            if self.is_layer(node) and skip_layers is not None and skip_layers:
                layer_name = self.get_layer_name(node)
                #                logger.debug('layer: %s', layer_name)
                for skip_layer in skip_layers:
                    if re.match(skip_layer, layer_name) is not None:
                        #                        logger.debug('skipping layer: %s', layer_name)
                        return []
            # Recursively traverse group children
            for child_node in node:
                subnodes = self._get_shape_nodes_recurs(
                    child_node, shapetags, node_transform, False, skip_layers,
                    accumulate_transform)
                nodelist.extend(subnodes)
        elif node.tag == svg_ns('use') or node.tag == 'use':
            # A <use> element refers to another SVG element via an
            # xlink:href="#id" attribute.
            refid = node.get(svg.xlink_ns('href'))
            if refid:
                # [1:] to ignore leading '#' in reference
                refnode = self.get_node_by_id(refid[1:])
                # TODO: Can the referred node not be visible?
                if refnode is not None:  # and self.node_is_visible(refnode):
                    # Apply explicit x,y translation transform
                    x = float(node.get('x', '0'))
                    y = float(node.get('y', '0'))
                    if x != 0 or y != 0:
                        translation = transform2d.matrix_translate(x, y)
                        node_transform = transform2d.compose_transform(
                            node_transform, translation)
                    subnodes = self._get_shape_nodes_recurs(
                        refnode, shapetags, node_transform, False, skip_layers,
                        accumulate_transform)
                    nodelist.extend(subnodes)
        elif svg.strip_ns(node.tag) in shapetags:
            nodelist.append((node, node_transform))
        return nodelist
コード例 #9
0
    def run(self):
        """Main entry point for Inkscape plugins.
        """
        random.seed()
        geom.set_epsilon(self.options.epsilon)
        geom.debug.set_svg_context(self.debug_svg)

        doc_size = geom.P(self.svg.get_document_size())
        self.doc_center = doc_size / 2

        # Determine clipping rectangle which is bounded by the document
        # or the margins, depending on user options. The margins can
        # be outside the document bounds.
        # (Y axis is inverted in Inkscape)
        clip_rect = None # Default
        bottom_left = geom.P(self.options.margin_left, self.options.margin_top)
        top_right = doc_size - geom.P(self.options.margin_right,
                                     self.options.margin_bottom)
        self.margin_clip_rect = geom.box.Box(bottom_left, top_right)
        doc_clip_rect = geom.box.Box(geom.P(0, 0), doc_size)
        if self.options.clip_to_doc and self.options.clip_to_margins:
            clip_rect = doc_clip_rect.intersection(self.margin_clip_rect)
        elif self.options.clip_to_doc:
            clip_rect = doc_clip_rect
        elif self.options.clip_to_margins:
            clip_rect = self.margin_clip_rect

        # The clipping region can be a circle or a rectangle
        if clip_rect is not None and self.options.clip_to_circle:
            radius = min(clip_rect.width(), clip_rect.height()) / 2.0
            self.clip_region = geom.ellipse.Ellipse(clip_rect.center(), radius)
        else:
            self.clip_region = clip_rect

        if self.options.clip_offset_center:
            clip_offset = clip_rect.center() - self.doc_center
            self.options.offset_x += clip_offset.x
            self.options.offset_y += clip_offset.y

        # Optionally insert spherical point projection
        if self.options.project_sphere:
            projector = SphericalProjector(self.doc_center,
                                                self.options.project_radius,
                                                invert=self.options.project_invert)
        else:
            projector = IdentityProjector()
        # Set up plotter transform for rotation, scale, and offset.
        # Origin at document center.
        scale = self.options.scale * self._SCALE_SCALE
        offset = geom.P(self.doc_center) + geom.P(self.options.offset_x,
                                                  self.options.offset_y)
        transform1 = transform2d.matrix_rotate(self.options.rotate)
        transform2 = transform2d.matrix_scale_translate(scale, scale,
                                                        offset.x, offset.y)
        plot_transform = transform2d.compose_transform(transform1, transform2)
        plotter = _QuasiPlotter(self.clip_region, plot_transform, projector)

        # Create color LUTs
        for i in range(255):
            ci = 255 - i
            self._FILL_LUT['red'].append('#%02x0000' % ci)
            self._FILL_LUT['yellow'].append('#%02x%02x00' % (ci, ci))

        q = quasi.Quasi()
        q.offset_salt_x = self.options.salt_x
        q.offset_salt_y = self.options.salt_y
        q.skinnyfat_ratio = self.options.skinnyfat_ratio
        q.segment_ratio = self.options.segment_ratio
        q.segtype_skinny = self.options.segtype_skinny
        q.segtype_fat = self.options.segtype_fat
        q.segment_split_cross = self.options.segment_split_cross
        q.symmetry = self.options.symmetry
        q.numlines = self.options.numlines
        q.plotter = plotter
        q.color_fill = self.options.polygon_fill
        q.color_by_polytype = self.options.polygon_zfill
        q.quasi()

        # Re-center the quasi polygons to the clip region borders
        if self.options.clip_recenter and self.clip_region is not None:
            q.plotter.recenter()

        polygon_segment_graph = planargraph.Graph()
        for poly in q.plotter.polygons:
            polygon_segment_graph.add_poly(poly)
        polygon_segments = list(polygon_segment_graph.edges)

        # Optionally sort the polygons to change drawing order.
        if self.options.polygon_sort != self.POLYGON_SORT_NONE:
            outer_hull = polygon_segment_graph.boundary_polygon()
            hull_centroid = polygon.centroid(outer_hull)
            # Find the distance of the farthest polygon
            max_d = 0.0
            for poly in q.plotter.polygons:
                d = hull_centroid.distance(polygon.centroid(poly))
                if d > max_d:
                    max_d = d
            segment_size = q.plotter.polygons[0][0].distance(q.plotter.polygons[0][1])

            # Secondary sort key is angular location
            angle_key = lambda poly: hull_centroid.ccw_angle2(hull_centroid + geom.P(1, 0), polygon.centroid(poly))
            q.plotter.polygons.sort(key=angle_key)
            angle_key = lambda segment: hull_centroid.ccw_angle2(hull_centroid + geom.P(1, 0), segment.midpoint())
            polygon_segments.sort(key=angle_key)
            q.plotter.segments.sort(key=angle_key)

            # Primary sort key is distance from centroid
            dist_key = lambda poly: int(hull_centroid.distance(polygon.centroid(poly)) / segment_size)
            dist_key2 = lambda segment: int(hull_centroid.distance(segment.midpoint()) / segment_size)
            if self.options.polygon_sort == self.POLYGON_SORT_INSIDE_OUT:
                q.plotter.polygons.sort(key=dist_key)
                polygon_segments.sort(key=dist_key2)
                q.plotter.segments.sort(key=dist_key2)
            elif self.options.polygon_sort == self.POLYGON_SORT_OUTSIDE_IN:
                q.plotter.polygons.sort(key=dist_key, reverse=True)
                polygon_segments.sort(key=dist_key2, reverse=True)
                q.plotter.segments.sort(key=dist_key2, reverse=True)
#             angle_key = lambda poly: hull_centroid.ccw_angle2(hull_centroid + geom.P(1, 0), polygon.centroid(poly))
#             q.plotter.polygons.sort(key=angle_key)
#             angle_key = lambda segment: hull_centroid.ccw_angle2(hull_centroid + geom.P(1, 0), segment.midpoint())
#             polygon_segments.sort(key=angle_key)

        # Update styles with any command line option values
        self._styles.update(self.svg.styles_from_templates(
            self._styles, self._style_defaults, vars(self.options)))

#         logger.debug('colors: %d' % len(plotter.color_count))
#         for color in sorted(plotter.color_count.keys()):
#             logger.debug('[%.5f]: %d' % (color, plotter.color_count[color]))

        if self.options.create_info_layer:
            self._draw_info_layer()

        if self.options.margin_draw:
            self._draw_margins(q.plotter.bbox())

        if self.options.polygon_draw:
            self._draw_polygons(q.plotter)
#            self._draw_polygon_circles(q.plotter.polygons)

        if self.options.polyseg_draw:
            self._draw_polygon_segments(polygon_segments)

        if self.options.polygon_mult > 0:
            self._draw_inset_polygons(q.plotter.polygons,
                                       self.options.polygon_mult_spacing,
                                       self.options.polygon_mult)

        if self.options.ellipse_draw:
            self._draw_polygon_ellipses(q.plotter.polygons,
                                        self.options.ellipse_inset)

        if self.options.segtype_skinny == quasi.Quasi.SEG_NONE \
        and self.options.segtype_fat == quasi.Quasi.SEG_NONE:
            self.options.segment_draw = False
            self.options.segpath_draw = False

        if self.options.segment_draw:
            self._draw_segments(q.plotter.segments)
            self._draw_segboxes(q.plotter)

        if self.options.segpath_draw:
            self._draw_segment_chains(q.plotter.segments)

        if self.options.frame_draw and (self.options.frame_width > 0 and
                                        self.options.frame_height > 0):
            self._draw_frame()
コード例 #10
0
ファイル: geomsvg.py プロジェクト: utlco/tcnc
def svg_element_to_geometry(element, element_transform=None,
                            parent_transform=None):
    """Convert the SVG shape element to a list of one or more
    Line, Arc, and/or CubicBezier segments,
    and apply node/parent transforms.
    The coordinates of the segments will be absolute with
    respect to the parent container.

    Args:
        element: An SVG Element shape node.
        element_transform: An optional transform to apply to the element.
            Default is None.
        parent_transform: An optional parent transform to apply to the element.
            Default is None.

    Returns:
        A list of zero or more paths.
        A path being a list of zero or more Line, Arc, EllipticalArc,
        or CubicBezier objects.
    """
    # Convert the element to a list of subpaths
    subpath_list = []
    tag = svg.strip_ns(element.tag) # tag stripped of namespace part
    if tag == 'path':
        d = element.get('d')
        if d is not None and d:
            subpath_list = parse_path_geom(d, ellipse_to_bezier=True)
    else:
        subpath = []
        if tag == 'line':
            subpath = convert_line(element)
        elif tag == 'ellipse':
            ellipse = convert_ellipse(element)
            subpath = bezier.bezier_ellipse(ellipse)
        elif tag == 'rect':
            subpath = convert_rect(element)
        elif tag == 'circle':
            subpath = convert_circle(element)
        elif tag == 'polyline':
            subpath = convert_polyline(element)
        elif tag == 'polygon':
            subpath = convert_polygon(element)
        if subpath:
            subpath_list = [subpath, ]

    if subpath_list:
        # Create a transform matrix that is composed of the
        # parent transform and the element transform
        # so that control points are in absolute coordinates.
        if parent_transform is not None:
            element_transform = transform2d.compose_transform(parent_transform,
                                                              element_transform)
        if element_transform is not None:
            x_subpath_list = []
            for subpath in subpath_list:
                x_subpath = []
                for segment in subpath:
                    # Skip zero-length segments.
                    if not segment.p1 == segment.p2:
                        segment = segment.transform(element_transform)
                        x_subpath.append(segment)
                x_subpath_list.append(x_subpath)
            return x_subpath_list
    return subpath_list
コード例 #11
0
ファイル: inksvg.py プロジェクト: utlco/tcnc
    def _get_shape_nodes_recurs(self, node, shapetags, parent_transform,
                                check_parent, skip_layers,
                                accumulate_transform):
        """Recursively traverse an SVG node tree and flatten it to a list of
        tuples containing an SVG shape element and its accumulated transform.

        This does a depth-first traversal of <g> and <use> elements.
        Anything besides paths, rectangles, circles, ellipses, lines, polygons,
        and polylines are ignored.

        Hidden elements are ignored.

        Args:
            node: The root of the node tree to traverse and flatten.
            shapetags: List of shape element tags that can be fetched.
            parent_transform: Transform matrix to add to each node's transforms.
            check_parent: Check parent visibility
            skip_layers: A list of layer names (as regexes) to ignore
            accumulate_transform: Apply parent transform(s) to element node
                if True.

        Returns:
            A possibly empty list of 2-tuples consisting of
            SVG element and transform.
        """
        if not self.node_is_visible(node, check_parent=check_parent):
            return []
        if parent_transform is None:
            parent_transform = self.get_parent_transform(node)
        nodelist = []
        # first apply the current transform matrix to this node's tranform
        node_transform = self.parse_transform_attr(node.get('transform'))
        if accumulate_transform:
            node_transform = transform2d.compose_transform(parent_transform,
                                                           node_transform)
        if self.node_is_group(node):
            if self.is_layer(node) and skip_layers is not None and skip_layers:
                layer_name = self.get_layer_name(node)
#                logger.debug('layer: %s', layer_name)
                for skip_layer in skip_layers:
                    if re.match(skip_layer, layer_name) is not None:
#                        logger.debug('skipping layer: %s', layer_name)
                        return []
            # Recursively traverse group children
            for child_node in node:
                subnodes = self._get_shape_nodes_recurs(child_node, shapetags,
                                                        node_transform,
                                                        False, skip_layers,
                                                        accumulate_transform)
                nodelist.extend(subnodes)
        elif node.tag == svg_ns('use') or node.tag == 'use':
            # A <use> element refers to another SVG element via an
            # xlink:href="#id" attribute.
            refid = node.get(svg.xlink_ns('href'))
            if refid:
                # [1:] to ignore leading '#' in reference
                refnode = self.get_node_by_id(refid[1:])
                # TODO: Can the referred node not be visible?
                if refnode is not None: # and self.node_is_visible(refnode):
                    # Apply explicit x,y translation transform
                    x = float(node.get('x', '0'))
                    y = float(node.get('y', '0'))
                    if x != 0 or y != 0:
                        translation = transform2d.matrix_translate(x, y)
                        node_transform = transform2d.compose_transform(
                                            node_transform, translation)
                    subnodes = self._get_shape_nodes_recurs(refnode, shapetags,
                                                            node_transform,
                                                            False, skip_layers,
                                                            accumulate_transform)
                    nodelist.extend(subnodes)
        elif svg.strip_ns(node.tag) in shapetags:
            nodelist.append((node, node_transform))
        return nodelist
コード例 #12
0
ファイル: geomsvg.py プロジェクト: sod4602/tcnc
def svg_element_to_geometry(element,
                            element_transform=None,
                            parent_transform=None):
    """Convert the SVG shape element to a list of one or more
    Line, Arc, and/or CubicBezier segments,
    and apply node/parent transforms.
    The coordinates of the segments will be absolute with
    respect to the parent container.

    Args:
        element: An SVG Element shape node.
        element_transform: An optional transform to apply to the element.
            Default is None.
        parent_transform: An optional parent transform to apply to the element.
            Default is None.

    Returns:
        A list of zero or more paths.
        A path being a list of zero or more Line, Arc, EllipticalArc,
        or CubicBezier objects.
    """
    # Convert the element to a list of subpaths
    subpath_list = []
    tag = svg.strip_ns(element.tag)  # tag stripped of namespace part
    if tag == 'path':
        d = element.get('d')
        if d is not None and d:
            subpath_list = parse_path_geom(d, ellipse_to_bezier=True)
    else:
        subpath = []
        if tag == 'line':
            subpath = convert_line(element)
        elif tag == 'ellipse':
            ellipse = convert_ellipse(element)
            subpath = bezier.bezier_ellipse(ellipse)
        elif tag == 'rect':
            subpath = convert_rect(element)
        elif tag == 'circle':
            subpath = convert_circle(element)
        elif tag == 'polyline':
            subpath = convert_polyline(element)
        elif tag == 'polygon':
            subpath = convert_polygon(element)
        if subpath:
            subpath_list = [
                subpath,
            ]

    if subpath_list:
        # Create a transform matrix that is composed of the
        # parent transform and the element transform
        # so that control points are in absolute coordinates.
        if parent_transform is not None:
            element_transform = transform2d.compose_transform(
                parent_transform, element_transform)
        if element_transform is not None:
            x_subpath_list = []
            for subpath in subpath_list:
                x_subpath = []
                for segment in subpath:
                    # Skip zero-length segments.
                    if not segment.p1 == segment.p2:
                        segment = segment.transform(element_transform)
                        x_subpath.append(segment)
                x_subpath_list.append(x_subpath)
            return x_subpath_list
    return subpath_list