def test_cubic_cubic(self): # q1 = Bezier(10,100, 90,30, 40,140, 220,220) # q2 = Bezier(5,150, 180,20, 80,250, 210,190) # console.log(q1.intersects(q2)) q1 = CubicBezier(Point(10, 100), Point(90, 30), Point(40, 140), Point(220, 220)) q2 = CubicBezier(Point(5, 150), Point(180, 20), Point(80, 250), Point(210, 190)) i = q1.intersections(q2) # self.assertEqual(len(i),3) # self.assertAlmostEqual(i[0].point.x,81.7904225873) # self.assertAlmostEqual(i[0].point.y,109.899396337) # self.assertAlmostEqual(i[1].point.x,133.186831292) # self.assertAlmostEqual(i[1].point.y,167.148173322) # self.assertAlmostEqual(i[2].point.x,179.869157678) # self.assertAlmostEqual(i[2].point.y,199.661989162) import matplotlib.pyplot as plt fig, ax = plt.subplots() path = BezierPath() path.closed = False path.activeRepresentation = SegmentRepresentation(path, [q1]) path.plot(ax) path.activeRepresentation = SegmentRepresentation(path, [q2]) path.plot(ax) for n in i: circle = plt.Circle((n.point.x, n.point.y), 2, fill=True, color="red") ax.add_artist(circle)
def test_cubic_line(self): q = CubicBezier(Point(100, 240), Point(30, 60), Point(210, 230), Point(160, 30)) l = Line(Point(25, 260), Point(230, 20)) path = BezierPath() path.closed = False path.activeRepresentation = SegmentRepresentation(path, [q]) i = q.intersections(l) self.assertEqual(len(i), 3) self.assertEqual(i[0].point, q.pointAtTime(0.117517031451)) self.assertEqual(i[1].point, q.pointAtTime(0.518591792307)) self.assertEqual(i[2].point, q.pointAtTime(0.867886610031))
def test_extremes(self): q = CubicBezier( Point(65,59), Point(194,90), Point(220,260), Point(70,261) ) # console.log(Bezier(65,59, 194,90, 220,260, 70,261).extrema()) r = q.findExtremes() self.assertEqual(len(r), 1) self.assertAlmostEqual(r[0], 0.5275787707261016) r = q.findExtremes(inflections = True) self.assertEqual(len(r), 2) self.assertAlmostEqual(r[0], 0.4512987012987013) self.assertAlmostEqual(r[1], 0.5275787707261016)
def test_addextremes(self): q = CubicBezier(Point(42, 135), Point(129, 242), Point(167, 77), Point(65, 59)) ex = q.findExtremes() self.assertEqual(len(ex), 2) path = BezierPath() path.closed = False path.activeRepresentation = SegmentRepresentation(path, [q]) path.addExtremes() path.balance() segs = path.asSegments() self.assertEqual(len(segs), 3)
def test_align(self): q = CubicBezier( Point(120,160), Point(35,200), Point(220,260), Point(220,40) ) s = q.aligned() self.assertAlmostEqual(s[0].x,0.0) self.assertAlmostEqual(s[0].y,0.0) self.assertAlmostEqual(s[1].x,-85.14452515537582) self.assertAlmostEqual(s[1].y,-39.69143277919774) self.assertAlmostEqual(s[2].x,-12.803687993289572) self.assertAlmostEqual(s[2].y,140.84056792618557) self.assertAlmostEqual(s[3].x,156.2049935181331) self.assertAlmostEqual(s[3].y,0.0)
def test_loop(self): q = CubicBezier( Point(171,272), Point(388,249), Point(167,444), Point(388,176) ) self.assertTrue(not q.hasLoop) q = CubicBezier( Point(171,272), Point(595,249), Point(167,444), Point(388,176) ) roots = q.hasLoop p1 = q.pointAtTime(roots[0]) p2 = q.pointAtTime(roots[1]) self.assertTrue(q.hasLoop) self.assertEqual(p1,p2)
def cubic_beziers_from_arc(r, phi, flagA, flagS, p1, p2): #======================================================== r_abs = tuple2(abs(r.x), abs(r.y)) d = tuple2((p1.x - p2.x), (p1.y - p2.y)) p = tuple2( math.cos(phi) * d.x / 2 + math.sin(phi) * d.y / 2, -math.sin(phi) * d.x / 2 + math.cos(phi) * d.y / 2) p_sq = tuple2(p.x**2, p.y**2) r_sq = tuple2(r_abs.x**2, r_abs.y**2) ratio = p_sq.x / r_sq.x + p_sq.y / r_sq.y if ratio > 1: scale = math.sqrt(ratio) r_abs = tuple2(scale * r_abs.x, scale * r_abs.y) r_sq = tuple2(r_abs.x**2, r_abs.y**2) dq = r_sq.x * p_sq.y + r_sq.y * p_sq.x pq = (r_sq.x * r_sq.y - dq) / dq q = math.sqrt(max(0, pq)) if flagA == flagS: q = -q cp = tuple2(q * r_abs.x * p.y / r_abs.y, -q * r_abs.y * p.x / r_abs.x) c = tuple2( cp.x * math.cos(phi) - cp.y * math.sin(phi) + (p1.x + p2.x) / 2.0, cp.x * math.sin(phi) + cp.y * math.cos(phi) + (p1.y + p2.y) / 2.0) lambda1 = svg_angle(tuple2(1, 0), tuple2((p.x - cp.x) / r_abs.x, (p.y - cp.y) / r_abs.y)) delta = svg_angle(tuple2((p.x - cp.x) / r_abs.x, (p.y - cp.y) / r_abs.y), tuple2((-p.x - cp.x) / r_abs.x, (-p.y - cp.y) / r_abs.y)) delta = delta - 2 * math.pi * math.floor(delta / (2 * math.pi)) if not flagS: delta -= 2 * math.pi lambda2 = lambda1 + delta t = lambda1 dt = math.pi / 4 curves = [] while (t + dt) < lambda2: control_points = (BezierPoint( *cp) for cp in cubic_bezier_control_points(c, r_abs, phi, t, t + dt)) curves.append(CubicBezier(*control_points)) t += dt control_points = (BezierPoint( *cp) for cp in cubic_bezier_control_points(c, r_abs, phi, t, lambda2)) curves.append(CubicBezier(*(tuple(control_points)[:3]), BezierPoint(*p2))) return curves
def test_cubic_cubic(self): # q1 = Bezier(10,100, 90,30, 40,140, 220,220) # q2 = Bezier(5,150, 180,20, 80,250, 210,190) # console.log(q1.intersects(q2)) q1 = CubicBezier(Point(10, 100), Point(90, 30), Point(40, 140), Point(220, 220)) q2 = CubicBezier(Point(5, 150), Point(180, 20), Point(80, 250), Point(210, 190)) i = q1.intersections(q2) self.assertEqual(len(i), 3) self.assertAlmostEqual(i[0].point.x, 81.7904225873) self.assertAlmostEqual(i[0].point.y, 109.899396337) self.assertAlmostEqual(i[1].point.x, 133.186831292) self.assertAlmostEqual(i[1].point.y, 167.148173322) self.assertAlmostEqual(i[2].point.x, 179.869157678) self.assertAlmostEqual(i[2].point.y, 199.661989162)
def test_cubic_line_2(self): s1 = CubicBezier.fromRepr( "B<<584.0,126.03783241124995>-<402.0,163.0378324112499>-<220.00000000000003,200.03783241124995>-<38.0,237.03783241124995>>" ), ray = Line.fromRepr( "L<<357.4,-9.99999999999999>--<357.6,250.2692949284206>>") assert (s1[0].intersections(ray))
def join_beziers_in_region(last_bezier, join_region, next_bezier): #================================================================= last_bezier = trim_bezier_to_region_edge(last_bezier.asSegments()[-1], join_region, start=False) next_bezier = trim_bezier_to_region_edge(next_bezier.asSegments()[0], join_region, start=True) # now want intersection of derivative lines intersection = line_intersection(last_bezier.points[2:4], next_bezier.points[0:2]) intersection = BezierPoint(*intersection) #join_bezier = CubicBezier(last_bezier.points[3], intersection, intersection, next_bezier.points[0]) ## What if intersection is outside of join_region? Or even not near the region's centre?? region_centre = BezierPoint(*join_region.centroid.coords[0]) join_bezier = CubicBezier(last_bezier.points[3], region_centre, region_centre, next_bezier.points[0]) ## Adjust derivatives at end/start of last/next bezier to have same magnitudes ## as join_bezier's start/end derivative (slope will already match so simply set as ## last_bezier.points[2] = (last_bezier.points[3] - (intersection - last_bezier.points[3])) ## etc... return (last_bezier, join_bezier, next_bezier)
def Ellipse(x_radius, y_radius, origin=None, superness=CIRCULAR_SUPERNESS): """Returns a path representing an ellipse of given x and y radii. You can specify the `origin` as a Point and the `superness` of the ellipse.""" if not origin: origin = Point(0, 0) w = origin + west * x_radius e = origin + east * x_radius n = origin + north * y_radius s = origin + south * y_radius w_n = CubicBezier(w, w + north * y_radius * superness, n + west * x_radius * superness, n) n_e = CubicBezier(n, n + east * x_radius * superness, e + north * y_radius * superness, e) e_s = CubicBezier(e, e + south * y_radius * superness, s + east * x_radius * superness, s) s_w = CubicBezier(s, s + west * x_radius * superness, w + south * y_radius * superness, w) return BezierPath.fromSegments([w_n, n_e, e_s, s_w])
def appendSegment(self, seg): seg = [Point(n[0], n[1]) for n in seg] if len(seg) == 2: self.segments.append(Line(*seg)) elif len(seg) == 3: self.segments.append(QuadraticBezier(*seg)) elif len(seg) == 4: self.segments.append(CubicBezier(*seg)) else: raise ValueError("Unknown segment type")
def bezier_paths_from_arc_endpoints(r, phi, flagA, flagS, p1, p2, T): #==================================================================== arc = arc_endpoints_to_centre(r, phi, flagA, flagS, p1, p2) end_theta = arc.theta + arc.delta_theta t = arc.theta dt = math.pi / 4 segments = [] while (t + dt) < end_theta: control_points = (BezierPoint(*T.transform_point(cp)) for cp in cubic_bezier_control_points( arc.centre, arc.radii, phi, t, t + dt)) segments.append(CubicBezier(*control_points)) t += dt control_points = (BezierPoint(*T.transform_point(cp)) for cp in cubic_bezier_control_points( arc.centre, arc.radii, phi, t, end_theta)) segments.append( CubicBezier(*(tuple(control_points)[:3]), BezierPoint(*T.transform_point(p2)))) path = BezierPath.fromSegments(segments) path.closed = False return path
def fitLine(self, data, tHat1, tHat2): p0, p3 = data[0], data[-1] dist = p0.distanceFrom(p3) / 3.0 if tHat1: p1 = p0 + tHat1 * dist else: p1 = ((p0 * 2.0) + p3) / 3.0 if tHat2: p2 = p3 + tHat2 * dist else: p2 = ((p3 * 2.0) + p0) / 3.0 return CubicBezier(p0, p1, p2, p3)
def estimateLengths(self, data, u, tHat1, tHat2): C = [ [ 0.0, 0.0], [ 0.0, 0.0 ]] X = [ 0.0, 0.0 ] # bez = SCBezier() for (coeff,datum) in zip(u, data): (b0,b1,b2,b3) = (B0(coeff), B1(coeff), B2(coeff), B3(coeff)) (a1, a2) = (tHat1 * b1, tHat2 * b2) C[0][0] += a1.__matmul__(a1) C[0][1] += a1.__matmul__(a2) C[1][0] = C[0][1] C[1][1] += a2.__matmul__(a2) s1 = data[0] * (b0+b1) shortfall = datum - s1 shortfall = shortfall - (data[-1] * (b2+b3)) X[0] += a1.__matmul__(shortfall) X[1] += a2.__matmul__(shortfall) det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1] if det_C0_C1 != 0.0: det_C0_X = C[0][0] * X[1] - C[0][1] * X[0] det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1] alpha_l = det_X_C1 / det_C0_C1 alpha_r = det_C0_X / det_C0_C1 else: c0 = C[0][0] + C[0][1] if c0 != 0: alpha_l = X[0] / c0 alpha_r = X[0] / c0 else: alpha_l = 0.0 alpha_r = 0.0 if alpha_l < 1.0e-6 or alpha_r < 1.0e-6: alpha_l = data[-1].distanceFrom(data[0]) / 3.0 alpha_r = alpha_l return CubicBezier(data[0], tHat1 * alpha_l + data[0], tHat2 * alpha_r + data[-1], data[-1])
def toCubicBezier(self): """Converts the quadratic bezier to a CubicBezier""" from beziers.cubicbezier import CubicBezier return CubicBezier(self[0], self[0] * (1 / 3.) + self[1] * (2 / 3.), self[1] * (2 / 3.) + self[2] * (1 / 3.), self[2])
def __get_geometry(self, shape, properties, transform): #====================================================== ## ## Returns shape's geometry as `shapely` object. ## coordinates = [] bezier_segments = [] pptx_geometry = Geometry(shape) for path in pptx_geometry.path_list: bbox = (shape.width, shape.height) if path.w is None or path.h is None else ( path.w, path.h) T = transform @ DrawMLTransform(shape, bbox) moved = False first_point = None current_point = None closed = False for c in path.getchildren(): if c.tag == DML('arcTo'): (wR, hR) = ((pptx_geometry.attrib_value(c, 'wR'), pptx_geometry.attrib_value(c, 'hR'))) stAng = radians(pptx_geometry.attrib_value(c, 'stAng')) swAng = radians(pptx_geometry.attrib_value(c, 'swAng')) p1 = ellipse_point(wR, hR, stAng) p2 = ellipse_point(wR, hR, stAng + swAng) pt = (current_point[0] - p1[0] + p2[0], current_point[1] - p1[1] + p2[1]) large_arc_flag = 1 if swAng >= math.pi else 0 paths = bezier_paths_from_arc_endpoints( tuple2(wR, hR), 0, large_arc_flag, 1, tuple2(*current_point), tuple2(*pt), T) bezier_segments.extend(paths.asSegments()) coordinates.extend(bezier_sample(paths)) current_point = pt elif c.tag == DML('close'): if first_point is not None and current_point != first_point: coordinates.append(T.transform_point(first_point)) closed = True first_point = None # Close current pptx_geometry and start a new one... elif c.tag == DML('cubicBezTo'): coords = [BezierPoint(*T.transform_point(current_point))] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*T.transform_point(pt))) current_point = pt bz = CubicBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) elif c.tag == DML('lnTo'): pt = pptx_geometry.point(c.pt) if moved: coordinates.append(T.transform_point(current_point)) moved = False coordinates.append(T.transform_point(pt)) current_point = pt elif c.tag == DML('moveTo'): pt = pptx_geometry.point(c.pt) if first_point is None: first_point = pt current_point = pt moved = True elif c.tag == DML('quadBezTo'): coords = [BezierPoint(*T.transform_point(current_point))] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*T.transform_point(pt))) current_point = pt bz = QuadraticBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) else: print('Unknown path element: {}'.format(c.tag)) if settings.get('saveBeziers', False) and len(bezier_segments) > 0: properties['bezier-segments'] = [ repr(bz) for bz in bezier_segments ] if closed: geometry = shapely.geometry.Polygon(coordinates) else: geometry = shapely.geometry.LineString(coordinates) if properties.get('closed', False): # Return a polygon if flagged as `closed` coordinates.append(coordinates[0]) return shapely.geometry.Polygon(coordinates) return geometry
def __get_geometry(self, element, properties, transform): #======================================================= ## ## Returns path element as a `shapely` object. ## coordinates = [] bezier_segments = [] moved = False first_point = None current_point = None closed = False path_tokens = [] T = transform@SVGTransform(element.attrib.get('transform')) if element.tag == SVG_NS('path'): path_tokens = list(parse_svg_path(element.attrib.get('d', ''))) elif element.tag == SVG_NS('rect'): x = length_as_pixels(element.attrib.get('x', 0)) y = length_as_pixels(element.attrib.get('y', 0)) width = length_as_pixels(element.attrib.get('width', 0)) height = length_as_pixels(element.attrib.get('height', 0)) rx = length_as_pixels(element.attrib.get('rx')) ry = length_as_pixels(element.attrib.get('ry')) if width == 0 or height == 0: return None if rx is None and ry is None: rx = ry = 0 elif ry is None: ry = rx elif rx is None: rx = ry rx = min(rx, width/2) ry = min(ry, height/2) if rx == 0 and ry == 0: path_tokens = ['M', x, y, 'H', x+width, 'V', y+height, 'H', x, 'V', y, 'Z'] else: path_tokens = ['M', x+rx, y, 'H', x+width-rx, 'A', rx, ry, 0, 0, 1, x+width, y+ry, 'V', y+height-ry, 'A', rx, ry, 0, 0, 1, x+width-rx, y+height, 'H', x+rx, 'A', rx, ry, 0, 0, 1, x, y+height-ry, 'V', y+ry, 'A', rx, ry, 0, 0, 1, x+rx, y, 'Z'] elif element.tag == SVG_NS('line'): x1 = length_as_pixels(element.attrib.get('x1', 0)) y1 = length_as_pixels(element.attrib.get('y1', 0)) x2 = length_as_pixels(element.attrib.get('x2', 0)) y2 = length_as_pixels(element.attrib.get('y2', 0)) path_tokens = ['M', x1, y1, x2, y2] elif element.tag == SVG_NS('polyline'): points = element.attrib.get('points', '').replace(',', ' ').split() path_tokens = ['M'] + points elif element.tag == SVG_NS('polygon'): points = element.attrib.get('points', '').replace(',', ' ').split() path_tokens = ['M'] + points + ['Z'] elif element.tag == SVG_NS('circle'): cx = length_as_pixels(element.attrib.get('cx', 0)) cy = length_as_pixels(element.attrib.get('cy', 0)) r = length_as_pixels(element.attrib.get('r', 0)) if r == 0: return None path_tokens = ['M', cx+r, cy, 'A', r, r, 0, 0, 0, cx, cy-r, 'A', r, r, 0, 0, 0, cx-r, cy, 'A', r, r, 0, 0, 0, cx, cy+r, 'A', r, r, 0, 0, 0, cx+r, cy, 'Z'] elif element.tag == SVG_NS('ellipse'): cx = length_as_pixels(element.attrib.get('cx', 0)) cy = length_as_pixels(element.attrib.get('cy', 0)) rx = length_as_pixels(element.attrib.get('rx', 0)) ry = length_as_pixels(element.attrib.get('ry', 0)) if rx == 0 or ry == 0: return None path_tokens = ['M', cx+rx, cy, 'A', rx, ry, 0, 0, 0, cx, cy-ry, 'A', rx, ry, 0, 0, 0, cx-rx, cy, 'A', rx, ry, 0, 0, 0, cx, cy+ry, 'A', rx, ry, 0, 0, 0, cx+rx, cy, 'Z'] elif element.tag == SVG_NS('image'): if 'id' in properties or 'class' in properties: width = length_as_pixels(element.attrib.get('width', 0)) height = length_as_pixels(element.attrib.get('height', 0)) path_tokens = ['M', 0, 0, 'H', width, 'V', height, 'H', 0, 'V', 0, 'Z'] pos = 0 while pos < len(path_tokens): if isinstance(path_tokens[pos], str) and path_tokens[pos].isalpha(): cmd = path_tokens[pos] pos += 1 # Else repeat previous command with new coordinates # with `moveTo` becoming `lineTo` elif cmd == 'M': cmd = 'L' elif cmd == 'm': cmd = 'l' if cmd not in ['s', 'S']: second_cubic_control = None if cmd not in ['t', 'T']: second_quad_control = None if cmd in ['a', 'A']: params = [float(x) for x in path_tokens[pos:pos+7]] pos += 7 pt = params[5:7] if cmd == 'a': pt[0] += current_point[0] pt[1] += current_point[1] phi = radians(params[2]) path = bezier_path_from_arc_endpoints(tuple2(*params[0:2]), phi, *params[3:5], tuple2(*current_point), tuple2(*pt), T) bezier_segments.extend(path.asSegments()) coordinates.extend(bezier_sample(path)) current_point = pt elif cmd in ['c', 'C', 's', 'S']: coords = [BezierPoint(*T.transform_point(current_point))] if cmd in ['c', 'C']: n_params = 6 else: n_params = 4 if second_cubic_control is None: coords.append(BezierPoint(*T.transform_point(current_point))) else: coords.append(BezierPoint(*T.transform_point( reflect_point(second_cubic_control, current_point)))) params = [float(x) for x in path_tokens[pos:pos+n_params]] pos += n_params for n in range(0, n_params, 2): pt = params[n:n+2] if cmd.islower(): pt[0] += current_point[0] pt[1] += current_point[1] if n == (n_params - 4): second_cubic_control = pt coords.append(BezierPoint(*T.transform_point(pt))) bz = CubicBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) current_point = pt elif cmd in ['l', 'L', 'h', 'H', 'v', 'V']: if cmd in ['l', 'L']: params = [float(x) for x in path_tokens[pos:pos+2]] pos += 2 pt = params[0:2] if cmd == 'l': pt[0] += current_point[0] pt[1] += current_point[1] else: param = float(path_tokens[pos]) pos += 1 if cmd == 'h': param += current_point[0] elif cmd == 'v': param += current_point[1] if cmd in ['h', 'H']: pt = [param, current_point[1]] else: pt = [current_point[0], param] if moved: coordinates.append(T.transform_point(current_point)) moved = False coordinates.append(T.transform_point(pt)) current_point = pt elif cmd in ['m', 'M']: params = [float(x) for x in path_tokens[pos:pos+2]] pos += 2 pt = params[0:2] if first_point is None: # First `m` in a path is treated as `M` first_point = pt else: if cmd == 'm': pt[0] += current_point[0] pt[1] += current_point[1] current_point = pt moved = True elif cmd in ['q', 'Q', 't', 'T']: coords = [BezierPoint(*T.transform_point(current_point))] if cmd in ['q', 'Q']: n_params = 4 else: n_params = 2 if second_quad_control is None: coords.append(BezierPoint(*T.transform_point(current_point))) else: coords.append(BezierPoint(*T.transform_point( reflect_point(second_quad_control, current_point)))) params = [float(x) for x in path_tokens[pos:pos+n_params]] pos += n_params for n in range(0, n_params, 2): pt = params[n:n+2] if cmd.islower(): pt[0] += current_point[0] pt[1] += current_point[1] if n == (n_params - 4): second_quad_control = pt coords.append(BezierPoint(*T.transform_point(pt))) bz = QuadraticBezier(*coords) bezier_segments.append(bz) coordinates.extend(bezier_sample(bz)) current_point = pt elif cmd in ['z', 'Z']: if first_point is not None and current_point != first_point: coordinates.append(T.transform_point(first_point)) closed = True first_point = None else: log.warn('Unknown path command: {}'.format(cmd)) if len(bezier_segments) > 0: properties['bezier-path'] = BezierPath.fromSegments(bezier_segments) if closed and len(coordinates) >= 3: geometry = shapely.geometry.Polygon(coordinates) elif properties.get('closed', False) and len(coordinates) >= 3: # Return a polygon if flagged as `closed` coordinates.append(coordinates[0]) geometry = shapely.geometry.Polygon(coordinates) elif len(coordinates) >= 2: geometry = shapely.geometry.LineString(coordinates) else: geometry = None return geometry
# Subdivision r1 = self.minDist(uinterval=(umin, newuMid), vinterval=(vmin, newvMid)) r2 = self.minDist(uinterval=(umin, newuMid), vinterval=(newvMid, vmax)) r3 = self.minDist(uinterval=(newuMid, umax), vinterval=(vmin, newvMid)) r4 = self.minDist(uinterval=(newuMid, umax), vinterval=(newvMid, vmax)) results = min([r1, r2, r3, r4], key=lambda x: x[0]) return results def curveDistance(bez1, bez2): """Find the distance between two curves.""" c = MinimumCurveDistanceFinder(bez1, bez2) dist, t1, t2 = c.minDist() return math.sqrt(dist), t1, t2 if __name__ == "__main__": bez1 = CubicBezier(Point(129, 139), Point(190, 139), Point(201, 364), Point(90, 364)) bez2 = CubicBezier(Point(309, 159), Point(178, 159), Point(215, 408), Point(309, 408)) bez3 = Line(Point(309, 159), Point(309, 408)) c = MinimumCurveDistanceFinder(bez1, bez3) dist, t1, t2 = c.minDist() print(bez1.pointAtTime(t1)) print(bez3.pointAtTime(t2)) print(math.sqrt(dist)) print(c.iterations)
def process_shape(self, shape, transform): feature = {'type': 'Feature', 'id': shape.shape_id, 'properties': {}} if shape.name_id != '': feature['properties']['id'] = '{}/{}'.format( self.layer_id, shape.name_id) feature['properties']['selectable'] = True if len(shape.name_attributes): feature['properties']['type'] = shape.name_attributes[0] geometry = {} coordinates = [] pptx_geometry = Geometry(shape) for path in pptx_geometry.path_list: bbox = (shape.width, shape.height) if path.w is None else (path.w, path.h) T = transform * Transform(shape, bbox).matrix() moved = False first_point = None current_point = None closed = False for c in path.getchildren(): if c.tag == DML('arcTo'): wR = pptx_geometry.attrib_value(c, 'wR') hR = pptx_geometry.attrib_value(c, 'hR') stAng = radians(pptx_geometry.attrib_value(c, 'stAng')) swAng = radians(pptx_geometry.attrib_value(c, 'swAng')) p1 = ellipse_point(wR, hR, stAng) p2 = ellipse_point(wR, hR, stAng + swAng) pt = (current_point[0] - p1[0] + p2[0], current_point[1] - p1[1] + p2[1]) large_arc_flag = 1 if swAng >= math.pi else 0 beziers = cubic_beziers_from_arc(tuple2(wR, hR), 0, large_arc_flag, 1, tuple2(*current_point), tuple2(*pt)) for bz in beziers: coordinates.extend(transform_bezier_samples(T, bz)) current_point = pt elif c.tag == DML('close'): if first_point is not None and current_point != first_point: coordinates.append(transform_point(T, first_point)) closed = True first_point = None # Close current pptx_geometry and start a new one... elif c.tag == DML('cubicBezTo'): coords = [BezierPoint(*current_point)] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*pt)) current_point = pt bz = CubicBezier(*coords) coordinates.extend(transform_bezier_samples(T, bz)) elif c.tag == DML('lnTo'): pt = pptx_geometry.point(c.pt) if moved: coordinates.append(transform_point(T, current_point)) moved = False coordinates.append(transform_point(T, pt)) current_point = pt elif c.tag == DML('moveTo'): pt = pptx_geometry.point(c.pt) if first_point is None: first_point = pt current_point = pt moved = True elif c.tag == DML('quadBezTo'): coords = [BezierPoint(*current_point)] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*pt)) current_point = pt bz = QuadraticBezier(*coords) coordinates.extend(transform_bezier_samples(T, bz)) else: print('Unknown path element: {}'.format(c.tag)) lat_lon = points_to_lon_lat(coordinates) if closed: geometry['type'] = 'Polygon' geometry['coordinates'] = [lat_lon] else: geometry['type'] = 'LineString' geometry['coordinates'] = lat_lon feature['geometry'] = geometry self._features.append(feature)
def process_shape(self, shape, properties, transform): #===================================================== ## ## Returns shape's geometry as `shapely` object. ## coordinates = [] pptx_geometry = Geometry(shape) for path in pptx_geometry.path_list: bbox = (shape.width, shape.height) if path.w is None or path.h is None else ( path.w, path.h) T = transform @ Transform(shape, bbox).matrix() moved = False first_point = None current_point = None closed = False for c in path.getchildren(): if c.tag == DML('arcTo'): wR = pptx_geometry.attrib_value(c, 'wR') hR = pptx_geometry.attrib_value(c, 'hR') stAng = radians(pptx_geometry.attrib_value(c, 'stAng')) swAng = radians(pptx_geometry.attrib_value(c, 'swAng')) p1 = ellipse_point(wR, hR, stAng) p2 = ellipse_point(wR, hR, stAng + swAng) pt = (current_point[0] - p1[0] + p2[0], current_point[1] - p1[1] + p2[1]) large_arc_flag = 1 if swAng >= math.pi else 0 beziers = cubic_beziers_from_arc(tuple2(wR, hR), 0, large_arc_flag, 1, tuple2(*current_point), tuple2(*pt)) for bz in beziers: coordinates.extend(transform_bezier_samples(T, bz)) current_point = pt elif c.tag == DML('close'): if first_point is not None and current_point != first_point: coordinates.append(transform_point(T, first_point)) closed = True first_point = None # Close current pptx_geometry and start a new one... elif c.tag == DML('cubicBezTo'): coords = [BezierPoint(*current_point)] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*pt)) current_point = pt bz = CubicBezier(*coords) coordinates.extend(transform_bezier_samples(T, bz)) elif c.tag == DML('lnTo'): pt = pptx_geometry.point(c.pt) if moved: coordinates.append(transform_point(T, current_point)) moved = False coordinates.append(transform_point(T, pt)) current_point = pt elif c.tag == DML('moveTo'): pt = pptx_geometry.point(c.pt) if first_point is None: first_point = pt current_point = pt moved = True elif c.tag == DML('quadBezTo'): coords = [BezierPoint(*current_point)] for p in c.getchildren(): pt = pptx_geometry.point(p) coords.append(BezierPoint(*pt)) current_point = pt bz = QuadraticBezier(*coords) coordinates.extend(transform_bezier_samples(T, bz)) else: print('Unknown path element: {}'.format(c.tag)) if closed: geometry = shapely.geometry.Polygon(coordinates) else: geometry = shapely.geometry.LineString(coordinates) if properties.get('closed', False): # Return a polygon if flagged as `closed` coordinates.append(coordinates[0]) return shapely.geometry.Polygon(coordinates) return geometry
def test_cubic_line_3(self): seg = CubicBezier.fromRepr( "B<<320.0,454.0>-<277.0,454.0>-<230.0,439.0>-<189.0,417.0>>") ray = Line.fromRepr( "L<<254.5,221.5>--<254.5000000000001,887.6681469418963>>") assert seg.intersections(ray)
def test_curvature(self): q = CubicBezier( Point(122,102), Point(35,200), Point(228,145), Point(190,46) ) self.assertAlmostEqual(q.curvatureAtTime(0.5),-103450.5)
def test_length(self): q = CubicBezier( Point(120,160), Point(35,200), Point(220,260), Point(220,40) ) self.assertAlmostEqual(q.length,272.87003168)
def test_cubicLength(self): b1 = CubicBezier(Point(100, 25), Point(10, 90), Point(110, 100), Point(132, 192)) self.assertAlmostEqual(b1.length, 202.20118972656385)