锯齿形边框和填充色

时间:2020-05-24 08:01:26

标签: d3.js svg rotation

我有一个曲折的路径,我旋转它来制作一个曲折的边框。由于使用了旋转,因此每条锯齿形线都具有不同的路径属性,因此我无法填写颜色。如何使用d3.js填充这些路径的颜色

This is the look i`m trying to replicate

<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 个答案:

答案 0 :(得分:3)

您在这里。

<?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>


我不确定您是否要了解如何生成此图像。

我使用Python,并尝试绘制每个路径。

首先,您需要找到已经旋转的新点。

最后,您应该移动路径。

  1. 曲线[0]不需要移动。
  2. 曲线1应移至曲线[0]。开始
  3. 然后,曲线[2]应该移至曲线1。结束

代码如下。 (但我删除了转换方法,因为该代码太丑陋了,但我相信只是一个可以自己尝试的概念。)

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()


解决方案2:Potrace

对不起,我不擅长d3.js语言。即使我现在学习d3.js,我也可能很快无法回答您...

但是我可以为您提供另一种解决方案,您可以尝试使用potrace

这是脚本(再次是Python,但是它只是通过该脚本运行Potrace)

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 :(使用SVG,然后打开Microsoft Paint(mspaint.exe)(或您喜欢的某些编辑器),然后填充红色。) zigzag.png

我之所以为您编写此脚本,是希望它可以用于更一般的情况。 在某些情况下,该图像可能很难看,因此您需要在使用Potrace生成图像之前进行修改(例如这些methods), 比直接生成它更好。

如果您不喜欢使用Python,则可以运行以下命令。

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

最后,将得到以下图片

<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>