Porównaj commity

...

7 Commity

Autor SHA1 Wiadomość Data
CapellanCitizen 00a06477c0 Back to specular, path transform optimization. 2024-04-15 23:45:18 -04:00
CapellanCitizen d7a9b508a1 Changed whiskers for better coverage of boundaries. More comments, yay. 2024-04-15 21:06:10 -04:00
Lex Neva 1d96c3721b tweak realistic filter 2024-04-15 14:32:21 -04:00
Lex Neva c10ce96628 make realistic preview respect thread color 2024-04-15 14:31:52 -04:00
Lex Neva 2e0597e762 work around inkscape parsing error 2024-04-15 13:12:55 -04:00
CapellanCitizen a24c77ccd9 Minor filter tweaks. 2024-04-14 15:22:21 -04:00
CapellanCitizen 8fc3296361 Experimental realistic stitch preview branch
Includes optimized/cheaper realistic stitch filter.
Logs an error message when run (Inkstitch issue?)
2024-04-14 14:40:46 -04:00
3 zmienionych plików z 70 dodań i 71 usunięć

Wyświetl plik

@ -23,6 +23,7 @@ class StitchPlanPreview(InkstitchExtension):
self.arg_parser.add_argument("-i", "--insensitive", type=Boolean, default=False, dest="insensitive")
self.arg_parser.add_argument("-c", "--visual-commands", type=Boolean, default="symbols", dest="visual_commands")
self.arg_parser.add_argument("-o", "--overwrite", type=Boolean, default=True, dest="overwrite")
self.arg_parser.add_argument("-r", "--realistic", type=Boolean, default=True, dest="realistic")
def effect(self):
# delete old stitch plan
@ -33,7 +34,7 @@ class StitchPlanPreview(InkstitchExtension):
return
svg = self.document.getroot()
realistic = False
realistic = self.options.realistic
visual_commands = self.options.visual_commands
self.metadata = self.get_inkstitch_metadata()
collapse_len = self.metadata['collapse_len_mm']

Wyświetl plik

