Add commented reference helpers to kisscut_gui.py

This commit is contained in:
2026-01-11 15:07:44 +01:00
parent 9cfd431228
commit c5f5c159ba

View File

@@ -244,6 +244,187 @@ def export_mask_as_bezier_svg(mask, path, rdp_epsilon_mm=0.2, spline_smooth=8.0,
f.write(svg)
return smooth_poly
# --- Commented reference helpers from old code/ for future experiments ---
# These stay commented so the current workflow remains unchanged, but the
# snippets can be copied back in without keeping the old files around.
# def fit_spline_polyline(points, num_out=200, smooth=5.0):
# """SciPy spline fit used by an earlier GUI version (periodic curve)."""
# x, y = points[:, 0], points[:, 1]
# tck, _ = splprep([x, y], s=smooth, per=True)
# unew = np.linspace(0, 1.0, num_out)
# out = splev(unew, tck)
# return np.vstack([out[0], out[1]]).T
# def catmull_rom_to_beziers(points, closed=True, max_handle_frac=0.5):
# """CatmullRom to cubic Bézier conversion with handle clamping."""
# points = np.asarray(points)
# n = len(points)
# beziers = []
# if closed:
# pts = np.vstack([points[-1], points, points[:2]])
# rng = range(1, n + 1)
# else:
# pts = np.vstack([points[0], points, points[-1]])
# rng = range(1, n)
# for i in rng:
# p0, p1, p2, p3 = pts[i - 1], pts[i], pts[i + 1], pts[i + 2]
# bp0 = p1
# v1 = (p2 - p0) / 6.0
# v2 = (p3 - p1) / 6.0
# seg1 = np.linalg.norm(p2 - p1)
# seg0 = np.linalg.norm(p1 - p0)
# seg2 = np.linalg.norm(p3 - p2)
# max_len1 = max_handle_frac * min(seg1, seg0)
# max_len2 = max_handle_frac * min(seg2, seg1)
# v1_len = np.linalg.norm(v1)
# v2_len = np.linalg.norm(v2)
# if v1_len > max_len1 and v1_len > 0:
# v1 = v1 * (max_len1 / v1_len)
# if v2_len > max_len2 and v2_len > 0:
# v2 = v2 * (max_len2 / v2_len)
# bp1 = p1 + v1
# bp2 = p2 - v2
# bp3 = p2
# beziers.append([bp0, bp1, bp2, bp3])
# return beziers
# def export_polyline_svg(points, path, width, height):
# """Quick SVG export of a polyline outline for debugging."""
# d = "M " + " L ".join(f"{x:.2f},{y:.2f}" for x, y in points) + " Z"
# svg = f'<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">\\n'
# svg += f'<path d="{d}" fill="none" stroke="red" stroke-width="1"/>\\n</svg>'
# with open(path, "w") as f:
# f.write(svg)
# def sample_polyline(points, N=1000):
# """Evenly sample points along a polyline."""
# points = np.asarray(points)
# dists = np.sqrt(np.sum(np.diff(points, axis=0)**2, axis=1))
# cumlen = np.concatenate([[0], np.cumsum(dists)])
# total_len = cumlen[-1]
# interp_locs = np.linspace(0, total_len, N)
# interp_points = []
# for loc in interp_locs:
# idx = np.searchsorted(cumlen, loc) - 1
# idx = min(idx, len(points) - 2)
# seglen = cumlen[idx+1] - cumlen[idx]
# t = (loc - cumlen[idx]) / seglen if seglen > 0 else 0
# interp = (1 - t) * points[idx] + t * points[idx+1]
# interp_points.append(interp)
# return np.array(interp_points)
# def _legacy_bezier_q(ctrl_poly, t):
# """Cubic Bézier evaluation reused by sampling helpers."""
# mt = 1 - t
# return (
# mt**3 * ctrl_poly[0][0] + 3 * mt**2 * t * ctrl_poly[1][0] + 3 * mt * t**2 * ctrl_poly[2][0] + t**3 * ctrl_poly[3][0],
# mt**3 * ctrl_poly[0][1] + 3 * mt**2 * t * ctrl_poly[1][1] + 3 * mt * t**2 * ctrl_poly[2][1] + t**3 * ctrl_poly[3][1]
# )
# def sample_bezier_path(beziers, N=1000):
# """Sample a list of cubic Béziers into N evenly spaced points."""
# samples = []
# segs = len(beziers)
# pts_per_seg = max(2, N // segs)
# for bez in beziers:
# for i in range(pts_per_seg):
# t = i / (pts_per_seg - 1)
# p = _legacy_bezier_q(bez, t)
# samples.append(np.array(p))
# return np.array(samples[:N])
# def find_deviations(poly_points, bezier_points, threshold=5.0):
# """Locate indexes where Bézier samples deviate from the polyline."""
# dists = np.linalg.norm(poly_points - bezier_points, axis=1)
# deviations = np.where(dists > threshold)[0]
# return deviations, dists
# def blend_bezier_with_polyline(beziers, poly_points, bezier_points, deviations, alpha=0.7):
# """Pull Bézier handles toward polyline points where error is large."""
# N = len(bezier_points)
# segs = len(beziers)
# pts_per_seg = max(2, N // segs)
# for idx in deviations:
# seg_idx = idx // pts_per_seg
# if seg_idx >= len(beziers):
# continue
# p_poly = poly_points[idx]
# for c_idx in (1, 2):
# c = np.array(beziers[seg_idx][c_idx])
# beziers[seg_idx][c_idx] = tuple(alpha * c + (1 - alpha) * p_poly)
# return beziers
# Potrace + Skia offset pipeline (old kisscut_wrap.py). Requires potrace CLI,
# lxml, svg.path, and skia-python; kept for reference if a true stroke offset
# is preferred over the contour-based approach here.
# import subprocess, tempfile
# from lxml import etree
# from svg.path import parse_path, CubicBezier, Line, QuadraticBezier, Arc
# import skia
#
# def png_to_pnm(input_png, pnm_path):
# img = Image.open(input_png).convert("L")
# img.save(pnm_path, format="PPM")
#
# def pnm_to_base_svg(pnm_path, svg_path):
# subprocess.run([
# "potrace", "-s", "-o", svg_path, "-b", "svg",
# "--alphamax", "0.1", "--turdsize", "1", pnm_path
# ], check=True)
#
# def offset_svg_paths(input_svg, output_svg, offset_pt=9.0):
# parser = etree.XMLParser(remove_blank_text=True)
# tree = etree.parse(input_svg, parser)
# root = tree.getroot()
# ns = {"svg": "http://www.w3.org/2000/svg"}
# svg_ns = "http://www.w3.org/2000/svg"
# new_root = etree.Element("{%s}svg" % svg_ns,
# nsmap={None: svg_ns},
# width=root.get("width"),
# height=root.get("height"),
# viewBox=root.get("viewBox") or f"0 0 {root.get('width')} {root.get('height')}")
# for path_el in root.xpath("//svg:path", namespaces=ns):
# d = path_el.get("d")
# path = parse_path(d)
# sk_path = skia.Path()
# start = path[0].start
# sk_path.moveTo(start.real, -start.imag)
# for seg in path:
# if isinstance(seg, CubicBezier):
# sk_path.cubicTo(
# seg.control1.real, -seg.control1.imag,
# seg.control2.real, -seg.control2.imag,
# seg.end.real, -seg.end.imag
# )
# elif isinstance(seg, Line):
# sk_path.lineTo(seg.end.real, -seg.end.imag)
# elif isinstance(seg, QuadraticBezier):
# c1 = seg.start + 2/3*(seg.control - seg.start)
# c2 = seg.end + 2/3*(seg.control - seg.end)
# sk_path.cubicTo(
# c1.real, -c1.imag,
# c2.real, -c2.imag,
# seg.end.real, -seg.end.imag
# )
# elif isinstance(seg, Arc):
# sk_path.lineTo(seg.end.real, -seg.end.imag)
# stroke = skia.StrokeRec()
# stroke.setStrokeStyle(offset_pt)
# sk_offset = sk_path.strokeAndConvertToPath(stroke)
# d2 = sk_offset.toSVGString()
# etree.SubElement(new_root, "path",
# d=d2,
# fill="none",
# stroke="red",
# **{"stroke-width": "1"})
# etree.ElementTree(new_root).write(
# output_svg,
# pretty_print=True,
# xml_declaration=True,
# encoding="utf-8"
# )
# --- Preview Widget (just display, auto-margin alignment) ---
class PreviewWidget(QLabel):
def __init__(self, *args, **kwargs):
@@ -514,4 +695,4 @@ if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())
sys.exit(app.exec())