From 8b3937cd282bba0411492ec6b44e06c001b9bc12 Mon Sep 17 00:00:00 2001 From: Padlex Date: Sat, 2 May 2020 00:11:04 +0200 Subject: [PATCH] Remove unused fuctions identified by vulture --- laser.py | 1652 ------------------------------------------------------ 1 file changed, 1652 deletions(-) diff --git a/laser.py b/laser.py index 3446542..9c7ced3 100644 --- a/laser.py +++ b/laser.py @@ -70,9 +70,6 @@ def bezierslopeatt(((bx0, by0), (bx1, by1), (bx2, by2), (bx3, by3)), t): bezmisc.bezierslopeatt = bezierslopeatt -def ireplace(self, old, new, count=0): - pattern = re.compile(re.escape(old), re.I) - return re.sub(pattern, new, self, count) ################################################################################ @@ -195,148 +192,18 @@ styles = { ### Cubic Super Path additional functions ################################################################################ -def csp_simple_bound(csp): - minx, miny, maxx, maxy = None, None, None, None - for subpath in csp: - for sp in subpath: - for p in sp: - minx = min(minx, p[0]) if minx != None else p[0] - miny = min(miny, p[1]) if miny != None else p[1] - maxx = max(maxx, p[0]) if maxx != None else p[0] - maxy = max(maxy, p[1]) if maxy != None else p[1] - return minx, miny, maxx, maxy def csp_segment_to_bez(sp1, sp2): return sp1[1:] + sp2[:2] -def csp_to_point_distance(csp, p, dist_bounds=[0, 1e100], tolerance=.01): - min_dist = [1e100, 0, 0, 0] - for j in range(len(csp)): - for i in range(1, len(csp[j])): - d = csp_seg_to_point_distance(csp[j][i - 1], csp[j][i], p, sample_points=5, tolerance=.01) - if d[0] < dist_bounds[0]: - # draw_pointer( list(csp_at_t(subpath[dist[2]-1],subpath[dist[2]],dist[3])) - # +list(csp_at_t(csp[dist[4]][dist[5]-1],csp[dist[4]][dist[5]],dist[6])),"red","line", comment = math.sqrt(dist[0])) - return [d[0], j, i, d[1]] - else: - if d[0] < min_dist[0]: min_dist = [d[0], j, i, d[1]] - return min_dist -def csp_seg_to_point_distance(sp1, sp2, p, sample_points=5, tolerance=.01): - ax, ay, bx, by, cx, cy, dx, dy = csp_parameterize(sp1, sp2) - dx, dy = dx - p[0], dy - p[1] - if sample_points < 2: sample_points = 2 - d = min([(p[0] - sp1[1][0]) ** 2 + (p[1] - sp1[1][1]) ** 2, 0.], - [(p[0] - sp2[1][0]) ** 2 + (p[1] - sp2[1][1]) ** 2, 1.]) - for k in range(sample_points): - t = float(k) / (sample_points - 1) - i = 0 - while i == 0 or abs(f) > 0.000001 and i < 20: - t2, t3 = t ** 2, t ** 3 - f = (ax * t3 + bx * t2 + cx * t + dx) * (3 * ax * t2 + 2 * bx * t + cx) + ( - ay * t3 + by * t2 + cy * t + dy) * (3 * ay * t2 + 2 * by * t + cy) - df = (6 * ax * t + 2 * bx) * (ax * t3 + bx * t2 + cx * t + dx) + (3 * ax * t2 + 2 * bx * t + cx) ** 2 + ( - 6 * ay * t + 2 * by) * (ay * t3 + by * t2 + cy * t + dy) + (3 * ay * t2 + 2 * by * t + cy) ** 2 - if df != 0: - t = t - f / df - else: - break - i += 1 - if 0 <= t <= 1: - p1 = csp_at_t(sp1, sp2, t) - d1 = (p1[0] - p[0]) ** 2 + (p1[1] - p[1]) ** 2 - if d1 < d[0]: - d = [d1, t] - return d -def csp_seg_to_csp_seg_distance(sp1, sp2, sp3, sp4, dist_bounds=[0, 1e100], sample_points=5, tolerance=.01): - # check the ending points first - dist = csp_seg_to_point_distance(sp1, sp2, sp3[1], sample_points, tolerance) - dist += [0.] - if dist[0] <= dist_bounds[0]: return dist - d = csp_seg_to_point_distance(sp1, sp2, sp4[1], sample_points, tolerance) - if d[0] < dist[0]: - dist = d + [1.] - if dist[0] <= dist_bounds[0]: return dist - d = csp_seg_to_point_distance(sp3, sp4, sp1[1], sample_points, tolerance) - if d[0] < dist[0]: - dist = [d[0], 0., d[1]] - if dist[0] <= dist_bounds[0]: return dist - d = csp_seg_to_point_distance(sp3, sp4, sp2[1], sample_points, tolerance) - if d[0] < dist[0]: - dist = [d[0], 1., d[1]] - if dist[0] <= dist_bounds[0]: return dist - sample_points -= 2 - if sample_points < 1: sample_points = 1 - ax1, ay1, bx1, by1, cx1, cy1, dx1, dy1 = csp_parameterize(sp1, sp2) - ax2, ay2, bx2, by2, cx2, cy2, dx2, dy2 = csp_parameterize(sp3, sp4) - # try to find closes points using Newtons method - for k in range(sample_points): - for j in range(sample_points): - t1, t2 = float(k + 1) / (sample_points + 1), float(j) / (sample_points + 1) - t12, t13, t22, t23 = t1 * t1, t1 * t1 * t1, t2 * t2, t2 * t2 * t2 - i = 0 - F1, F2, F = [0, 0], [[0, 0], [0, 0]], 1e100 - x, y = ax1 * t13 + bx1 * t12 + cx1 * t1 + dx1 - ( - ax2 * t23 + bx2 * t22 + cx2 * t2 + dx2), ay1 * t13 + by1 * t12 + cy1 * t1 + dy1 - ( - ay2 * t23 + by2 * t22 + cy2 * t2 + dy2) - while i < 2 or abs(F - Flast) > tolerance and i < 30: - # draw_pointer(csp_at_t(sp1,sp2,t1)) - f1x = 3 * ax1 * t12 + 2 * bx1 * t1 + cx1 - f1y = 3 * ay1 * t12 + 2 * by1 * t1 + cy1 - f2x = 3 * ax2 * t22 + 2 * bx2 * t2 + cx2 - f2y = 3 * ay2 * t22 + 2 * by2 * t2 + cy2 - F1[0] = 2 * f1x * x + 2 * f1y * y - F1[1] = -2 * f2x * x - 2 * f2y * y - F2[0][0] = 2 * (6 * ax1 * t1 + 2 * bx1) * x + 2 * f1x * f1x + 2 * ( - 6 * ay1 * t1 + 2 * by1) * y + 2 * f1y * f1y - F2[0][1] = -2 * f1x * f2x - 2 * f1y * f2y - F2[1][0] = -2 * f2x * f1x - 2 * f2y * f1y - F2[1][1] = -2 * (6 * ax2 * t2 + 2 * bx2) * x + 2 * f2x * f2x - 2 * ( - 6 * ay2 * t2 + 2 * by2) * y + 2 * f2y * f2y - F2 = inv_2x2(F2) - if F2 != None: - t1 -= (F2[0][0] * F1[0] + F2[0][1] * F1[1]) - t2 -= (F2[1][0] * F1[0] + F2[1][1] * F1[1]) - t12, t13, t22, t23 = t1 * t1, t1 * t1 * t1, t2 * t2, t2 * t2 * t2 - x, y = ax1 * t13 + bx1 * t12 + cx1 * t1 + dx1 - ( - ax2 * t23 + bx2 * t22 + cx2 * t2 + dx2), ay1 * t13 + by1 * t12 + cy1 * t1 + dy1 - ( - ay2 * t23 + by2 * t22 + cy2 * t2 + dy2) - Flast = F - F = x * x + y * y - else: - break - i += 1 - if F < dist[0] and 0 <= t1 <= 1 and 0 <= t2 <= 1: - dist = [F, t1, t2] - if dist[0] <= dist_bounds[0]: - return dist - return dist -def csp_to_csp_distance(csp1, csp2, dist_bounds=[0, 1e100], tolerance=.01): - dist = [1e100, 0, 0, 0, 0, 0, 0] - for i1 in range(len(csp1)): - for j1 in range(1, len(csp1[i1])): - for i2 in range(len(csp2)): - for j2 in range(1, len(csp2[i2])): - d = csp_seg_bound_to_csp_seg_bound_max_min_distance(csp1[i1][j1 - 1], csp1[i1][j1], - csp2[i2][j2 - 1], csp2[i2][j2]) - if d[0] >= dist_bounds[1]: continue - if d[1] < dist_bounds[0]: return [d[1], i1, j1, 1, i2, j2, 1] - d = csp_seg_to_csp_seg_distance(csp1[i1][j1 - 1], csp1[i1][j1], csp2[i2][j2 - 1], csp2[i2][j2], - dist_bounds, tolerance=tolerance) - if d[0] < dist[0]: - dist = [d[0], i1, j1, d[1], i2, j2, d[2]] - if dist[0] <= dist_bounds[0]: - return dist - if dist[0] >= dist_bounds[1]: - return dist - return dist # draw_pointer( list(csp_at_t(csp1[dist[1]][dist[2]-1],csp1[dist[1]][dist[2]],dist[3])) @@ -360,39 +227,6 @@ def csp_split(sp1, sp2, t=.5): return [sp1[0], sp1[1], [x12, y12]], [[x1223, y1223], [x, y], [x2334, y2334]], [[x34, y34], sp2[1], sp2[2]] -def csp_true_bounds(csp): - # Finds minx,miny,maxx,maxy of the csp and return their (x,y,i,j,t) - minx = [float("inf"), 0, 0, 0] - maxx = [float("-inf"), 0, 0, 0] - miny = [float("inf"), 0, 0, 0] - maxy = [float("-inf"), 0, 0, 0] - for i in range(len(csp)): - for j in range(1, len(csp[i])): - ax, ay, bx, by, cx, cy, x0, y0 = bezmisc.bezierparameterize( - (csp[i][j - 1][1], csp[i][j - 1][2], csp[i][j][0], csp[i][j][1])) - roots = cubic_solver(0, 3 * ax, 2 * bx, cx) + [0, 1] - for root in roots: - if type(root) is complex and abs(root.imag) < 1e-10: - root = root.real - if type(root) is not complex and 0 <= root <= 1: - y = ay * (root ** 3) + by * (root ** 2) + cy * root + y0 - x = ax * (root ** 3) + bx * (root ** 2) + cx * root + x0 - maxx = max([x, y, i, j, root], maxx) - minx = min([x, y, i, j, root], minx) - - roots = cubic_solver(0, 3 * ay, 2 * by, cy) + [0, 1] - for root in roots: - if type(root) is complex and root.imag == 0: - root = root.real - if type(root) is not complex and 0 <= root <= 1: - y = ay * (root ** 3) + by * (root ** 2) + cy * root + y0 - x = ax * (root ** 3) + bx * (root ** 2) + cx * root + x0 - maxy = max([y, x, i, j, root], maxy) - miny = min([y, x, i, j, root], miny) - maxy[0], maxy[1] = maxy[1], maxy[0] - miny[0], miny[1] = miny[1], miny[0] - - return minx, miny, maxx, maxy ############################################################################ @@ -402,158 +236,12 @@ def csp_true_bounds(csp): ### super path. Results are [ta,tb], or [ta0, ta1, tb0, tb1, "Overlap"] ### where ta, tb are values of t for the intersection point. ############################################################################ -def csp_segments_intersection(sp1, sp2, sp3, sp4): - a, b = csp_segment_to_bez(sp1, sp2), csp_segment_to_bez(sp3, sp4) - - def polish_intersection(a, b, ta, tb, tolerance=intersection_tolerance): - ax, ay, bx, by, cx, cy, dx, dy = bezmisc.bezierparameterize(a) - ax1, ay1, bx1, by1, cx1, cy1, dx1, dy1 = bezmisc.bezierparameterize(b) - i = 0 - F, F1 = [.0, .0], [[.0, .0], [.0, .0]] - while i == 0 or (abs(F[0]) ** 2 + abs(F[1]) ** 2 > tolerance and i < 10): - ta3, ta2, tb3, tb2 = ta ** 3, ta ** 2, tb ** 3, tb ** 2 - F[0] = ax * ta3 + bx * ta2 + cx * ta + dx - ax1 * tb3 - bx1 * tb2 - cx1 * tb - dx1 - F[1] = ay * ta3 + by * ta2 + cy * ta + dy - ay1 * tb3 - by1 * tb2 - cy1 * tb - dy1 - F1[0][0] = 3 * ax * ta2 + 2 * bx * ta + cx - F1[0][1] = -3 * ax1 * tb2 - 2 * bx1 * tb - cx1 - F1[1][0] = 3 * ay * ta2 + 2 * by * ta + cy - F1[1][1] = -3 * ay1 * tb2 - 2 * by1 * tb - cy1 - det = F1[0][0] * F1[1][1] - F1[0][1] * F1[1][0] - if det != 0: - F1 = [[F1[1][1] / det, -F1[0][1] / det], [-F1[1][0] / det, F1[0][0] / det]] - ta = ta - (F1[0][0] * F[0] + F1[0][1] * F[1]) - tb = tb - (F1[1][0] * F[0] + F1[1][1] * F[1]) - else: - break - i += 1 - - return ta, tb - def recursion(a, b, ta0, ta1, tb0, tb1, depth_a, depth_b): - global bezier_intersection_recursive_result - if a == b: - bezier_intersection_recursive_result += [[ta0, tb0, ta1, tb1, "Overlap"]] - return - tam, tbm = (ta0 + ta1) / 2, (tb0 + tb1) / 2 - if depth_a > 0 and depth_b > 0: - a1, a2 = bez_split(a, 0.5) - b1, b2 = bez_split(b, 0.5) - if bez_bounds_intersect(a1, b1): recursion(a1, b1, ta0, tam, tb0, tbm, depth_a - 1, depth_b - 1) - if bez_bounds_intersect(a2, b1): recursion(a2, b1, tam, ta1, tb0, tbm, depth_a - 1, depth_b - 1) - if bez_bounds_intersect(a1, b2): recursion(a1, b2, ta0, tam, tbm, tb1, depth_a - 1, depth_b - 1) - if bez_bounds_intersect(a2, b2): recursion(a2, b2, tam, ta1, tbm, tb1, depth_a - 1, depth_b - 1) - elif depth_a > 0: - a1, a2 = bez_split(a, 0.5) - if bez_bounds_intersect(a1, b): recursion(a1, b, ta0, tam, tb0, tb1, depth_a - 1, depth_b) - if bez_bounds_intersect(a2, b): recursion(a2, b, tam, ta1, tb0, tb1, depth_a - 1, depth_b) - elif depth_b > 0: - b1, b2 = bez_split(b, 0.5) - if bez_bounds_intersect(a, b1): recursion(a, b1, ta0, ta1, tb0, tbm, depth_a, depth_b - 1) - if bez_bounds_intersect(a, b2): recursion(a, b2, ta0, ta1, tbm, tb1, depth_a, depth_b - 1) - else: # Both segments have been subdevided enougth. Let's get some intersections :). - intersection, t1, t2 = straight_segments_intersection([a[0]] + [a[3]], [b[0]] + [b[3]]) - if intersection: - if intersection == "Overlap": - t1 = (max(0, min(1, t1[0])) + max(0, min(1, t1[1]))) / 2 - t2 = (max(0, min(1, t2[0])) + max(0, min(1, t2[1]))) / 2 - bezier_intersection_recursive_result += [[ta0 + t1 * (ta1 - ta0), tb0 + t2 * (tb1 - tb0)]] - - global bezier_intersection_recursive_result - bezier_intersection_recursive_result = [] - recursion(a, b, 0., 1., 0., 1., intersection_recursion_depth, intersection_recursion_depth) - intersections = bezier_intersection_recursive_result - for i in range(len(intersections)): - if len(intersections[i]) < 5 or intersections[i][4] != "Overlap": - intersections[i] = polish_intersection(a, b, intersections[i][0], intersections[i][1]) - return intersections -def csp_segments_true_intersection(sp1, sp2, sp3, sp4): - intersections = csp_segments_intersection(sp1, sp2, sp3, sp4) - res = [] - for intersection in intersections: - if ( - (len(intersection) == 5 and intersection[4] == "Overlap" and ( - 0 <= intersection[0] <= 1 or 0 <= intersection[1] <= 1) and ( - 0 <= intersection[2] <= 1 or 0 <= intersection[3] <= 1)) - or (0 <= intersection[0] <= 1 and 0 <= intersection[1] <= 1) - ): - res += [intersection] - return res -def csp_get_t_at_curvature(sp1, sp2, c, sample_points=16): - # returns a list containning [t1,t2,t3,...,tn], 0<=ti<=1... - if sample_points < 2: sample_points = 2 - tolerance = .0000000001 - res = [] - ax, ay, bx, by, cx, cy, dx, dy = csp_parameterize(sp1, sp2) - for k in range(sample_points): - t = float(k) / (sample_points - 1) - i, F = 0, 1e100 - while i < 2 or abs(F) > tolerance and i < 17: - try: # some numerical calculation could exceed the limits - t2 = t * t - # slopes... - f1x = 3 * ax * t2 + 2 * bx * t + cx - f1y = 3 * ay * t2 + 2 * by * t + cy - f2x = 6 * ax * t + 2 * bx - f2y = 6 * ay * t + 2 * by - f3x = 6 * ax - f3y = 6 * ay - d = (f1x ** 2 + f1y ** 2) ** 1.5 - F1 = ( - ((f1x * f3y - f3x * f1y) * d - (f1x * f2y - f2x * f1y) * 3. * (f2x * f1x + f2y * f1y) * ( - (f1x ** 2 + f1y ** 2) ** .5)) / - ((f1x ** 2 + f1y ** 2) ** 3) - ) - F = (f1x * f2y - f1y * f2x) / d - c - t -= F / F1 - except: - break - i += 1 - if 0 <= t <= 1 and F <= tolerance: - if len(res) == 0: - res.append(t) - for i in res: - if abs(t - i) <= 0.001: - break - if not abs(t - i) <= 0.001: - res.append(t) - return res - - -def csp_max_curvature(sp1, sp2): - ax, ay, bx, by, cx, cy, dx, dy = csp_parameterize(sp1, sp2) - tolerance = .0001 - F = 0. - i = 0 - while i < 2 or F - Flast < tolerance and i < 10: - t = .5 - f1x = 3 * ax * t ** 2 + 2 * bx * t + cx - f1y = 3 * ay * t ** 2 + 2 * by * t + cy - f2x = 6 * ax * t + 2 * bx - f2y = 6 * ay * t + 2 * by - f3x = 6 * ax - f3y = 6 * ay - d = pow(f1x ** 2 + f1y ** 2, 1.5) - if d != 0: - Flast = F - F = (f1x * f2y - f1y * f2x) / d - F1 = ( - (d * (f1x * f3y - f3x * f1y) - (f1x * f2y - f2x * f1y) * 3. * (f2x * f1x + f2y * f1y) * pow( - f1x ** 2 + f1y ** 2, .5)) / - (f1x ** 2 + f1y ** 2) ** 3 - ) - i += 1 - if F1 != 0: - t -= F / F1 - else: - break - else: - break - return t def csp_curvature_at_t(sp1, sp2, t, depth=3): @@ -585,44 +273,10 @@ def csp_curvature_at_t(sp1, sp2, t, depth=3): return 1e100 -def csp_curvature_radius_at_t(sp1, sp2, t): - c = csp_curvature_at_t(sp1, sp2, t) - if c == 0: - return 1e100 - else: - return 1 / c -def csp_special_points(sp1, sp2): - # special points = curvature == 0 - ax, ay, bx, by, cx, cy, dx, dy = bezmisc.bezierparameterize((sp1[1], sp1[2], sp2[0], sp2[1])) - a = 3 * ax * by - 3 * ay * bx - b = 3 * ax * cy - 3 * cx * ay - c = bx * cy - cx * by - roots = cubic_solver(0, a, b, c) - res = [] - for i in roots: - if type(i) is complex and i.imag == 0: - i = i.real - if type(i) is not complex and 0 <= i <= 1: - res.append(i) - return res -def csp_subpath_ccw(subpath): - # Remove all zerro length segments - s = 0 - # subpath = subpath[:] - if (P(subpath[-1][1]) - P(subpath[0][1])).l2() > 1e-10: - subpath[-1][2] = subpath[-1][1] - subpath[0][0] = subpath[0][1] - subpath += [[subpath[0][1], subpath[0][1], subpath[0][1]]] - pl = subpath[-1][2] - for sp1 in subpath: - for p in sp1: - s += (p[0] - pl[0]) * (p[1] + pl[1]) - pl = p - return s < 0 def csp_at_t(sp1, sp2, t): @@ -640,10 +294,6 @@ def csp_at_t(sp1, sp2, t): return [x, y] -def csp_splitatlength(sp1, sp2, l=0.5, tolerance=0.01): - bez = (sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:]) - t = bezmisc.beziertatlength(bez, l, tolerance) - return csp_split(sp1, sp2, t) def cspseglength(sp1, sp2, tolerance=0.001): @@ -651,116 +301,16 @@ def cspseglength(sp1, sp2, tolerance=0.001): return bezmisc.bezierlength(bez, tolerance) -def csplength(csp): - total = 0 - lengths = [] - for sp in csp: - for i in xrange(1, len(sp)): - l = cspseglength(sp[i - 1], sp[i]) - lengths.append(l) - total += l - return lengths, total -def csp_segments(csp): - l, seg = 0, [0] - for sp in csp: - for i in xrange(1, len(sp)): - l += cspseglength(sp[i - 1], sp[i]) - seg += [l] - - if l > 0: - seg = [seg[i] / l for i in xrange(len(seg))] - return seg, l -def rebuild_csp(csp, segs, s=None): - # rebuild_csp() adds to csp control points making it's segments looks like segs - if s == None: s, l = csp_segments(csp) - - if len(s) > len(segs): return None - segs = segs[:] - segs.sort() - for i in xrange(len(s)): - d = None - for j in xrange(len(segs)): - d = min([abs(s[i] - segs[j]), j], d) if d != None else [abs(s[i] - segs[j]), j] - del segs[d[1]] - for i in xrange(len(segs)): - for j in xrange(0, len(s)): - if segs[i] < s[j]: break - if s[j] - s[j - 1] != 0: - t = (segs[i] - s[j - 1]) / (s[j] - s[j - 1]) - sp1, sp2, sp3 = csp_split(csp[j - 1], csp[j], t) - csp = csp[:j - 1] + [sp1, sp2, sp3] + csp[j + 1:] - s = s[:j] + [s[j - 1] * (1 - t) + s[j] * t] + s[j:] - return csp, s -def csp_slope(sp1, sp2, t): - bez = (sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:]) - return bezmisc.bezierslopeatt(bez, t) -def csp_line_intersection(l1, l2, sp1, sp2): - dd = l1[0] - cc = l2[0] - l1[0] - bb = l1[1] - aa = l2[1] - l1[1] - if aa == cc == 0: return [] - if aa: - coef1 = cc / aa - coef2 = 1 - else: - coef1 = 1 - coef2 = aa / cc - bez = (sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:]) - ax, ay, bx, by, cx, cy, x0, y0 = bezmisc.bezierparameterize(bez) - a = coef1 * ay - coef2 * ax - b = coef1 * by - coef2 * bx - c = coef1 * cy - coef2 * cx - d = coef1 * (y0 - bb) - coef2 * (x0 - dd) - roots = cubic_solver(a, b, c, d) - retval = [] - for i in roots: - if type(i) is complex and abs(i.imag) < 1e-7: - i = i.real - if type(i) is not complex and -1e-10 <= i <= 1. + 1e-10: - retval.append(i) - return retval -def csp_from_arc(start, end, center, r, slope_st): - # Creates csp that approximise specified arc - r = abs(r) - alpha = (atan2(end[0] - center[0], end[1] - center[1]) - atan2(start[0] - center[0], - start[1] - center[1])) % math.pi2 - - sectors = int(abs(alpha) * 2 / math.pi) + 1 - alpha_start = atan2(start[0] - center[0], start[1] - center[1]) - cos_, sin_ = math.cos(alpha_start), math.sin(alpha_start) - k = (4. * math.tan(alpha / sectors / 4.) / 3.) - if dot(slope_st, [- sin_ * k * r, cos_ * k * r]) < 0: - if alpha > 0: - alpha -= math.pi2 - else: - alpha += math.pi2 - if abs(alpha * r) < 0.001: - return [] - - sectors = int(abs(alpha) * 2 / math.pi) + 1 - k = (4. * math.tan(alpha / sectors / 4.) / 3.) - result = [] - for i in range(sectors + 1): - cos_, sin_ = math.cos(alpha_start + alpha * i / sectors), math.sin(alpha_start + alpha * i / sectors) - sp = [[], [center[0] + cos_ * r, center[1] + sin_ * r], []] - sp[0] = [sp[1][0] + sin_ * k * r, sp[1][1] - cos_ * k * r] - sp[2] = [sp[1][0] - sin_ * k * r, sp[1][1] + cos_ * k * r] - result += [sp] - result[0][0] = result[0][1][:] - result[-1][2] = result[-1][1] - - return result def point_to_arc_distance(p, arc): @@ -802,438 +352,100 @@ def csp_to_arc_distance(sp1, sp2, arc1, arc2, tolerance=0.01): # arc = [start,e return d1[0] -def csp_simple_bound_to_point_distance(p, csp): - minx, miny, maxx, maxy = None, None, None, None - for subpath in csp: - for sp in subpath: - for p_ in sp: - minx = min(minx, p_[0]) if minx != None else p_[0] - miny = min(miny, p_[1]) if miny != None else p_[1] - maxx = max(maxx, p_[0]) if maxx != None else p_[0] - maxy = max(maxy, p_[1]) if maxy != None else p_[1] - return math.sqrt(max(minx - p[0], p[0] - maxx, 0) ** 2 + max(miny - p[1], p[1] - maxy, 0) ** 2) -def csp_point_inside_bound(sp1, sp2, p): - bez = [sp1[1], sp1[2], sp2[0], sp2[1]] - x, y = p - c = 0 - for i in range(4): - [x0, y0], [x1, y1] = bez[i - 1], bez[i] - if x0 - x1 != 0 and (y - y0) * (x1 - x0) >= (x - x0) * (y1 - y0) and x > min(x0, x1) and x <= max(x0, x1): - c += 1 - return c % 2 == 0 -def csp_bound_to_point_distance(sp1, sp2, p): - if csp_point_inside_bound(sp1, sp2, p): - return 0. - bez = csp_segment_to_bez(sp1, sp2) - min_dist = 1e100 - for i in range(0, 4): - d = point_to_line_segment_distance_2(p, bez[i - 1], bez[i]) - if d <= min_dist: min_dist = d - return min_dist -def line_line_intersect(p1, p2, p3, p4): # Return only true intersection. - if (p1[0] == p2[0] and p1[1] == p2[1]) or (p3[0] == p4[0] and p3[1] == p4[1]): return False - x = (p2[0] - p1[0]) * (p4[1] - p3[1]) - (p2[1] - p1[1]) * (p4[0] - p3[0]) - if x == 0: # Lines are parallel - if (p3[0] - p1[0]) * (p2[1] - p1[1]) == (p3[1] - p1[1]) * (p2[0] - p1[0]): - if p3[0] != p4[0]: - t11 = (p1[0] - p3[0]) / (p4[0] - p3[0]) - t12 = (p2[0] - p3[0]) / (p4[0] - p3[0]) - t21 = (p3[0] - p1[0]) / (p2[0] - p1[0]) - t22 = (p4[0] - p1[0]) / (p2[0] - p1[0]) - else: - t11 = (p1[1] - p3[1]) / (p4[1] - p3[1]) - t12 = (p2[1] - p3[1]) / (p4[1] - p3[1]) - t21 = (p3[1] - p1[1]) / (p2[1] - p1[1]) - t22 = (p4[1] - p1[1]) / (p2[1] - p1[1]) - return ("Overlap" if (0 <= t11 <= 1 or 0 <= t12 <= 1) and (0 <= t21 <= 1 or 0 <= t22 <= 1) else False) - else: - return False - else: - return ( - 0 <= ((p4[0] - p3[0]) * (p1[1] - p3[1]) - (p4[1] - p3[1]) * (p1[0] - p3[0])) / x <= 1 and - 0 <= ((p2[0] - p1[0]) * (p1[1] - p3[1]) - (p2[1] - p1[1]) * (p1[0] - p3[0])) / x <= 1) -def line_line_intersection_points(p1, p2, p3, p4): # Return only points [ (x,y) ] - if (p1[0] == p2[0] and p1[1] == p2[1]) or (p3[0] == p4[0] and p3[1] == p4[1]): return [] - x = (p2[0] - p1[0]) * (p4[1] - p3[1]) - (p2[1] - p1[1]) * (p4[0] - p3[0]) - if x == 0: # Lines are parallel - if (p3[0] - p1[0]) * (p2[1] - p1[1]) == (p3[1] - p1[1]) * (p2[0] - p1[0]): - if p3[0] != p4[0]: - t11 = (p1[0] - p3[0]) / (p4[0] - p3[0]) - t12 = (p2[0] - p3[0]) / (p4[0] - p3[0]) - t21 = (p3[0] - p1[0]) / (p2[0] - p1[0]) - t22 = (p4[0] - p1[0]) / (p2[0] - p1[0]) - else: - t11 = (p1[1] - p3[1]) / (p4[1] - p3[1]) - t12 = (p2[1] - p3[1]) / (p4[1] - p3[1]) - t21 = (p3[1] - p1[1]) / (p2[1] - p1[1]) - t22 = (p4[1] - p1[1]) / (p2[1] - p1[1]) - res = [] - if (0 <= t11 <= 1 or 0 <= t12 <= 1) and (0 <= t21 <= 1 or 0 <= t22 <= 1): - if 0 <= t11 <= 1: res += [p1] - if 0 <= t12 <= 1: res += [p2] - if 0 <= t21 <= 1: res += [p3] - if 0 <= t22 <= 1: res += [p4] - return res - else: - return [] - else: - t1 = ((p4[0] - p3[0]) * (p1[1] - p3[1]) - (p4[1] - p3[1]) * (p1[0] - p3[0])) / x - t2 = ((p2[0] - p1[0]) * (p1[1] - p3[1]) - (p2[1] - p1[1]) * (p1[0] - p3[0])) / x - if 0 <= t1 <= 1 and 0 <= t2 <= 1: - return [[p1[0] * (1 - t1) + p2[0] * t1, p1[1] * (1 - t1) + p2[1] * t1]] - else: - return [] -def point_to_point_d2(a, b): - return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 -def point_to_point_d(a, b): - return math.sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2) -def point_to_line_segment_distance_2(p1, p2, p3): - # p1 - point, p2,p3 - line segment - # draw_pointer(p1) - w0 = [p1[0] - p2[0], p1[1] - p2[1]] - v = [p3[0] - p2[0], p3[1] - p2[1]] - c1 = w0[0] * v[0] + w0[1] * v[1] - if c1 <= 0: - return w0[0] * w0[0] + w0[1] * w0[1] - c2 = v[0] * v[0] + v[1] * v[1] - if c2 <= c1: - return (p1[0] - p3[0]) ** 2 + (p1[1] - p3[1]) ** 2 - return (p1[0] - p2[0] - v[0] * c1 / c2) ** 2 + (p1[1] - p2[1] - v[1] * c1 / c2) -def line_to_line_distance_2(p1, p2, p3, p4): - if line_line_intersect(p1, p2, p3, p4): return 0 - return min( - point_to_line_segment_distance_2(p1, p3, p4), - point_to_line_segment_distance_2(p2, p3, p4), - point_to_line_segment_distance_2(p3, p1, p2), - point_to_line_segment_distance_2(p4, p1, p2)) -def csp_seg_bound_to_csp_seg_bound_max_min_distance(sp1, sp2, sp3, sp4): - bez1 = csp_segment_to_bez(sp1, sp2) - bez2 = csp_segment_to_bez(sp3, sp4) - min_dist = 1e100 - max_dist = 0. - for i in range(4): - if csp_point_inside_bound(sp1, sp2, bez2[i]) or csp_point_inside_bound(sp3, sp4, bez1[i]): - min_dist = 0. - break - for i in range(4): - for j in range(4): - d = line_to_line_distance_2(bez1[i - 1], bez1[i], bez2[j - 1], bez2[j]) - if d < min_dist: min_dist = d - d = (bez2[j][0] - bez1[i][0]) ** 2 + (bez2[j][1] - bez1[i][1]) ** 2 - if max_dist < d: max_dist = d - return min_dist, max_dist -def csp_reverse(csp): - for i in range(len(csp)): - n = [] - for j in csp[i]: - n = [[j[2][:], j[1][:], j[0][:]]] + n - csp[i] = n[:] - return csp -def csp_normalized_slope(sp1, sp2, t): - ax, ay, bx, by, cx, cy, dx, dy = bezmisc.bezierparameterize((sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:])) - if sp1[1] == sp2[1] == sp1[2] == sp2[0]: return [1., 0.] - f1x = 3 * ax * t * t + 2 * bx * t + cx - f1y = 3 * ay * t * t + 2 * by * t + cy - if abs(f1x * f1x + f1y * f1y) > 1e-20: - l = math.sqrt(f1x * f1x + f1y * f1y) - return [f1x / l, f1y / l] - - if t == 0: - f1x = sp2[0][0] - sp1[1][0] - f1y = sp2[0][1] - sp1[1][1] - if abs(f1x * f1x + f1y * f1y) > 1e-20: - l = math.sqrt(f1x * f1x + f1y * f1y) - return [f1x / l, f1y / l] - else: - f1x = sp2[1][0] - sp1[1][0] - f1y = sp2[1][1] - sp1[1][1] - if f1x * f1x + f1y * f1y != 0: - l = math.sqrt(f1x * f1x + f1y * f1y) - return [f1x / l, f1y / l] - elif t == 1: - f1x = sp2[1][0] - sp1[2][0] - f1y = sp2[1][1] - sp1[2][1] - if abs(f1x * f1x + f1y * f1y) > 1e-20: - l = math.sqrt(f1x * f1x + f1y * f1y) - return [f1x / l, f1y / l] - else: - f1x = sp2[1][0] - sp1[1][0] - f1y = sp2[1][1] - sp1[1][1] - if f1x * f1x + f1y * f1y != 0: - l = math.sqrt(f1x * f1x + f1y * f1y) - return [f1x / l, f1y / l] - else: - return [1., 0.] -def csp_normalized_normal(sp1, sp2, t): - nx, ny = csp_normalized_slope(sp1, sp2, t) - return [-ny, nx] -def csp_parameterize(sp1, sp2): - return bezmisc.bezierparameterize(csp_segment_to_bez(sp1, sp2)) -def csp_concat_subpaths(*s): - def concat(s1, s2): - if s1 == []: return s2 - if s2 == []: return s1 - if (s1[-1][1][0] - s2[0][1][0]) ** 2 + (s1[-1][1][1] - s2[0][1][1]) ** 2 > 0.00001: - return s1[:-1] + [[s1[-1][0], s1[-1][1], s1[-1][1]], [s2[0][1], s2[0][1], s2[0][2]]] + s2[1:] - else: - return s1[:-1] + [[s1[-1][0], s2[0][1], s2[0][2]]] + s2[1:] - - if len(s) == 0: return [] - if len(s) == 1: return s[0] - result = s[0] - for s1 in s[1:]: - result = concat(result, s1) - return result -def csp_draw(csp, color="#05f", group=None, style="fill:none;", width=.1, comment=""): - if csp != [] and csp != [[]]: - if group == None: group = options.doc_root - style += "stroke:" + color + ";" + "stroke-width:%0.4fpx;" % width - args = {"d": cubicsuperpath.formatPath(csp), "style": style} - if comment != "": args["comment"] = str(comment) - inkex.etree.SubElement(group, inkex.addNS('path', 'svg'), args) -def csp_subpaths_end_to_start_distance2(s1, s2): - return (s1[-1][1][0] - s2[0][1][0]) ** 2 + (s1[-1][1][1] - s2[0][1][1]) ** 2 -def csp_subpath_line_to(subpath, points): - # Appends subpath with line or polyline. - if len(points) > 0: - if len(subpath) > 0: - subpath[-1][2] = subpath[-1][1][:] - if type(points[0]) == type([1, 1]): - for p in points: - subpath += [[p[:], p[:], p[:]]] - else: - subpath += [[points, points, points]] - return subpath -def csp_join_subpaths(csp): - result = csp[:] - done_smf = True - joined_result = [] - while done_smf: - done_smf = False - while len(result) > 0: - s1 = result[-1][:] - del (result[-1]) - j = 0 - joined_smf = False - while j < len(joined_result): - if csp_subpaths_end_to_start_distance2(joined_result[j], s1) < 0.000001: - joined_result[j] = csp_concat_subpaths(joined_result[j], s1) - done_smf = True - joined_smf = True - break - if csp_subpaths_end_to_start_distance2(s1, joined_result[j]) < 0.000001: - joined_result[j] = csp_concat_subpaths(s1, joined_result[j]) - done_smf = True - joined_smf = True - break - j += 1 - if not joined_smf: joined_result += [s1[:]] - if done_smf: - result = joined_result[:] - joined_result = [] - return joined_result -def triangle_cross(a, b, c): - return (a[0] - b[0]) * (c[1] - b[1]) - (c[0] - b[0]) * (a[1] - b[1]) -def csp_segment_convex_hull(sp1, sp2): - a, b, c, d = sp1[1][:], sp1[2][:], sp2[0][:], sp2[1][:] - - abc = triangle_cross(a, b, c) - abd = triangle_cross(a, b, d) - bcd = triangle_cross(b, c, d) - cad = triangle_cross(c, a, d) - if abc == 0 and abd == 0: return [min(a, b, c, d), max(a, b, c, d)] - if abc == 0: return [d, min(a, b, c), max(a, b, c)] - if abd == 0: return [c, min(a, b, d), max(a, b, d)] - if bcd == 0: return [a, min(b, c, d), max(b, c, d)] - if cad == 0: return [b, min(c, a, d), max(c, a, d)] - - m1, m2, m3 = abc * abd > 0, abc * bcd > 0, abc * cad > 0 - if m1 and m2 and m3: return [a, b, c] - if m1 and m2 and not m3: return [a, b, c, d] - if m1 and not m2 and m3: return [a, b, d, c] - if not m1 and m2 and m3: return [a, d, b, c] - if m1 and not (m2 and m3): return [a, b, d] - if not (m1 and m2) and m3: return [c, a, d] - if not (m1 and m3) and m2: return [b, c, d] - - raise ValueError, "csp_segment_convex_hull happend something that shouldnot happen!" ################################################################################ ### Bezier additional functions ################################################################################ -def bez_bounds_intersect(bez1, bez2): - return bounds_intersect(bez_bound(bez2), bez_bound(bez1)) -def bez_bound(bez): - return [ - min(bez[0][0], bez[1][0], bez[2][0], bez[3][0]), - min(bez[0][1], bez[1][1], bez[2][1], bez[3][1]), - max(bez[0][0], bez[1][0], bez[2][0], bez[3][0]), - max(bez[0][1], bez[1][1], bez[2][1], bez[3][1]), - ] -def bounds_intersect(a, b): - return not ((a[0] > b[2]) or (b[0] > a[2]) or (a[1] > b[3]) or (b[1] > a[3])) -def tpoint((x1, y1), (x2, y2), t): - return [x1 + t * (x2 - x1), y1 + t * (y2 - y1)] -def bez_to_csp_segment(bez): - return [bez[0], bez[0], bez[1]], [bez[2], bez[3], bez[3]] -def bez_split(a, t=0.5): - a1 = tpoint(a[0], a[1], t) - at = tpoint(a[1], a[2], t) - b2 = tpoint(a[2], a[3], t) - a2 = tpoint(a1, at, t) - b1 = tpoint(b2, at, t) - a3 = tpoint(a2, b1, t) - return [a[0], a1, a2, a3], [a3, b1, b2, a[3]] -def bez_at_t(bez, t): - return csp_at_t([bez[0], bez[0], bez[1]], [bez[2], bez[3], bez[3]], t) -def bez_to_point_distance(bez, p, needed_dist=[0., 1e100]): - # returns [d^2,t] - return csp_seg_to_point_distance(bez_to_csp_segment(bez), p, needed_dist) -def bez_normalized_slope(bez, t): - return csp_normalized_slope([bez[0], bez[0], bez[1]], [bez[2], bez[3], bez[3]], t) ################################################################################ ### Some vector functions ################################################################################ -def normalize((x, y)): - l = math.sqrt(x ** 2 + y ** 2) - if l == 0: - return [0., 0.] - else: - return [x / l, y / l] -def cross(a, b): - return a[1] * b[0] - a[0] * b[1] -def dot(a, b): - return a[0] * b[0] + a[1] * b[1] -def rotate_ccw(d): - return [-d[1], d[0]] -def vectors_ccw(a, b): - return a[0] * b[1] - b[0] * a[1] < 0 -def vector_from_to_length(a, b): - return math.sqrt((a[0] - b[0]) * (a[0] - b[0]) + (a[1] - b[1]) * (a[1] - b[1])) ################################################################################ ### Common functions ################################################################################ -def matrix_mul(a, b): - return [[sum([a[i][k] * b[k][j] for k in range(len(a[0]))]) for j in range(len(b[0]))] for i in range(len(a))] - try: - return [[sum([a[i][k] * b[k][j] for k in range(len(a[0]))]) for j in range(len(b[0]))] for i in range(len(a))] - except: - return None -def transpose(a): - try: - return [[a[i][j] for i in range(len(a))] for j in range(len(a[0]))] - except: - return None -def det_3x3(a): - return float( - a[0][0] * a[1][1] * a[2][2] + a[0][1] * a[1][2] * a[2][0] + a[1][0] * a[2][1] * a[0][2] - - a[0][2] * a[1][1] * a[2][0] - a[0][0] * a[2][1] * a[1][2] - a[0][1] * a[2][2] * a[1][0] - ) -def inv_3x3(a): # invert matrix 3x3 - det = det_3x3(a) - if det == 0: return None - return [ - [(a[1][1] * a[2][2] - a[2][1] * a[1][2]) / det, -(a[0][1] * a[2][2] - a[2][1] * a[0][2]) / det, - (a[0][1] * a[1][2] - a[1][1] * a[0][2]) / det], - [-(a[1][0] * a[2][2] - a[2][0] * a[1][2]) / det, (a[0][0] * a[2][2] - a[2][0] * a[0][2]) / det, - -(a[0][0] * a[1][2] - a[1][0] * a[0][2]) / det], - [(a[1][0] * a[2][1] - a[2][0] * a[1][1]) / det, -(a[0][0] * a[2][1] - a[2][0] * a[0][1]) / det, - (a[0][0] * a[1][1] - a[1][0] * a[0][1]) / det] - ] -def inv_2x2(a): # invert matrix 2x2 - det = a[0][0] * a[1][1] - a[1][0] * a[0][1] - if det == 0: return None - return [ - [a[1][1] / det, -a[0][1] / det], - [-a[1][0] / det, a[0][0] / det] - ] -def small(a): - global small_tolerance - return abs(a) < small_tolerance def atan2(*arg): @@ -1246,103 +458,20 @@ def atan2(*arg): raise ValueError, "Bad argumets for atan! (%s)" % arg -def draw_text(text, x, y, style=None, font_size=20): - if style == None: - style = "font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;" - style += "font-size:%fpx;" % font_size - t = inkex.etree.SubElement(options.doc_root, inkex.addNS('text', 'svg'), { - 'x': str(x), - inkex.addNS("space", "xml"): "preserve", - 'y': str(y) - }) - text = str(text).split("\n") - for s in text: - span = inkex.etree.SubElement(t, inkex.addNS('tspan', 'svg'), - { - 'x': str(x), - 'y': str(+y), - inkex.addNS("role", "sodipodi"): "line", - }) - y += font_size - span.text = s -def draw_pointer(x, color="#f00", figure="cross", comment="", width=.1): - if figure == "line": - s = "" - for i in range(1, len(x) / 2): - s += " %s, %s " % (x[i * 2], x[i * 2 + 1]) - inkex.etree.SubElement(options.doc_root, inkex.addNS('path', 'svg'), {"d": "M %s,%s L %s" % (x[0], x[1], s), - "style": "fill:none;stroke:%s;stroke-width:%f;" % ( - color, width), "comment": str(comment)}) - else: - inkex.etree.SubElement(options.doc_root, inkex.addNS('path', 'svg'), - {"d": "m %s,%s l 10,10 -20,-20 10,10 -10,10, 20,-20" % (x[0], x[1]), - "style": "fill:none;stroke:%s;stroke-width:%f;" % (color, width), - "comment": str(comment)}) -def straight_segments_intersection(a, b, - true_intersection=True): # (True intersection means check ta and tb are in [0,1]) - ax, bx, cx, dx, ay, by, cy, dy = a[0][0], a[1][0], b[0][0], b[1][0], a[0][1], a[1][1], b[0][1], b[1][1] - if (ax == bx and ay == by) or (cx == dx and cy == dy): return False, 0, 0 - if (bx - ax) * (dy - cy) - (by - ay) * (dx - cx) == 0: # Lines are parallel - ta = (ax - cx) / (dx - cx) if cx != dx else (ay - cy) / (dy - cy) - tb = (bx - cx) / (dx - cx) if cx != dx else (by - cy) / (dy - cy) - tc = (cx - ax) / (bx - ax) if ax != bx else (cy - ay) / (by - ay) - td = (dx - ax) / (bx - ax) if ax != bx else (dy - ay) / (by - ay) - return ( - "Overlap" if 0 <= ta <= 1 or 0 <= tb <= 1 or 0 <= tc <= 1 or 0 <= td <= 1 or not true_intersection else False), ( - ta, tb), (tc, td) - else: - ta = ((ay - cy) * (dx - cx) - (ax - cx) * (dy - cy)) / ((bx - ax) * (dy - cy) - (by - ay) * (dx - cx)) - tb = (ax - cx + ta * (bx - ax)) / (dx - cx) if dx != cx else (ay - cy + ta * (by - ay)) / (dy - cy) - return (0 <= ta <= 1 and 0 <= tb <= 1 or not true_intersection), ta, tb -def isnan(x): return type(x) is float and x != x -def isinf(x): inf = 1e5000; return x == inf or x == -inf def between(c, x, y): return x - straight_tolerance <= c <= y + straight_tolerance or y - straight_tolerance <= c <= x + straight_tolerance -def cubic_solver(a, b, c, d): - if a != 0: - # Monics formula see http://en.wikipedia.org/wiki/Cubic_function#Monic_formula_of_roots - a, b, c = (b / a, c / a, d / a) - m = 2 * a ** 3 - 9 * a * b + 27 * c - k = a ** 2 - 3 * b - n = m ** 2 - 4 * k ** 3 - w1 = -.5 + .5 * cmath.sqrt(3) * 1j - w2 = -.5 - .5 * cmath.sqrt(3) * 1j - if n >= 0: - t = m + math.sqrt(n) - m1 = pow(t / 2, 1. / 3) if t >= 0 else -pow(-t / 2, 1. / 3) - t = m - math.sqrt(n) - n1 = pow(t / 2, 1. / 3) if t >= 0 else -pow(-t / 2, 1. / 3) - else: - m1 = pow(complex((m + cmath.sqrt(n)) / 2), 1. / 3) - n1 = pow(complex((m - cmath.sqrt(n)) / 2), 1. / 3) - x1 = -1. / 3 * (a + m1 + n1) - x2 = -1. / 3 * (a + w1 * m1 + w2 * n1) - x3 = -1. / 3 * (a + w2 * m1 + w1 * n1) - return [x1, x2, x3] - elif b != 0: - det = c ** 2 - 4 * b * d - if det > 0: - return [(-c + math.sqrt(det)) / (2 * b), (-c - math.sqrt(det)) / (2 * b)] - elif d == 0: - return [-c / (b * b)] - else: - return [(-c + cmath.sqrt(det)) / (2 * b), (-c - cmath.sqrt(det)) / (2 * b)] - elif c != 0: - return [-d / c] - else: - return [] ################################################################################ @@ -1397,13 +526,7 @@ class P: else: return P(0, 0) - def dot(self, other): - return self.x * other.x + self.y * other.y - def rot(self, theta): - c = math.cos(theta) - s = math.sin(theta) - return P(self.x * c - self.y * s, self.x * s + self.y * c) def angle(self): return math.atan2(self.y, self.x) @@ -1411,14 +534,8 @@ class P: def __repr__(self): return '%f,%f' % (self.x, self.y) - def pr(self): - return "%.2f,%.2f" % (self.x, self.y) - def to_list(self): - return [self.x, self.y] - def ccw(self): - return P(-self.y, self.x) def l2(self): return self.x * self.x + self.y * self.y @@ -1433,352 +550,6 @@ class P: ### ### ################################################################################ -def csp_offset(csp, r): - offset_tolerance = 0.05 - offset_subdivision_depth = 10 - time_ = time.time() - time_start = time_ - print_("Offset start at %s" % time_) - print_("Offset radius %s" % r) - - - def create_offset_segment(sp1, sp2, r): - # See Gernot Hoffmann "Bezier Curves" p.34 -> 7.1 Bezier Offset Curves - p0, p1, p2, p3 = P(sp1[1]), P(sp1[2]), P(sp2[0]), P(sp2[1]) - s0, s1, s3 = p1 - p0, p2 - p1, p3 - p2 - n0 = s0.ccw().unit() if s0.l2() != 0 else P(csp_normalized_normal(sp1, sp2, 0)) - n3 = s3.ccw().unit() if s3.l2() != 0 else P(csp_normalized_normal(sp1, sp2, 1)) - n1 = s1.ccw().unit() if s1.l2() != 0 else (n0.unit() + n3.unit()).unit() - - q0, q3 = p0 + r * n0, p3 + r * n3 - c = csp_curvature_at_t(sp1, sp2, 0) - q1 = q0 + (p1 - p0) * (1 - (r * c if abs(c) < 100 else 0)) - c = csp_curvature_at_t(sp1, sp2, 1) - q2 = q3 + (p2 - p3) * (1 - (r * c if abs(c) < 100 else 0)) - - return [[q0.to_list(), q0.to_list(), q1.to_list()], [q2.to_list(), q3.to_list(), q3.to_list()]] - - - def csp_get_subapths_last_first_intersection(s1, s2): - _break = False - for i in range(1, len(s1)): - sp11, sp12 = s1[-i - 1], s1[-i] - for j in range(1, len(s2)): - sp21, sp22 = s2[j - 1], s2[j] - intersection = csp_segments_true_intersection(sp11, sp12, sp21, sp22) - if intersection != []: - _break = True - break - if _break: break - if _break: - intersection = max(intersection) - return [len(s1) - i, intersection[0], j, intersection[1]] - else: - return [] - - - def csp_join_offsets(prev, next, sp1, sp2, sp1_l, sp2_l, r): - if len(next) > 1: - if (P(prev[-1][1]) - P(next[0][1])).l2() < 0.001: - return prev, [], next - intersection = csp_get_subapths_last_first_intersection(prev, next) - if intersection != []: - i, t1, j, t2 = intersection - sp1_, sp2_, sp3_ = csp_split(prev[i - 1], prev[i], t1) - sp3_, sp4_, sp5_ = csp_split(next[j - 1], next[j], t2) - return prev[:i - 1] + [sp1_, sp2_], [], [sp4_, sp5_] + next[j + 1:] - - # Offsets do not intersect... will add an arc... - start = (P(csp_at_t(sp1_l, sp2_l, 1.)) + r * P(csp_normalized_normal(sp1_l, sp2_l, 1.))).to_list() - end = (P(csp_at_t(sp1, sp2, 0.)) + r * P(csp_normalized_normal(sp1, sp2, 0.))).to_list() - arc = csp_from_arc(start, end, sp1[1], r, csp_normalized_slope(sp1_l, sp2_l, 1.)) - if arc == []: - return prev, [], next - else: - # Clip prev by arc - if csp_subpaths_end_to_start_distance2(prev, arc) > 0.00001: - intersection = csp_get_subapths_last_first_intersection(prev, arc) - if intersection != []: - i, t1, j, t2 = intersection - sp1_, sp2_, sp3_ = csp_split(prev[i - 1], prev[i], t1) - sp3_, sp4_, sp5_ = csp_split(arc[j - 1], arc[j], t2) - prev = prev[:i - 1] + [sp1_, sp2_] - arc = [sp4_, sp5_] + arc[j + 1:] - # else : raise ValueError, "Offset curvature clipping error" - # Clip next by arc - if next == []: - return prev, [], arc - if csp_subpaths_end_to_start_distance2(arc, next) > 0.00001: - intersection = csp_get_subapths_last_first_intersection(arc, next) - if intersection != []: - i, t1, j, t2 = intersection - sp1_, sp2_, sp3_ = csp_split(arc[i - 1], arc[i], t1) - sp3_, sp4_, sp5_ = csp_split(next[j - 1], next[j], t2) - arc = arc[:i - 1] + [sp1_, sp2_] - next = [sp4_, sp5_] + next[j + 1:] - # else : raise ValueError, "Offset curvature clipping error" - - return prev, arc, next - - - def offset_segment_recursion(sp1, sp2, r, depth, tolerance): - sp1_r, sp2_r = create_offset_segment(sp1, sp2, r) - err = max( - csp_seg_to_point_distance(sp1_r, sp2_r, ( - P(csp_at_t(sp1, sp2, .25)) + P(csp_normalized_normal(sp1, sp2, .25)) * r).to_list())[0], - csp_seg_to_point_distance(sp1_r, sp2_r, ( - P(csp_at_t(sp1, sp2, .50)) + P(csp_normalized_normal(sp1, sp2, .50)) * r).to_list())[0], - csp_seg_to_point_distance(sp1_r, sp2_r, ( - P(csp_at_t(sp1, sp2, .75)) + P(csp_normalized_normal(sp1, sp2, .75)) * r).to_list())[0], - ) - - if err > tolerance ** 2 and depth > 0: - # print_(csp_seg_to_point_distance(sp1_r,sp2_r, (P(csp_at_t(sp1,sp2,.25)) + P(csp_normalized_normal(sp1,sp2,.25))*r).to_list())[0], tolerance) - if depth > offset_subdivision_depth - 2: - t = csp_max_curvature(sp1, sp2) - t = max(.1, min(.9, t)) - else: - t = .5 - sp3, sp4, sp5 = csp_split(sp1, sp2, t) - r1 = offset_segment_recursion(sp3, sp4, r, depth - 1, tolerance) - r2 = offset_segment_recursion(sp4, sp5, r, depth - 1, tolerance) - return r1[:-1] + [[r1[-1][0], r1[-1][1], r2[0][2]]] + r2[1:] - else: - # csp_draw([[sp1_r,sp2_r]]) - # draw_pointer(sp1[1]+sp1_r[1], "#057", "line") - # draw_pointer(sp2[1]+sp2_r[1], "#705", "line") - return [sp1_r, sp2_r] - - - ############################################################################ - # Some small definitions - ############################################################################ - csp_len = len(csp) - - ############################################################################ - # Prepare the path - ############################################################################ - # Remove all small segments (segment length < 0.001) - - for i in xrange(len(csp)): - for j in xrange(len(csp[i])): - sp = csp[i][j] - if (P(sp[1]) - P(sp[0])).mag() < 0.001: - csp[i][j][0] = sp[1] - if (P(sp[2]) - P(sp[0])).mag() < 0.001: - csp[i][j][2] = sp[1] - for i in xrange(len(csp)): - for j in xrange(1, len(csp[i])): - if cspseglength(csp[i][j - 1], csp[i][j]) < 0.001: - csp[i] = csp[i][:j] + csp[i][j + 1:] - if cspseglength(csp[i][-1], csp[i][0]) > 0.001: - csp[i][-1][2] = csp[i][-1][1] - csp[i] += [[csp[i][0][1], csp[i][0][1], csp[i][0][1]]] - - # TODO Get rid of self intersections. - - original_csp = csp[:] - # Clip segments which has curvature>1/r. Because their offset will be selfintersecting and very nasty. - - print_("Offset prepared the path in %s" % (time.time() - time_)) - print_("Path length = %s" % sum([len(i) for i in csp])) - time_ = time.time() - - ############################################################################ - # Offset - ############################################################################ - # Create offsets for all segments in the path. And join them together inside each subpath. - unclipped_offset = [[] for i in xrange(csp_len)] - offsets_original = [[] for i in xrange(csp_len)] - join_points = [[] for i in xrange(csp_len)] - intersection = [[] for i in xrange(csp_len)] - for i in xrange(csp_len): - subpath = csp[i] - subpath_offset = [] - last_offset_len = 0 - for sp1, sp2 in zip(subpath, subpath[1:]): - segment_offset = csp_offset_segment(sp1, sp2, r) - if subpath_offset == []: - subpath_offset = segment_offset - - prev_l = len(subpath_offset) - else: - prev, arc, next = csp_join_offsets(subpath_offset[-prev_l:], segment_offset, sp1, sp2, sp1_l, sp2_l, r) - # csp_draw([prev],"Blue") - # csp_draw([arc],"Magenta") - subpath_offset = csp_concat_subpaths(subpath_offset[:-prev_l + 1], prev, arc, next) - prev_l = len(next) - sp1_l, sp2_l = sp1[:], sp2[:] - - # Join last and first offsets togother to close the curve - - prev, arc, next = csp_join_offsets(subpath_offset[-prev_l:], subpath_offset[:2], subpath[0], subpath[1], sp1_l, - sp2_l, r) - subpath_offset[:2] = next[:] - subpath_offset = csp_concat_subpaths(subpath_offset[:-prev_l + 1], prev, arc) - # csp_draw([prev],"Blue") - # csp_draw([arc],"Red") - # csp_draw([next],"Red") - - # Collect subpath's offset and save it to unclipped offset list. - unclipped_offset[i] = subpath_offset[:] - - # for k,t in intersection[i]: - # draw_pointer(csp_at_t(subpath_offset[k-1], subpath_offset[k], t)) - - # inkex.etree.SubElement( options.doc_root, inkex.addNS('path','svg'), {"d": cubicsuperpath.formatPath(unclipped_offset), "style":"fill:none;stroke:#0f0;"} ) - print_("Offsetted path in %s" % (time.time() - time_)) - time_ = time.time() - - # for i in range(len(unclipped_offset)): - # csp_draw([unclipped_offset[i]], color = ["Green","Red","Blue"][i%3], width = .1) - # return [] - ############################################################################ - # Now to the clipping. - ############################################################################ - # First of all find all intersection's between all segments of all offseted subpaths, including self intersections. - - # TODO define offset tolerance here - global small_tolerance - small_tolerance = 0.01 - summ = 0 - summ1 = 0 - for subpath_i in xrange(csp_len): - for subpath_j in xrange(subpath_i, csp_len): - subpath = unclipped_offset[subpath_i] - subpath1 = unclipped_offset[subpath_j] - for i in xrange(1, len(subpath)): - # If subpath_i==subpath_j we are looking for self intersections, so - # we'll need search intersections only for xrange(i,len(subpath1)) - for j in (xrange(i, len(subpath1)) if subpath_i == subpath_j else xrange(len(subpath1))): - if subpath_i == subpath_j and j == i: - # Find self intersections of a segment - sp1, sp2, sp3 = csp_split(subpath[i - 1], subpath[i], .5) - intersections = csp_segments_intersection(sp1, sp2, sp2, sp3) - summ += 1 - for t in intersections: - summ1 += 1 - if not (small(t[0] - 1) and small(t[1])) and 0 <= t[0] <= 1 and 0 <= t[1] <= 1: - intersection[subpath_i] += [[i, t[0] / 2], [j, t[1] / 2 + .5]] - else: - intersections = csp_segments_intersection(subpath[i - 1], subpath[i], subpath1[j - 1], - subpath1[j]) - summ += 1 - for t in intersections: - summ1 += 1 - # TODO tolerance dependence to cpsp_length(t) - if len(t) == 2 and 0 <= t[0] <= 1 and 0 <= t[1] <= 1 and not ( - subpath_i == subpath_j and ( - (j - i - 1) % (len(subpath) - 1) == 0 and small(t[0] - 1) and small(t[1]) or - (i - j - 1) % (len(subpath) - 1) == 0 and small(t[1] - 1) and small(t[0]))): - intersection[subpath_i] += [[i, t[0]]] - intersection[subpath_j] += [[j, t[1]]] - # draw_pointer(csp_at_t(subpath[i-1],subpath[i],t[0]),"#f00") - # print_(t) - # print_(i,j) - elif len(t) == 5 and t[4] == "Overlap": - intersection[subpath_i] += [[i, t[0]], [i, t[1]]] - intersection[subpath_j] += [[j, t[1]], [j, t[3]]] - - print_("Intersections found in %s" % (time.time() - time_)) - print_("Examined %s segments" % (summ)) - print_("found %s intersections" % (summ1)) - time_ = time.time() - - ######################################################################## - # Split unclipped offset by intersection points into splitted_offset - ######################################################################## - splitted_offset = [] - for i in xrange(csp_len): - subpath = unclipped_offset[i] - if len(intersection[i]) > 0: - parts = csp_subpath_split_by_points(subpath, intersection[i]) - # Close parts list to close path (The first and the last parts are joined together) - if [1, 0.] not in intersection[i]: - parts[0][0][0] = parts[-1][-1][0] - parts[0] = csp_concat_subpaths(parts[-1], parts[0]) - splitted_offset += parts[:-1] - else: - splitted_offset += parts[:] - else: - splitted_offset += [subpath[:]] - - # for i in range(len(splitted_offset)): - # csp_draw([splitted_offset[i]], color = ["Green","Red","Blue"][i%3]) - print_("Splitted in %s" % (time.time() - time_)) - time_ = time.time() - - ######################################################################## - # Clipping - ######################################################################## - result = [] - for subpath_i in range(len(splitted_offset)): - clip = False - s1 = splitted_offset[subpath_i] - for subpath_j in range(len(splitted_offset)): - s2 = splitted_offset[subpath_j] - if (P(s1[0][1]) - P(s2[-1][1])).l2() < 0.0001 and ((subpath_i + 1) % len(splitted_offset) != subpath_j): - if dot(csp_normalized_normal(s2[-2], s2[-1], 1.), csp_normalized_slope(s1[0], s1[1], 0.)) * r < -0.0001: - clip = True - break - if (P(s2[0][1]) - P(s1[-1][1])).l2() < 0.0001 and ((subpath_j + 1) % len(splitted_offset) != subpath_i): - if dot(csp_normalized_normal(s2[0], s2[1], 0.), csp_normalized_slope(s1[-2], s1[-1], 1.)) * r > 0.0001: - clip = True - break - - if not clip: - result += [s1[:]] - elif options.offset_draw_clippend_path: - csp_draw([s1], color="Red", width=.1) - draw_pointer(csp_at_t(s2[-2], s2[-1], 1.) + - (P(csp_at_t(s2[-2], s2[-1], 1.)) + P( - csp_normalized_normal(s2[-2], s2[-1], 1.)) * 10).to_list(), "Green", "line") - draw_pointer(csp_at_t(s1[0], s1[1], 0.) + - (P(csp_at_t(s1[0], s1[1], 0.)) + P(csp_normalized_slope(s1[0], s1[1], 0.)) * 10).to_list(), - "Red", "line") - - # Now join all together and check closure and orientation of result - joined_result = csp_join_subpaths(result) - # Check if each subpath from joined_result is closed - # csp_draw(joined_result,color="Green",width=1) - - - for s in joined_result[:]: - if csp_subpaths_end_to_start_distance2(s, s) > 0.001: - # Remove open parts - if options.offset_draw_clippend_path: - csp_draw([s], color="Orange", width=1) - draw_pointer(s[0][1], comment=csp_subpaths_end_to_start_distance2(s, s)) - draw_pointer(s[-1][1], comment=csp_subpaths_end_to_start_distance2(s, s)) - joined_result.remove(s) - else: - # Remove small parts - minx, miny, maxx, maxy = csp_true_bounds([s]) - if (minx[0] - maxx[0]) ** 2 + (miny[1] - maxy[1]) ** 2 < 0.1: - joined_result.remove(s) - print_("Clipped and joined path in %s" % (time.time() - time_)) - time_ = time.time() - - ######################################################################## - # Now to the Dummy cliping: remove parts from splitted offset if their - # centers are closer to the original path than offset radius. - ######################################################################## - - r1, r2 = ((0.99 * r) ** 2, (1.01 * r) ** 2) if abs(r * .01) < 1 else ((abs(r) - 1) ** 2, (abs(r) + 1) ** 2) - for s in joined_result[:]: - dist = csp_to_point_distance(original_csp, s[int(len(s) / 2)][1], dist_bounds=[r1, r2], tolerance=.000001) - if not r1 < dist[0] < r2: - joined_result.remove(s) - if options.offset_draw_clippend_path: - csp_draw([s], comment=math.sqrt(dist[0])) - draw_pointer( - csp_at_t(csp[dist[1]][dist[2] - 1], csp[dist[1]][dist[2]], dist[3]) + s[int(len(s) / 2)][1], "blue", - "line", comment=[math.sqrt(dist[0]), i, j, sp]) - - print_("-----------------------------") - print_("Total offset time %s" % (time.time() - time_start)) - print_() - return joined_result ################################################################################ @@ -1885,13 +656,6 @@ def biarc(sp1, sp2, z1, z2, depth=0): [[P2.x, P2.y], 'arc', [R2.x, R2.y], a2, [P4.x, P4.y], [zm, z2]]] -def biarc_curve_segment_length(seg): - if seg[1] == "arc": - return math.sqrt((seg[0][0] - seg[2][0]) ** 2 + (seg[0][1] - seg[2][1]) ** 2) * seg[3] - elif seg[1] == "line": - return math.sqrt((seg[0][0] - seg[4][0]) ** 2 + (seg[0][1] - seg[4][1]) ** 2) - else: - return 0 ################################################################################ @@ -1902,129 +666,22 @@ class Polygon: self.polygon = [] if polygon == None else polygon[:] - def move(self, x, y): - for i in range(len(self.polygon)): - for j in range(len(self.polygon[i])): - self.polygon[i][j][0] += x - self.polygon[i][j][1] += y - def bounds(self): - minx, miny, maxx, maxy = 1e400, 1e400, -1e400, -1e400 - for poly in self.polygon: - for p in poly: - if minx > p[0]: minx = p[0] - if miny > p[1]: miny = p[1] - if maxx < p[0]: maxx = p[0] - if maxy < p[1]: maxy = p[1] - return minx * 1, miny * 1, maxx * 1, maxy * 1 - def width(self): - b = self.bounds() - return b[2] - b[0] - def rotate_(self, sin, cos): - for i in range(len(self.polygon)): - for j in range(len(self.polygon[i])): - x, y = self.polygon[i][j][0], self.polygon[i][j][1] - self.polygon[i][j][0] = x * cos - y * sin - self.polygon[i][j][1] = x * sin + y * cos - def rotate(self, a): - cos, sin = math.cos(a), math.sin(a) - self.rotate_(sin, cos) - def drop_into_direction(self, direction, surface): - # Polygon is a list of simple polygons - # Surface is a polygon + line y = 0 - # Direction is [dx,dy] - if len(self.polygon) == 0 or len(self.polygon[0]) == 0: return - if direction[0] ** 2 + direction[1] ** 2 < 1e-10: return - direction = normalize(direction) - sin, cos = direction[0], -direction[1] - self.rotate_(-sin, cos) - surface.rotate_(-sin, cos) - self.drop_down(surface, zerro_plane=False) - self.rotate_(sin, cos) - surface.rotate_(sin, cos) - def centroid(self): - centroids = [] - sa = 0 - for poly in self.polygon: - cx, cy, a = 0, 0, 0 - for i in range(len(poly)): - [x1, y1], [x2, y2] = poly[i - 1], poly[i] - cx += (x1 + x2) * (x1 * y2 - x2 * y1) - cy += (y1 + y2) * (x1 * y2 - x2 * y1) - a += (x1 * y2 - x2 * y1) - a *= 3. - if abs(a) > 0: - cx /= a - cy /= a - sa += abs(a) - centroids += [[cx, cy, a]] - if sa == 0: return [0., 0.] - cx, cy = 0., 0. - for c in centroids: - cx += c[0] * c[2] - cy += c[1] * c[2] - cx /= sa - cy /= sa - return [cx, cy] - def drop_down(self, surface, zerro_plane=True): - # Polygon is a list of simple polygons - # Surface is a polygon + line y = 0 - # Down means min y (0,-1) - if len(self.polygon) == 0 or len(self.polygon[0]) == 0: return - # Get surface top point - top = surface.bounds()[3] - if zerro_plane: top = max(0, top) - # Get polygon bottom point - bottom = self.bounds()[1] - self.move(0, top - bottom + 10) - # Now get shortest distance from surface to polygon in positive x=0 direction - # Such distance = min(distance(vertex, edge)...) where edge from surface and - # vertex from polygon and vice versa... - dist = 1e300 - for poly in surface.polygon: - for i in range(len(poly)): - for poly1 in self.polygon: - for i1 in range(len(poly1)): - st, end = poly[i - 1], poly[i] - vertex = poly1[i1] - if st[0] <= vertex[0] <= end[0] or end[0] <= vertex[0] <= st[0]: - if st[0] == end[0]: - d = min(vertex[1] - st[1], vertex[1] - end[1]) - else: - d = vertex[1] - st[1] - (end[1] - st[1]) * (vertex[0] - st[0]) / (end[0] - st[0]) - if dist > d: dist = d - # and vice versa just change the sign because vertex now under the edge - st, end = poly1[i1 - 1], poly1[i1] - vertex = poly[i] - if st[0] <= vertex[0] <= end[0] or end[0] <= vertex[0] <= st[0]: - if st[0] == end[0]: - d = min(- vertex[1] + st[1], -vertex[1] + end[1]) - else: - d = - vertex[1] + st[1] + (end[1] - st[1]) * (vertex[0] - st[0]) / (end[0] - st[0]) - if dist > d: dist = d - - if zerro_plane and dist > 10 + top: dist = 10 + top - # print_(dist, top, bottom) - # self.draw() - self.move(0, -dist) - def draw(self, color="#075", width=.1): - for poly in self.polygon: - csp_draw([csp_subpath_line_to([], poly + [poly[0]])], color=color, width=width) def add(self, add): @@ -2034,174 +691,8 @@ class Polygon: self.polygon += add.polygon[:] - def point_inside(self, p): - inside = False - for poly in self.polygon: - for i in range(len(poly)): - st, end = poly[i - 1], poly[i] - if p == st or p == end: return True # point is a vertex = point is on the edge - if st[0] > end[0]: st, end = end, st # This will be needed to check that edge if open only at rigth end - c = (p[1] - st[1]) * (end[0] - st[0]) - (end[1] - st[1]) * (p[0] - st[0]) - # print_(c) - if st[0] <= p[0] < end[0]: - if c < 0: - inside = not inside - elif c == 0: - return True # point is on the edge - elif st[0] == end[0] == p[0] and ( - st[1] <= p[1] <= end[1] or end[1] <= p[1] <= st[1]): # point is on the edge - return True - return inside - def hull(self): - # Add vertices at all self intersection points. - hull = [] - for i1 in range(len(self.polygon)): - poly1 = self.polygon[i1] - poly_ = [] - for j1 in range(len(poly1)): - s, e = poly1[j1 - 1], poly1[j1] - poly_ += [s] - - # Check self intersections - for j2 in range(j1 + 1, len(poly1)): - s1, e1 = poly1[j2 - 1], poly1[j2] - int_ = line_line_intersection_points(s, e, s1, e1) - for p in int_: - if point_to_point_d2(p, s) > 0.000001 and point_to_point_d2(p, e) > 0.000001: - poly_ += [p] - # Check self intersections with other polys - for i2 in range(len(self.polygon)): - if i1 == i2: continue - poly2 = self.polygon[i2] - for j2 in range(len(poly2)): - s1, e1 = poly2[j2 - 1], poly2[j2] - int_ = line_line_intersection_points(s, e, s1, e1) - for p in int_: - if point_to_point_d2(p, s) > 0.000001 and point_to_point_d2(p, e) > 0.000001: - poly_ += [p] - hull += [poly_] - # Create the dictionary containing all edges in both directions - edges = {} - for poly in self.polygon: - for i in range(len(poly)): - s, e = tuple(poly[i - 1]), tuple(poly[i]) - if (point_to_point_d2(e, s) < 0.000001): continue - break_s, break_e = False, False - for p in edges: - if point_to_point_d2(p, s) < 0.000001: - break_s = True - s = p - if point_to_point_d2(p, e) < 0.000001: - break_e = True - e = p - if break_s and break_e: break - l = point_to_point_d(s, e) - if not break_s and not break_e: - edges[s] = [[s, e, l]] - edges[e] = [[e, s, l]] - # draw_pointer(s+e,"red","line") - # draw_pointer(s+e,"red","line") - else: - if e in edges: - for edge in edges[e]: - if point_to_point_d2(edge[1], s) < 0.000001: - break - if point_to_point_d2(edge[1], s) > 0.000001: - edges[e] += [[e, s, l]] - # draw_pointer(s+e,"red","line") - - else: - edges[e] = [[e, s, l]] - # draw_pointer(s+e,"green","line") - if s in edges: - for edge in edges[s]: - if point_to_point_d2(edge[1], e) < 0.000001: - break - if point_to_point_d2(edge[1], e) > 0.000001: - edges[s] += [[s, e, l]] - # draw_pointer(s+e,"red","line") - else: - edges[s] = [[s, e, l]] - # draw_pointer(s+e,"green","line") - - - def angle_quadrant(sin, cos): - # quadrants are (0,pi/2], (pi/2,pi], (pi,3*pi/2], (3*pi/2, 2*pi], i.e. 0 is in the 4-th quadrant - if sin > 0 and cos >= 0: return 1 - if sin >= 0 and cos < 0: return 2 - if sin < 0 and cos <= 0: return 3 - if sin <= 0 and cos > 0: return 4 - - - def angle_is_less(sin, cos, sin1, cos1): - # 0 = 2*pi is the largest angle - if [sin1, cos1] == [0, 1]: return True - if [sin, cos] == [0, 1]: return False - if angle_quadrant(sin, cos) > angle_quadrant(sin1, cos1): - return False - if angle_quadrant(sin, cos) < angle_quadrant(sin1, cos1): - return True - if sin >= 0 and cos > 0: return sin < sin1 - if sin > 0 and cos <= 0: return sin > sin1 - if sin <= 0 and cos < 0: return sin > sin1 - if sin < 0 and cos >= 0: return sin < sin1 - - - def get_closes_edge_by_angle(edges, last): - # Last edge is normalized vector of the last edge. - min_angle = [0, 1] - next = last - last_edge = [(last[0][0] - last[1][0]) / last[2], (last[0][1] - last[1][1]) / last[2]] - for p in edges: - # draw_pointer(list(p[0])+[p[0][0]+last_edge[0]*40,p[0][1]+last_edge[1]*40], "Red", "line", width=1) - # print_("len(edges)=",len(edges)) - cur = [(p[1][0] - p[0][0]) / p[2], (p[1][1] - p[0][1]) / p[2]] - cos, sin = dot(cur, last_edge), cross(cur, last_edge) - # draw_pointer(list(p[0])+[p[0][0]+cur[0]*40,p[0][1]+cur[1]*40], "Orange", "line", width=1, comment = [sin,cos]) - # print_("cos, sin=",cos,sin) - # print_("min_angle_before=",min_angle) - - if angle_is_less(sin, cos, min_angle[0], min_angle[1]): - min_angle = [sin, cos] - next = p - # print_("min_angle=",min_angle) - - return next - - # Join edges together into new polygon cutting the vertexes inside new polygon - self.polygon = [] - len_edges = sum([len(edges[p]) for p in edges]) - loops = 0 - - while len(edges) > 0: - poly = [] - if loops > len_edges: raise ValueError, "Hull error" - loops += 1 - # Find left most vertex. - start = (1e100, 1) - for edge in edges: - start = min(start, min(edges[edge])) - last = [(start[0][0] - 1, start[0][1]), start[0], 1] - first_run = True - loops1 = 0 - while (last[1] != start[0] or first_run): - first_run = False - if loops1 > len_edges: raise ValueError, "Hull error" - loops1 += 1 - next = get_closes_edge_by_angle(edges[last[1]], last) - # draw_pointer(next[0]+next[1],"Green","line", comment=i, width= 1) - # print_(next[0],"-",next[1]) - - last = next - poly += [list(last[0])] - self.polygon += [poly] - # Remove all edges that are intersects new poly (any vertex inside new poly) - poly_ = Polygon([poly]) - for p in edges.keys()[:]: - if poly_.point_inside(list(p)): del edges[p] - self.draw(color="Green", width=1) class Arangement_Genetic: @@ -2218,156 +709,18 @@ class Arangement_Genetic: self.move_mutate_factor = 1. - def add_random_species(self, count): - for i in range(count): - specimen = [] - order = range(self.genes_count) - random.shuffle(order) - for j in order: - specimen += [[j, random.random(), random.random()]] - self.population += [[None, specimen]] - def species_distance2(self, sp1, sp2): - # retun distance, each component is normalized - s = 0 - for j in range(self.genes_count): - s += ((sp1[j][0] - sp2[j][0]) / self.genes_count) ** 2 + ((sp1[j][1] - sp2[j][1])) ** 2 + ( - (sp1[j][2] - sp2[j][2])) ** 2 - return s - - def leave_top_species(self, count): - self.population.sort() - res = [copy.deepcopy(self.population[0])] - del self.population[0] - for i in range(count - 1): - t = [] - for j in range(20): - i1 = random.randint(0, len(self.population) - 1) - t += [[self.population[i1][0], i1]] - t.sort() - res += [copy.deepcopy(self.population[t[0][1]])] - del self.population[t[0][1]] - self.population = res - # del self.population[0] - # for c in range(count-1) : - # rank = [] - # for i in range(len(self.population)) : - # sim = self.similarity(self.population[i][1],res) - # rank += [ [self.population[i][0] / sim if sim>0 else 1e100,i] ] - # rank.sort() - # res += [ copy.deepcopy(self.population[rank[0][1]]) ] - # print_(rank[0],self.population[rank[0][1]][0]) - # print_(res[-1]) - # del self.population[rank[0][1]] - - self.population = res - def populate_species(self, count, parent_count): - self.population.sort() - self.inc = 0 - for c in range(count): - parent1 = random.randint(0, parent_count - 1) - parent2 = random.randint(0, parent_count - 1) - if parent1 == parent2: parent2 = (parent2 + 1) % parent_count - parent1, parent2 = self.population[parent1][1], self.population[parent2][1] - i1, i2 = 0, 0 - genes_order = [] - specimen = [[0, 0., 0.] for i in range(self.genes_count)] - - self.incest_mutation_multiplyer = 1. - self.incest_mutation_count_multiplyer = 1. - - if self.species_distance2(parent1, parent2) <= .01 / self.genes_count: - # OMG it's a incest :O!!! - # Damn you bastards! - self.inc += 1 - self.incest_mutation_multiplyer = 2. - self.incest_mutation_count_multiplyer = 2. - else: - if random.random() < .01: print_(self.species_distance2(parent1, parent2)) - start_gene = random.randint(0, self.genes_count) - end_gene = (max(1, random.randint(0, self.genes_count), - int(self.genes_count / 4)) + start_gene) % self.genes_count - if end_gene < start_gene: - end_gene, start_gene = start_gene, end_gene - parent1, parent2 = parent2, parent1 - for i in range(start_gene, end_gene): - # rotation_mutate_param = random.random()/100 - # xposition_mutate_param = random.random()/100 - tr = 1. # - rotation_mutate_param - tp = 1. # - xposition_mutate_param - specimen[i] = [parent1[i][0], parent1[i][1] * tr + parent2[i][1] * (1 - tr), - parent1[i][2] * tp + parent2[i][2] * (1 - tp)] - genes_order += [parent1[i][0]] - - for i in range(0, start_gene) + range(end_gene, self.genes_count): - tr = 0. # rotation_mutate_param - tp = 0. # xposition_mutate_param - j = i - while parent2[j][0] in genes_order: - j = (j + 1) % self.genes_count - specimen[i] = [parent2[j][0], parent1[i][1] * tr + parent2[i][1] * (1 - tr), - parent1[i][2] * tp + parent2[i][2] * (1 - tp)] - genes_order += [parent2[j][0]] - - for i in range(random.randint(self.mutation_genes_count[0], - self.mutation_genes_count[0] * self.incest_mutation_count_multiplyer)): - if random.random() < self.order_mutate_factor * self.incest_mutation_multiplyer: - i1, i2 = random.randint(0, self.genes_count - 1), random.randint(0, self.genes_count - 1) - specimen[i1][0], specimen[i2][0] = specimen[i2][0], specimen[i1][0] - if random.random() < self.move_mutation_factor * self.incest_mutation_multiplyer: - i1 = random.randint(0, self.genes_count - 1) - specimen[i1][1] = (specimen[i1][ - 1] + random.random() * math.pi2 * self.move_mutation_multiplier) % 1. - specimen[i1][2] = (specimen[i1][2] + random.random() * self.move_mutation_multiplier) % 1. - self.population += [[None, specimen]] - def test_spiece_drop_down(self, spiece): - surface = Polygon() - for p in spiece: - time_ = time.time() - poly = Polygon(copy.deepcopy(self.polygons[p[0]].polygon)) - poly.rotate(p[1] * math.pi2) - w = poly.width() - left = poly.bounds()[0] - poly.move(-left + (self.width - w) * p[2], 0) - poly.drop_down(surface) - surface.add(poly) - return surface - def test(self, test_function): - for i in range(len(self.population)): - if self.population[i][0] == None: - surface = test_function(self.population[i][1]) - b = surface.bounds() - self.population[i][0] = (b[3] - b[1]) * (b[2] - b[0]) - self.population.sort() - def test_spiece_centroid(self, spiece): - poly = Polygon(copy.deepcopy(self.polygons[spiece[0][0]].polygon)) - poly.rotate(spiece[0][2] * math.pi2) - surface = Polygon(poly.polygon) - i = 0 - for p in spiece[1:]: - i += 1 - poly = Polygon(copy.deepcopy(self.polygons[p[0]].polygon)) - poly.rotate(p[2] * math.pi2) - c = surface.centroid() - c1 = poly.centroid() - direction = [math.cos(p[1] * math.pi2), -math.sin(p[1] * math.pi2)] - poly.move(c[0] - c1[0] - direction[0] * 100, c[1] - c1[1] - direction[1] * 100) - poly.drop_into_direction(direction, surface) - surface.add(poly) - return surface - # surface.draw() - ################################################################################ ### @@ -2633,11 +986,6 @@ class laser_gcode(inkex.Effect): r += s[i] + ("%f" % (round(c[i], 4))).rstrip('0') return r - def calculate_angle(a, current_a): - return min( - [abs(a - current_a % math.pi2 + math.pi2), a + current_a - current_a % math.pi2 + math.pi2], - [abs(a - current_a % math.pi2 - math.pi2), a + current_a - current_a % math.pi2 - math.pi2], - [abs(a - current_a % math.pi2), a + current_a - current_a % math.pi2])[1] if len(curve) == 0: return ""