@ -23,90 +23,81 @@ from .units import PIXELS_PER_MM, get_viewbox_transform
stitch_height = 1.216
# This vector path starts at the upper right corner of the stitch shape and
# proceeds counter-clockwise.and contains a placeholder (%s) for the stitch
# proceeds counter-clockwise and contains a placeholder (%s) for the stitch
# length.
#
# It contains two invisible "whiskers" of zero width that go above and below
# It contains four invisible "whiskers" of zero width that go outwards
# to ensure that the SVG renderer allocates a large enough canvas area when
# computing the gaussian blur steps. Otherwise, we'd have to expand the
# width and height attributes of the <filter> tag to add more buffer space.
# The width and height are specified in multiples of the bounding box
# size, It's the bounding box aligned with the global SVG canvas's axes, not
# the axes of the stitch itself. That means that having a big enough value
# computing the gaussian blur steps:
# \_____/
# (_____) (whiskers not to scale)
# / \
# This is necessary to avoid artifacting near the edges and corners that seems to be due to
# edge conditions for the feGaussianBlur, which is used to build the heightmap for
# the feDiffuseLighting node. So we need some extra buffer room around the shape.
# The whiskers let us specify a "fixed" amount of spacing around the stitch.
# Otherwise, we'd have to expand the width and height attributes of the <filter>
# tag to add more buffer space. The filter's width and height are specified in multiples of
# the bounding box size, It's the bounding box aligned with the global SVG canvas's axes,
# not the axes of the stitch itself. That means that having a big enough value
# to add enough padding on the long sides of the stitch would waste a ton
# of space on the short sides and significantly slow down rendering.
stitch_path = "M0,0c0.4,0,0.4,0.3,0.4,0.6c0,0.3,-0.1,0.6,-0.4,0.6v0.2,-0.2h-%sc-0.4,0,-0.4,-0.3,-0.4,-0.6c0,-0.3,0.1,-0.6,0.4,-0.6v-0.2,0.2z"
# This filter makes the above stitch path look like a real stitch with lighting.
# The specific extent of the whiskers (0.45 parallel to the stitch, 0.1 perpendicular)
# was found by experimentation. It seems to work without artifacting.
stitch_path = (
"M0,0" # Start point
"l0.45,-0.1,-0.45,0.1" # Bottom-right whisker
"c0.4,0,0.4,0.3,0.4,0.6c0,0.3,-0.1,0.6,-0.4,0.6" # Right endcap
"l0.45,0.1,-0.45,-0.1" # Top-right whisker
"h-%s" # Stitch length
"l-0.45,0.1,0.45,-0.1" # Top-left whisker
"c-0.4,0,-0.4,-0.3,-0.4,-0.6c0,-0.3,0.1,-0.6,0.4,-0.6" # Left endcap
"l-0.45,-0.1,0.45,0.1" # Bottom-left whisker
"z") # return to start
# The filter needs the xmlns:inkscape declaration, or Inkscape will display a parse error
# "Namespace prefix inkscape for auto-region on filter is not defined"
# Even when the document itself has the namespace, go figure.
realistic_filter = """
<filter
style="color-interpolation-filters:sRGB"
id="realistic-stitch-filter"
x="-0.1"
width="1.2"
y="-0.1"
height="1.2">
x="0"
width="1"
y="0"
height="1"
inkscape:auto-region="false"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape">
<feGaussianBlur
stdDeviation="1.5"
edgeMode="none"
stdDeviation="0.9"
id="feGaussianBlur1542-6"
in="SourceAlpha" />
<feComponentTransfer
id="feComponentTransfer1544-7"
result="result1">
<feFuncR
id="feFuncR1546-5"
type="identity" />
<feFuncG
id="feFuncG1548-3"
type="identity" />
<feFuncB
id="feFuncB1550-5"
type="identity"
slope="4.5300000000000002" />
<feFuncA
id="feFuncA1552-6"
type="gamma"
slope="0.14999999999999999"
intercept="0"
amplitude="3.1299999999999999"
offset="-0.33000000000000002" />
</feComponentTransfer>
<feComposite
in2="SourceAlpha"
id="feComposite1558-2"
operator="in" />
<feGaussianBlur
stdDeviation="0.089999999999999997"
id="feGaussianBlur1969" />
<feMorphology
id="feMorphology1971"
operator="dilate"
radius="0.10000000000000001" />
<feSpecularLighting
id="feSpecularLighting1973"
result="result2"
specularConstant="0.70899999"
surfaceScale="30">
<fePointLight
id="fePointLight1975"
z="10" />
surfaceScale="1.5"
specularConstant="0.78"
specularExponent="2.5">
<feDistantLight
id="feDistantLight1975"
azimuth="-125"
elevation="20" />
</feSpecularLighting>
<feGaussianBlur
stdDeviation="0.040000000000000001"
id="feGaussianBlur1979" />
<feComposite
in2="SourceGraphic"
id="feComposite1977"
operator="arithmetic"
k2="1"
k3="1"
result="result3"
k1="0"
k4="0" />
<feComposite
in2="SourceAlpha"
id="feComposite1981"
operator="in" />
operator="atop" />
<feComposite
in2="SourceGraphic"
id="feComposite1982"
operator="arithmetic"
k2="0.8"
k3="1.2"
result="result3"
k1="0"
k4="0" />
</filter>
"""
@ -124,17 +115,18 @@ def realistic_stitch(start, end):
stitch_length = max(0, stitch_length - 0.2 * PIXELS_PER_MM)
# create the path by filling in the length in the template
path = inkex.Path(stitch_path % stitch_length).to_arrays()
# rotate the path to match the stitch
rotation_center_x = -stitch_length / 2.0
rotation_center_y = stitch_height / 2.0
path = inkex.Path(path).rotate(stitch_angle, (rotation_center_x, rotation_center_y))
transform = (
inkex.Transform()
.add_translate(stitch_center.x - rotation_center_x, stitch_center.y - rotation_center_y)
.add_rotate(stitch_angle, (rotation_center_x, rotation_center_y))
)
# move the path to the location of the stitch
path = inkex.Path(path).translate(stitch_center.x - rotation_center_x, stitch_center.y - rotation_center_y)
# create the path by filling in the length in the template, and transforming it as above
path = inkex.Path(stitch_path % stitch_length).transform(transform, True)
return str(path)
@ -250,5 +242,10 @@ def render_stitch_plan(svg, stitch_plan, realistic=False, visual_commands=True):
color_block_to_paths(color_block, svg, group, visual_commands)
if realistic:
# Remove filter from defs, if any
filter: inkex.BaseElement = svg.defs.findone("//*[@id='realistic-stitch-filter']")
if filter is not None:
svg.defs.remove(filter)
filter_document = inkex.load_svg(realistic_filter)
svg.defs.append(filter_document.getroot())

Wyświetl plik

@ -24,6 +24,7 @@
<param name="insensitive" type="boolean" gui-text="Lock"
gui-description="Make stitch plan insensitive to mouse interactions">false</param>
<param name="visual-commands" type="boolean" gui-text="Display command symbols">false</param>
<param name="realistic" type="boolean" gui-text="Realistic output">false</param>
<spacer />
<separator />
<spacer />