问题
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.
- curve[0] doesn't need to shift.
- curve1 should shift to curve[0].start
- 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