Zigzag border and fill color

浪子不回头ぞ 提交于 2020-06-09 05:31:28

问题


I have a zigzag path, I rotated it to make a zigzag border like this. Because of using Rotate, each zigzag line is in a different path attribute so I can`t fill in the color. How can I fill in the color insde these pathes using d3.js

<svg height="2100" width="4000">
  <path d="M150 250 L190 260 L230 240 L270 260 L310 240 L350 260 L390 240" stroke="red" stroke-width="2" fill = "none"/>
  <path d="M150 250 L190 260 L230 240 L270 260 L310 240 L350 260 L390 240" stroke="red" stroke-width="2" fill = "none" transform="rotate(-60 150 250)"/>
  <path d="M150 250 L190 260 L230 240 L270 260 L310 240 L350 260 L390 240" stroke="red" stroke-width="2" fill = "none" transform="rotate(60 390 240)"/>

回答1:


Here you are.

<?xml version="1.0" ?>
<svg baseProfile="full" height="558px" version="1.1" viewBox="725.88 614.7492934009083 288.24 267.65531628991823" width="600px" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink">
  <defs/>
  <path d="M 750.0,850.0 L 790.0,860.0 L 830.0,840.0 L 870.0,860.0 L 910.0,840.0 L 950.0,860.0 L 990.0,840.0 M 750.0,850.0 L 778.6602540378444,820.3589838486225 L 781.3397459621556,775.717967697245 L 818.6602540378444,751.0769515458674 L 821.3397459621556,706.4359353944899 L 858.6602540378444,681.7949192431123 L 861.3397459621556,637.1539030917348 L 872.6794919243112,676.7949192431124 L 910.0,701.4359353944899 L 912.6794919243112,746.0769515458675 L 950.0,770.7179676972451 L 952.6794919243112,815.3589838486225 L 990.0,840.0000000000002" fill="#ffaec9" stroke="red" stroke-width="1"/>
</svg>

I am not sure whether are you want to understand how to generate this image.

I use Python, and try to draw each path.

At first, you need to find the new points that is do the rotate already.

Finally, you should shift the path.

  1. curve[0] doesn't need to shift.
  2. curve1 should shift to curve[0].start
  3. then, curve[2] should shift to curve1.end

The code is as follows. (but I delete how to shift because that code is too ugly, but I believe that just a concept you can try by yourself.)

from svgpathtools import svg2paths  # you need ``pip install svgpathtools``
from svgpathtools.path import Path as CombinePath
from svgpathtools import Line, Path, wsvg
from pathlib import Path
import numpy as np
import cv2
from math import sin, cos, radians
from typing import Tuple, List

TEST_SVG = Path('test2.svg')


class COLOR:
    """
    CV2: BGR
    """
    __slots__ = ()
    BLACK = (0, 0, 0)
    AZURE = (255, 127, 0)
    WHITE = (255, 255, 255)
    BLUE = (255, 0, 0)
    GREEN = (0, 255, 0)
    RED = (0, 0, 255)
    PURPLE = (255, 0, 255)
    YELLOW = (0, 255, 255)


def scale_image(img: np.ndarray, scale_percent: int):
    width = int(img.shape[1] * scale_percent / 100)
    height = int(img.shape[0] * scale_percent / 100)
    dim = (width, height)
    return cv2.resize(img, dim, interpolation=cv2.INTER_AREA)


def show_img(show_or_not: bool, scale_percent=50):
    def wrap(function):
        def job(*arg, **options):
            np_img = function(*arg, **options)
            if show_or_not:
                cv2.namedWindow('demo', cv2.WINDOW_NORMAL)  # can resize
                cv2.imshow('demo', scale_image(np_img, scale_percent))
                cv2.waitKey(0)
            return np_img
        return job
    return wrap


def svg_translate(array: np.ndarray,
                  co, si,
                  n_si, co2,
                  x_shift, y_shift):
    matrix = np.array([[co, n_si, x_shift],
                       [si, co2, y_shift],
                       [0, 0, 1]])
    result = matrix @ array  # rotate
    return result


@show_img(show_or_not=True, scale_percent=50)
def draw_line(np_img: np.ndarray, history_list: list, line: Line,
              degree=0, x_shift=0, y_shift=0):
    degree = radians(degree)
    list_point: List[Tuple[float, float]] = []
    for point in (line.start, line.end):
        x, y = point.real, point.imag
        x, y, _ = svg_translate(np.array([[x], [y], [1]]),
                                cos(degree), sin(degree),
                                -sin(degree), cos(degree),
                                x_shift, y_shift)
        list_point.append((x, y))

    (x_start, y_start), (x_end, y_end) = pt_start, pt_end = list_point

    np_img = cv2.line(np_img, pt_start, pt_end, COLOR.BLUE, thickness=1)
    history_list.append(Line(complex(x_start, y_start), complex(x_end, y_end)))
    return np_img


def main():
    path_list, data_list = svg2paths(str(Path(TEST_SVG)))
    np_img = np.ones((2000, 2000, 3), dtype=np.uint8) * 255  # background with white color

    history_list = []
    for idx, (cur_path, dict_data) in enumerate(zip(path_list, data_list)):
        transform = dict_data.get('transform')
        degree, x_shift, y_shift = 0, 0, 0
        if transform:
            degree, x_shift, y_shift = [int(_) for _ in transform.translate(str.maketrans({'(': '', ')': ''})).replace('rotate', '').split()]

        for cur_idx, curve in enumerate(cur_path):
            np_img = draw_line(np_img, history_list, curve, degree, x_shift, y_shift) if isinstance(curve, Line) else None

    wsvg(CombinePath(*history_list), filename=f'result.svg',
         attributes=[dict(fill="#ffaec9", stroke="red", stroke_width=1)],
         openinbrowser=True  # default is False,
         )


if __name__ == '__main__':
    main()


Solution2: Potrace

Sorry, I am not good at the d3.js language. Even if I learn d3.js now, I might not be able to answer you soon...

But I can provide you another solution, you can try with the potrace.

here is the scrips (Python again but it just runs Potrace by this scrips)

from pathlib import Path
from subprocess import Popen
import cv2
import sys
import numpy as np
from typing import Tuple

POTRACE_EXE_PATH = Path(r'X:\...\potrace-1.16.win64\potrace.exe')  # download: http://potrace.sourceforge.net/#downloading


def scale_image(img: np.ndarray, scale_percent):
    width = int(img.shape[1] * scale_percent / 100)
    height = int(img.shape[0] * scale_percent / 100)
    dim = (width, height)
    return cv2.resize(img, dim, interpolation=cv2.INTER_AREA)


def morph_open_image(img: np.ndarray, kernel_shape: Tuple[int, int] = (5, 5)):
    """
    fill all the small hole, the result as much as possible to the same as the original image.

    .. note:: please remove the isolated noise first before you use this function.
    """
    return cv2.morphologyEx(img, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_RECT, kernel_shape))


def init_potrace_exe(exe_path: Path, ansi_codepage='cp950'):  # decorator

    assert exe_path.exists(), f'FileNotFoundError: exe_path:{exe_path}'

    exe_path = str(exe_path.resolve())

    def run_fun(fun):
        def job(*args, **options):
            options['potrace_exe'] = exe_path
            options['ansi_codepage'] = ansi_codepage
            fun(*args, **options)
        return job
    return run_fun


def bmp2svg(bmp: Path, output_file: Path = None, **options):
    src_path = str(bmp.resolve())
    output_file = output_file.resolve() if output_file else (bmp.parent / Path(bmp.stem + '.svg')).resolve()

    cmd_process = Popen([options.get('potrace_exe', 'potrace.exe'), "-b", "svg", "--group",
                         '--flat',  # whole image as a single path
                         '--alphamax', '0',  # corner threshold parameter (default 1), if set 0, then all the curve can see as the Line
                         src_path, '-o', str(output_file)], stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdout)
    print(' '.join([arg for arg in cmd_process.args]))
    out = cmd_process.communicate()[0]
    # print(f"result msg: {out.decode(options['ansi_codepage'])}")


def img2svg(img_path: Path):
    bmp_path = img_path
    if img_path.suffix != '.bmp' and 'Potrace does not support the PNG, so we need to create the BMP first.':
        np_img = cv2.imread(str(img_path), 0)
        np_img = morph_open_image(np_img)  # fill the hole.
        np_img = cv2.threshold(np_img, 200, 255, cv2.THRESH_BINARY)[1]
        # np_img = scale_image(np_img, 5)
        output_bmp_path = Path('.')/Path(img_path.stem + '.bmp')
        cv2.imwrite(str(output_bmp_path), np_img)  # create bmp
        bmp_path = output_bmp_path
    POTRACE_EXE(bmp2svg)(bmp_path)  # bmp to svg file  # Possible input file formats are: pnm (pbm, pgm, ppm), bmp


if __name__ == '__main__':
    POTRACE_EXE = init_potrace_exe(POTRACE_EXE_PATH)
    img2svg(Path('./zigzag.png'))

zigzag.png: (Use your SVG, and open the Microsoft Paint(mspaint.exe) (or some editor you like), and then fill the red color.)

The reason why I write this script for you is I hope it can use for more general cases. Some cases which image may ugly such that you need to modify (such as these methods) before using the Potrace to generate, and this is better than you generate it directly.

If you don't like to use the Python, it's ok for you to run the command of the following.

"X:\...\potrace.exe" -b svg --group --flat --alphamax 0 your.bmp -o output.svg

Finally, the following picture will get

<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
 width="692.000000pt" height="649.000000pt" viewBox="0 0 692.000000 649.000000"
 preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.16, written by Peter Selinger 2001-2019
</metadata>
<g transform="translate(0.000000,649.000000) scale(0.100000,-0.100000)"

fill="#ffaec9" stroke="red" stroke-width="20">
<path d="M3150 6259 l0 -12 -35 -586 -35 -586 -4 -11 -4 -10 -505 -333 -506
-333 -5 -61 -6 -62 -30 -510 -30 -511 0 -37 0 -38 -507 -336 -508 -335 -5 -5
-4 -4 -38 -611 -37 -612 -59 -60 -59 -61 -327 -338 -326 -339 6 -26 7 -27 1 0
1 0 549 -137 549 -136 529 264 528 264 30 0 30 0 533 -266 532 -266 533 266
532 266 30 0 30 0 533 -266 532 -266 553 276 552 276 0 28 0 28 -497 329 -498
328 -4 4 -4 3 -36 603 -36 602 -7 11 -8 12 -505 332 -505 333 0 35 0 35 -36
576 -35 576 -12 7 -12 7 -499 330 -499 329 -143 499 -143 498 -11 38 -11 37
-29 0 -30 0 0 -11z"/>
</g>
</svg>


来源:https://stackoverflow.com/questions/61983170/zigzag-border-and-fill-color

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!