def test_extremes3(self): # console.log((new Bezier(127,242,27,5,210,60)).extrema()) q = QuadraticBezier(Point(127, 242), Point(27, 5), Point(210, 60)) r = q.findExtremes() self.assertEqual(len(r), 2) self.assertAlmostEqual(r[0], 0.35335689045936397) self.assertAlmostEqual(r[1], 0.8116438356164384)
def test_quadratic_bounds(self): # console.log((new Bezier(150,40,80,30,105,150)).bbox()) q = QuadraticBezier(Point(150, 40), Point(80, 30), Point(105, 150)) b = q.bounds() self.assertAlmostEqual(b.bl.x, 98.42105263157895) self.assertAlmostEqual(b.tr.x, 150) self.assertAlmostEqual(b.bl.y, 39.23076923076923) self.assertAlmostEqual(b.tr.y, 150)
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 __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 derivative(self): """Returns a `QuadraticBezier` representing the derivative of this curve.""" return QuadraticBezier((self[1] - self[0]) * 3, (self[2] - self[1]) * 3, (self[3] - self[2]) * 3)
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
def test_split(self): # console.log((new Bezier(150,40,80,30,105,150)).split(0.2)) q = QuadraticBezier(Point(150, 40), Point(80, 30), Point(105, 150)) left, right = q.splitAtTime(0.2) self.assertEqual(left[1], Point(136, 38)) self.assertEqual(right[1], Point(85, 54))
def test_extremes4(self): q = QuadraticBezier(Point(664, 1075), Point(732, 1167), Point(800, 1239)) r = q.findExtremes() self.assertEqual(len(r), 0)
def test_extremes2(self): # console.log((new Bezier(127,242,71,150,210,60)).extrema()) q = QuadraticBezier(Point(127, 242), Point(71, 150), Point(210, 60)) r = q.findExtremes() self.assertEqual(len(r), 1) self.assertAlmostEqual(r[0], 0.28717948717948716)
def test_extremes(self): q = QuadraticBezier(Point(70, 250), Point(13, 187), Point(209, 58)) r = q.findExtremes() self.assertEqual(len(r), 1) self.assertAlmostEqual(r[0], 0.22529644268774704)
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 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 test_quadraticLength(self): b1 = QuadraticBezier(Point(150, 40), Point(80, 30), Point(105, 150)) self.assertAlmostEqual(b1.length, 144.79561403359523) b2 = QuadraticBezier(Point(707, 596), Point(645, 596), Point(592, 623)) self.assertAlmostEqual(b2.length, 119.25113694489232)