2025-07-01
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
'''
|
||||
Copyright (C) 2023 CG Cookie
|
||||
http://cgcookie.com
|
||||
hello@cgcookie.com
|
||||
|
||||
Created by Jonathan Denning, Jonathan Williamson
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
'''
|
||||
|
||||
import bpy
|
||||
import math
|
||||
from mathutils import Vector, Matrix
|
||||
from mathutils.geometry import intersect_line_line_2d
|
||||
from ...addon_common.common.debug import dprint
|
||||
from ...addon_common.common.maths import Point,Point2D,Vec2D,Vec, Normal, clamp
|
||||
from ...addon_common.common.bezier import CubicBezierSpline, CubicBezier
|
||||
from ...addon_common.common.utils import iter_pairs
|
||||
|
||||
|
||||
def process_stroke_filter(stroke, min_distance=1.0, max_distance=2.0):
|
||||
''' filter stroke to pts that are at least min_distance apart '''
|
||||
nstroke = stroke[:1]
|
||||
for p in stroke[1:]:
|
||||
v = p - nstroke[-1]
|
||||
l = v.length
|
||||
if l < min_distance: continue
|
||||
d = v / l
|
||||
while l > 0:
|
||||
q = nstroke[-1] + d * min(l, max_distance)
|
||||
nstroke.append(q)
|
||||
l -= max_distance
|
||||
return nstroke
|
||||
|
||||
def process_stroke_source(stroke, raycast, Point_to_Point2D=None, is_point_on_mirrored_side=None, mirror_point=None, clamp_point_to_symmetry=None):
|
||||
''' filter out pts that don't hit source on non-mirrored side '''
|
||||
pts = [(pt, raycast(pt)[0]) for pt in stroke]
|
||||
pts = [(pt, p3d) for (pt, p3d) in pts if p3d]
|
||||
if Point_to_Point2D and mirror_point:
|
||||
pts_ = [Point_to_Point2D(mirror_point(p3d)) for (_, p3d) in pts]
|
||||
pts = [(pt, raycast(pt)[0]) for pt in pts_]
|
||||
pts = [(pt, p3d) for (pt, p3d) in pts if p3d]
|
||||
if Point_to_Point2D and clamp_point_to_symmetry:
|
||||
pts_ = [Point_to_Point2D(clamp_point_to_symmetry(p3d)) for (_, p3d) in pts]
|
||||
pts = [(pt, raycast(pt)[0]) for pt in pts_]
|
||||
pts = [(pt, p3d) for (pt, p3d) in pts if p3d]
|
||||
if is_point_on_mirrored_side:
|
||||
pts = [(pt, p3d) for (pt, p3d) in pts if not is_point_on_mirrored_side(p3d)]
|
||||
return [pt for (pt, _) in pts]
|
||||
|
||||
def find_edge_cycles(edges):
|
||||
edges = set(edges)
|
||||
verts = {v: set() for e in edges for v in e.verts}
|
||||
for e in edges:
|
||||
for v in e.verts:
|
||||
verts[v].add(e)
|
||||
in_cycle = set()
|
||||
for vstart in verts:
|
||||
if vstart in in_cycle: continue
|
||||
for estart in vstart.link_edges:
|
||||
if estart not in edges: continue
|
||||
if estart in in_cycle: continue
|
||||
q = [(estart, vstart, None)]
|
||||
found = None
|
||||
trace = {}
|
||||
while q:
|
||||
ec, vc, ep = q.pop(0)
|
||||
if ec in trace: continue
|
||||
trace[ec] = (vc, ep)
|
||||
vn = ec.other_vert(vc)
|
||||
if vn == vstart:
|
||||
found = ec
|
||||
break
|
||||
q += [(en, vn, ec) for en in vn.link_edges if en in edges]
|
||||
if not found: continue
|
||||
l = [found]
|
||||
in_cycle.add(found)
|
||||
while True:
|
||||
vn, ep = trace[l[-1]]
|
||||
in_cycle.add(vn)
|
||||
in_cycle.add(ep)
|
||||
if vn == vstart: break
|
||||
l.append(ep)
|
||||
yield l
|
||||
|
||||
def find_edge_strips(edges):
|
||||
''' find edge strips '''
|
||||
edges = set(edges)
|
||||
verts = {v: set() for e in edges for v in e.verts}
|
||||
for e in edges:
|
||||
for v in e.verts:
|
||||
verts[v].add(e)
|
||||
ends = [v for v in verts if len(verts[v]) == 1]
|
||||
def get_edge_sequence(v0, v1):
|
||||
trace = {}
|
||||
q = [(None, v0)]
|
||||
while q:
|
||||
vf,vt = q.pop(0)
|
||||
if vt in trace: continue
|
||||
trace[vt] = vf
|
||||
if vt == v1: break
|
||||
for e in verts[vt]:
|
||||
q.append((vt, e.other_vert(vt)))
|
||||
if v1 not in trace: return []
|
||||
l = []
|
||||
while v1 is not None:
|
||||
l.append(v1)
|
||||
v1 = trace[v1]
|
||||
l.reverse()
|
||||
return [v0.shared_edge(v1) for (v0, v1) in iter_pairs(l, wrap=False)]
|
||||
for i0 in range(len(ends)):
|
||||
for i1 in range(i0+1,len(ends)):
|
||||
l = get_edge_sequence(ends[i0], ends[i1])
|
||||
if l: yield l
|
||||
|
||||
def get_strip_verts(edge_strip):
|
||||
l = len(edge_strip)
|
||||
if l == 0: return []
|
||||
if l == 1:
|
||||
e = edge_strip[0]
|
||||
return list(e.verts) if e.is_valid else []
|
||||
vs = []
|
||||
for e0, e1 in iter_pairs(edge_strip, wrap=False):
|
||||
vs.append(e0.shared_vert(e1))
|
||||
vs = [edge_strip[0].other_vert(vs[0])] + vs + [edge_strip[-1].other_vert(vs[-1])]
|
||||
return vs
|
||||
|
||||
|
||||
def restroke(stroke, percentages):
|
||||
lens = [(s0 - s1).length for (s0, s1) in iter_pairs(stroke, wrap=False)]
|
||||
total_len = sum(lens)
|
||||
stops = [max(0, min(1, p)) * total_len for p in percentages]
|
||||
dist = 0
|
||||
istroke = 0
|
||||
istop = 0
|
||||
nstroke = []
|
||||
while istroke + 1 < len(stroke) and istop < len(stops):
|
||||
if lens[istroke] <= 0:
|
||||
istroke += 1
|
||||
continue
|
||||
t = (stops[istop] - dist) / lens[istroke]
|
||||
if t < 0:
|
||||
istop += 1
|
||||
elif t > 1.000001:
|
||||
dist += lens[istroke]
|
||||
istroke += 1
|
||||
else:
|
||||
s0, s1 = stroke[istroke], stroke[istroke + 1]
|
||||
nstroke.append(s0 + (s1 - s0) * t)
|
||||
istop += 1
|
||||
return nstroke
|
||||
|
||||
def walk_to_corner(from_vert, to_edges):
|
||||
to_verts = {v for e in to_edges for v in e.verts}
|
||||
edges = [
|
||||
(e, from_vert, None)
|
||||
for e in from_vert.link_edges
|
||||
if not e.is_manifold and e.is_valid
|
||||
]
|
||||
touched = {}
|
||||
found = None
|
||||
while edges:
|
||||
ec, v0, ep = edges.pop(0)
|
||||
if ec in touched: continue
|
||||
touched[ec] = (v0, ep)
|
||||
v1 = ec.other_vert(v0)
|
||||
if v1 in to_verts:
|
||||
found = ec
|
||||
break
|
||||
nedges = [
|
||||
(en, v1, ec)
|
||||
for en in v1.link_edges
|
||||
if en != ec and not en.is_manifold and en.is_valid
|
||||
]
|
||||
edges += nedges
|
||||
if not found: return None
|
||||
# walk back
|
||||
walk = [found]
|
||||
while True:
|
||||
ec = walk[-1]
|
||||
v0, ep = touched[ec]
|
||||
if v0 == from_vert:
|
||||
break
|
||||
walk.append(ep)
|
||||
return walk
|
||||
Reference in New Issue
Block a user