def drawIt(s, c, segs): import matplotlib.pyplot as plt fig, ax = plt.subplots() s.plot(ax, drawNodes=False) c.plot(ax) for s in segs: BezierPath.fromSegments([s]).plot(ax, drawNodes=False, color="red") plt.show()
def Rectangle(width, height, origin=None): """Returns a path representing an rectangle of given width and height. You can specify the `origin` as a Point.""" if not origin: origin = Point(0, 0) tl = origin + west * width / 2.0 + north * height / 2.0 tr = origin + east * width / 2.0 + north * height / 2.0 bl = origin + west * width / 2.0 + south * height / 2.0 br = origin + east * width / 2.0 + south * height / 2.0 return BezierPath.fromSegments( [Line(tl, tr), Line(tr, br), Line(br, bl), Line(bl, tl)])
def annotate_glyph(glyphname): paths = FontParts.fromFontpartsGlyph(font[glyphname]) paths2 = [] if drawarrows: for i in range(0, len(paths)): arrowP = paths[i].clone() arrowP.translate(arrowvector) arrowSeg, _ = (arrowP.asSegments())[0].splitAtTime(0.25) s = arrowSeg.start arrowPath = BezierPath.fromSegments([arrowSeg]) # arrowPath.closed = False paths2.append(arrowPath) number = FontParts.fromFontpartsGlyph( nf.numbersfont[nf.numbers[i]]) # print(number[0].asNodelist()) number[0].scale(0.05) number[0].translate(Point(s.x + 5, s.y + 5)) number[0].closed = True paths2.append(number[0]) # print('<text x="%s" y="%s">%i</text>' % (s.x+5.0,height-(s.y+5.0),1+i)) # print('<path d="%s" stroke="black" stroke-width="2" fill="transparent" marker-end="url(#arrowhead)"/>\n' % path2svg([arrowSeg])) splitlist = [] for i in range(0, len(paths)): for j in range(i + 1, len(paths)): one = paths[i] two = paths[j] for s1 in one.asSegments(): for s2 in two.asSegments(): for inter in s1.intersections(s2): splitlist.append((inter.seg1, inter.t1)) splitlist.append((inter.seg2, inter.t2)) for path in paths: path.splitAtPoints(splitlist) segs = [] for i in range(0, len(paths)): segs = paths[i].asSegments() for s in segs: paths2.append(Circle(dotradius, origin=s.start)) if s.length > dotspacing * dotradius: closeEnough = int(s.length / (dotspacing * dotradius)) samples = s.regularSample(closeEnough) for p in samples[1:]: paths2.append(Circle(dotradius, origin=p)) if len(segs) > 0: paths2.append(Circle(dotradius, origin=segs[-1].end)) for p in paths2: FontParts.drawToFontpartsGlyph(font[glyphname], p)
def __get_centreline(self, n1: str, n2: str) -> BezierPath: #========================================================= if (n1, n2) in self.__path_network.edges: edge = self.__path_network.edges[n1, n2] bezier_path = edge.get('geometry') if bezier_path is not None: if n1 == edge.get('start-node'): return bezier_path else: segments = [ bz.reversed() for bz in bezier_path.asSegments() ] segments.reverse() return BezierPath.fromSegments(segments) return None
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 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 clip(self, clip, cliptype, flat=False): splitlist1 = [] splitlist2 = [] intersections = {} cloned = self.clone() clip = clip.clone() # Split all segments at intersections for s1 in self.asSegments(): for s2 in clip.asSegments(): for i in s1.intersections(s2): if i.t1 > 1e-8 and i.t1 < 1 - 1e-8: if i.seg1 == s1: splitlist1.append((i.seg1, i.t1)) splitlist2.append((i.seg2, i.t2)) else: splitlist2.append((i.seg1, i.t1)) splitlist1.append((i.seg2, i.t2)) intersections[i.point] = i logging.debug("Split list: %s" % splitlist1) logging.debug("Split list 2: %s" % splitlist2) cloned.splitAtPoints(splitlist1) clip.splitAtPoints(splitlist2) logging.debug("Self:") logging.debug(cloned.asSegments()) logging.debug("Clip:") logging.debug(clip.asSegments()) segs1unflattened = cloned.asSegments() segs2unflattened = clip.asSegments() # Replace with flattened versions, building a dictionary of originals segs1 = [] reconstructionLUT = {} precision = 100. def fillLUT(flats): for line in flats: key = ((line.start * precision).rounded(), (line.end * precision).rounded()) reconstructionLUT[key] = (line._orig or line) key2 = ((line.end * precision).rounded(), (line.start * precision).rounded()) reconstructionLUT[key2] = (line._orig or line).reversed() for s in segs1unflattened: flats = s.flatten(2) fillLUT(flats) segs1.extend(flats) segs2 = [] for s in segs2unflattened: flats = s.flatten(2) fillLUT(flats) segs2.extend(flats) # Leave it to the professionals subj = [(s[0].x * precision, s[0].y * precision) for s in segs1] clip = [(s[0].x * precision, s[0].y * precision) for s in segs2] pc = pyclipper.Pyclipper() pc.AddPath(clip, pyclipper.PT_CLIP, True) pc.AddPath(subj, pyclipper.PT_SUBJECT, True) paths = pc.Execute(cliptype, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) outpaths = [] # Now reconstruct Bezier segments from flattened paths def pairwise(points): a = (p for p in points) b = (p for p in points) next(b) for curpoint, nextpoint in zip(a, b): yield curpoint, nextpoint newpaths = [] from beziers.path import BezierPath for p in paths: newpath = [] for scaledstart, scaledend in pairwise(p): key = (Point(*scaledstart), Point(*scaledend)) if key in reconstructionLUT and not flat: orig = reconstructionLUT[key] if len(newpath) == 0 or newpath[-1] != orig: newpath.append(orig) else: newpath.append(Line(key[0] / precision, key[1] / precision)) outpaths.append(BezierPath.fromSegments(newpath)) return outpaths
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 clip(self,clip,cliptype): import pyclipper splitlist1 = [] splitlist2 = [] intersections = {} cloned = self.clone() clip = clip.clone() # Split all segments at intersections for s1 in self.asSegments(): for s2 in clip.asSegments(): for i in s1.intersections(s2): if i.t1 > 1e-8 and i.t1 < 1-1e-8: if i.seg1 == s1: splitlist1.append((i.seg1,i.t1)) splitlist2.append((i.seg2,i.t2)) else: splitlist2.append((i.seg1,i.t1)) splitlist1.append((i.seg2,i.t2)) intersections[i.point] = i logging.debug("Split list: %s" % splitlist1) logging.debug("Split list 2: %s" % splitlist2) cloned.splitAtPoints(splitlist1) clip.splitAtPoints(splitlist2) logging.debug("Self:") logging.debug(cloned.asSegments()) logging.debug("Clip:") logging.debug(clip.asSegments()) segs1unflattened = cloned.asSegments() segs2unflattened = clip.asSegments() # Replace with flattened versions, building a dictionary of originals segs1 = [] reconstructionLUT = {} precision = 100. def fillLUT(flats): for line in flats: key = ((line.start * precision).rounded(), (line.end * precision).rounded()) reconstructionLUT[key] = (line._orig or line) key2 = ((line.end * precision).rounded(), (line.start * precision).rounded()) reconstructionLUT[key2] = (line._orig or line).reversed() for s in segs1unflattened: flats = s.flatten(2) fillLUT(flats) segs1.extend(flats) segs2 = [] for s in segs2unflattened: flats = s.flatten(2) fillLUT(flats) segs2.extend(flats) # Leave it to the professionals subj = [(s[0].x*precision, s[0].y*precision) for s in segs1] clip = [(s[0].x*precision, s[0].y*precision) for s in segs2] pc = pyclipper.Pyclipper() pc.AddPath(clip, pyclipper.PT_CLIP, True) pc.AddPath(subj, pyclipper.PT_SUBJECT, True) paths = pc.Execute(cliptype, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) outpaths = [] # Now reconstruct Bezier segments from flattened paths def pairwise(points): a = (p for p in points) b = (p for p in points) next(b) for curpoint,nextpoint in zip(a, b): yield curpoint, nextpoint newpaths = [] from beziers.path import BezierPath for p in paths: newpath = [] for scaledstart,scaledend in pairwise(p): key = (Point(*scaledstart), Point(*scaledend)) if key in reconstructionLUT: orig = reconstructionLUT[key] if len(newpath) == 0 or newpath[-1] != orig: newpath.append(orig) else: newpath.append(Line(key[0]/precision, key[1]/precision)) outpaths.append(BezierPath.fromSegments(newpath)) return outpaths
def __extract_components(self) -> None: #====================================== """ Extracts and stores centreline components (i.e, coordinates & derivatives) in a dictionary for every nerve set. Each nerve set is a dict with keys corresponding to the keys in self.__continuous_paths. """ node_geometry = self.__path_network.nodes(data='geometry') for path_id, path_nodes in self.__continuous_paths.items(): # First derive the segments that connect the path's nodes centrelines = [] node_regions = [] for node_1, node_2 in pairwise(path_nodes): centreline = self.__get_centreline(node_1, node_2) if len(centrelines) > 0: ##join_region = node_geometry[node_1] # Join previous and current centreline in circle centred at region's centroid join_region = node_geometry[node_1].centroid.buffer( JOIN_RADIUS) joined_beziers = join_beziers_in_region( centrelines[-1], join_region, centreline) # Adjust previous centreline segments = centrelines[-1].asSegments() segments[-1] = joined_beziers[0] centrelines[-1] = BezierPath.fromSegments(segments) # Add centreline of join centrelines.append( BezierPath.fromSegments(joined_beziers[1:2])) node_regions.append((None, None)) # Adjust current centreline segments = centreline.asSegments() segments[0] = joined_beziers[2] centreline = BezierPath.fromSegments(segments) centrelines.append(centreline) node_regions.append( (node_geometry[node_1], node_geometry[node_2])) # Get the path segment for each centreline path_segments = [] for n, centreline in enumerate(centrelines): regions = node_regions[n] path_segment = PathSegment( regions[0], centreline, regions[1], subdivision_parts=(1 if regions[0] is None else NUMBER_OF_BEZIER_PARTS)) path_segments.append(path_segment) # And use them to set the control points for the path's centreline sheath for path_segment in path_segments: control_points = path_segment.control_points if len(self.__control_points[path_id]) == 0: self.__control_points[path_id].append(control_points[0]) self.__control_points[path_id].extend(control_points[1:]) # The sheath starts and ends at the respective node centroids self.__control_points[path_id][0].set_position( path_segments[0].start_point) self.__control_points[path_id][-1].set_position( path_segments[-1].end_point)
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 path = bezier_path_from_arc_endpoints(tuple2(wR, hR), 0, large_arc_flag, 1, tuple2(*current_point), tuple2(*pt), T) bezier_segments.extend(path.asSegments()) coordinates.extend(bezier_sample(path)) 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: log.warn('Unknown path element: {}'.format(c.tag)) if len(bezier_segments) > 0: properties['bezier-path'] = BezierPath.fromSegments(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 geometry(self) -> [GeometricShape]: """ Returns: A list of geometric objects. This are LineStrings describing paths between nodes and possibly additional features (e.g. way markers) of the paths. """ display_bezier_points = True #False ### To come from settings... if self.__path_layout == 'automatic': log("Automated pathway layout. Path ID: ", self.__path_id) evaluate_settings = self.__sheath.settings() # TODO: use evenly-distributed offsets for the final product. number_of_neurons = len(evaluate_settings['derivatives']) # locations = [0.01 + x*(0.99-0.01)/number_of_neurons for x in range(number_of_neurons)] location = 0.5 geometry = [] for scaffold, path_id, derivative in zip( evaluate_settings['scaffolds'], evaluate_settings['path_ids'], evaluate_settings['derivatives']): scaffold.generate() connectivity = Connectivity(path_id, scaffold, derivative, location) auto_beziers = connectivity.get_neuron_line_beziers() path = BezierPath.fromSegments(auto_beziers) geometry.append( GeometricShape( shapely.geometry.LineString(bezier_sample(path)))) end_nodes = set(self.__source_nodes) end_nodes.update(self.__target_nodes) for node in end_nodes: for edge in self.__graph.edges(node, data=True): if edge[2].get('type') == 'terminal': line = self.__line_from_edge(edge) if line is not None: geometry.append(GeometricShape(line)) if display_bezier_points: for beziers in self.__sheath.path_beziers.values(): for bezier in beziers: bz_pts = tuple([p.x, p.y] for p in bezier.points) for pt in [bz_pts[0], bz_pts[3]]: geometry.append( GeometricShape(GeometricShape.circle(pt), { 'type': 'bezier', 'kind': 'bezier-end' })) for pt in bz_pts[1:3]: geometry.append( GeometricShape(GeometricShape.circle(pt), { 'type': 'bezier', 'kind': 'bezier-control' })) geometry.append( GeometricShape(GeometricShape.line(*bz_pts[0:2]), {'type': 'bezier'})) geometry.append( GeometricShape(GeometricShape.line(*bz_pts[2:4]), {'type': 'bezier'})) return geometry # Fallback is centreline layout geometry = [] for edge in self.__graph.edges.data(): nerve = edge[2].get('nerve') properties = {'nerve': nerve} if nerve is not None else None bezier = edge[2].get('geometry') if self.__path_layout != 'linear' and bezier is not None: geometry.append( GeometricShape( shapely.geometry.LineString(bezier_sample(bezier)), properties)) else: line = self.__line_from_edge(edge) if line is not None: geometry.append(GeometricShape(line, properties)) return geometry