def test_line_collection_extend_two_lines(lines): lc = LineCollection([(3, 3j)]) lc.extend(lines) assert len(lc) == 3 assert np.all(lc[0] == np.array([3, 3j])) assert np.all(lc[1] == np.array([0, 1 + 1j])) assert np.all(lc[2] == np.array([2 + 2j, 3 + 3j, 4 + 4j]))
def test_extend_same_as_init(lines): lc1 = LineCollection(lines) lc2 = LineCollection() lc2.extend(lines) assert len(lc1) == len(lc2) for l1, l2 in zip(lc1, lc2): assert np.all(l1 == l2)
def lcopy(document, sources, dest, prob: Optional[float]): """Copy the content of one or more layer(s) to another layer. SOURCES can be a single layer ID, the string 'all' (to copy all non-empty layers, or a coma-separated, whitespace-free list of layer IDs. DEST can be a layer ID or the string 'new', in which case a new layer with the lowest available ID will be created. If a layer is both in the source and destination, its content is not duplicated. The `--prob` option controls the probability with which each path is copied. With a value lower than 1.0, some paths will not be copied to DEST, which may be used to achieve random coloring effects. Examples: Copy layer 1 to a new layer: vpype [...] lcopy 1 new [...] # duplicate layer 1 Make a new layer with a merged copy of layer 1 and 2: vpype [...] lcopy 1,2 new [...] # make new layer with merged copy of layer 1 and 2 Add a merged copy of all layers to layer 1. If layer 1 previously had content, this \ content is not duplicated: vpype [...] lcopy all 1 [...] """ src_lids = multiple_to_layer_ids(sources, document) dest_lid = single_to_layer_id(dest, document) if dest_lid in src_lids: src_lids.remove(dest_lid) lc = LineCollection() for lid in src_lids: if prob is not None: for line in document[lid]: if random.random() < prob: lc.append(line) else: lc.extend(document[lid]) if len(lc) > 0: document.add(lc, dest_lid) return document
def splitall(lines: LineCollection) -> LineCollection: """ Split all paths into their constituent segments. This command may be used together with `linemerge` for cases such as densely-connected meshes where the latter cannot optimize well enough by itself. Note that since some paths (especially curved ones) can be made of a large number of segments, this command may significantly increase the processing time of the pipeline. """ new_lines = LineCollection() for line in lines: new_lines.extend([line[i:i + 2] for i in range(len(line) - 1)]) return new_lines
def lcopy(vector_data, sources, dest): """Copy the content of one or more layer(s) to another layer. SOURCES can be a single layer ID, the string 'all' (to copy all non-empty layers, or a coma-separated, whitespace-free list of layer IDs. DEST can be a layer ID or the string 'new', in which case a new layer with the lowest available ID will be created. If a layer is both in the source and destination, its content is not duplicated. Examples: Copy layer 1 to a new layer: vpype [...] lcopy 1 new [...] # duplicate layer 1 Make a new layer with a merged copy of layer 1 and 2: vpype [...] lcopy 1,2 new [...] # make new layer with merged copy of layer 1 and 2 Add a merged copy of all layers to layer 1. If layer 1 previously had content, this content is not duplicated: vpype [...] lcopy all 1 [...] """ src_lids = multiple_to_layer_ids(sources, vector_data) dest_lid = single_to_layer_id(dest, vector_data) if dest_lid in src_lids: src_lids.remove(dest_lid) lc = LineCollection() for lid in src_lids: lc.extend(vector_data[lid]) vector_data.add(lc, dest_lid) return vector_data
def variablewidth( filename, scale, pitch, pen_width, black_level, white_level, delete_white, outline_alpha, invert, ): """Documentation todo""" # load grayscale image data logging.info("variablewidth: loading image") orig_img = cv2.imread(filename, cv2.IMREAD_UNCHANGED) / 255.0 gray_img = np.average(orig_img[:, :, 0:3], axis=2) img = cv2.resize(np.array(gray_img, dtype=float), None, fx=scale, fy=scale) if invert: img = 1.0 - img logging.info( f"variablewidth: final image size: {img.shape[0]}x{img.shape[1]}") # load alpha layer if requested alpha_img = None if outline_alpha > 0: if orig_img.shape[2] == 4: alpha_img = orig_img[:, :, 3] else: logging.warning( "variablewidth: outline alpha requested but input image has no alpha layer" ) # create base line work into a MultiLineString logging.info("variablewidth: creating line work") mls_arr = [] for j in range(img.shape[0]): pixel_line = img[j] poly = translate( create_hatch_polygon( pixel_line, pitch=pitch, pen_width=pen_width, black_level=black_level, white_level=white_level, ), yoff=j * pitch, ) if not poly.is_empty: mls_arr.append(fill_polygon(poly, pen_width)) base_mls = unary_union(mls_arr) # deal with white if delete_white: logging.info("variablewidth: deleting white") white_cnt = [ c * pitch for c in measure.find_contours(img, white_level) ] white_mask = build_mask(white_cnt) base_mls = base_mls.difference(white_mask) # generate outline additional_mls = [] if alpha_img is not None: logging.info("variablewidth: outlining alpha") cnt = [ c * pitch * scale for c in measure.find_contours(alpha_img, 0.5) if len(c) >= 4 ] mask = build_mask(cnt) base_mls = base_mls.intersection(mask) # we want all boundaries, including possible holes bounds_mls = unary_union([mask.boundary] + [lr for lr in mask.interiors]).simplify( tolerance=pen_width / 2) additional_mls.append(bounds_mls) # if multiple additional_mls.extend( bounds_mls.parallel_offset((i + 1) * pen_width, side) for side in ("left", "right") for i in range(outline_alpha - 1)) lc = LineCollection(base_mls) for mls in additional_mls: lc.extend(mls) return lc
def _add_to_line_collection(geom: Any, lc: vp.LineCollection) -> None: if hasattr(geom, "exterior"): lc.append(geom.exterior) lc.extend(geom.interiors) else: lc.append(geom)