from IPython.display import SVG def tosvg(polygons,border=0): """ Convert a list of polygons into an SVG image. Polygons are lists of x,y tuples. """ import xml.etree.ElementTree as ET colors = ['aqua','blue','fuchsia','gray','green','lime','maroon', 'navy','olive','purple','red','silver','teal','yellow'] xmin,xmax,ymin,ymax = bbox(polygons,border) width = xmax-xmin height = ymax-ymin svg = ET.Element('svg', xmlns="http://www.w3.org/2000/svg", version="1.1", height="%s" % height, width="%s" % width) for i,polygon in enumerate(polygons): point_list = " ".join(["%d,%d" % (x-xmin,y-ymin) for (x,y) in polygon]) ET.SubElement(svg,"polygon",fill=colors[i%len(colors)], stroke="black",points=point_list) #ET.dump(svg) return ET.tostring(svg) def bbox(polygons,border=0,BIG=1e10): """ Compute the bounding box of a list of polygons. Border adds an optional border amount to all values in the bbox. """ xmin=ymin = BIG xmax=ymax = -BIG for polygon in polygons: for x,y in polygon: xmax = max(xmax,x) xmin = min(xmin,x) ymax = max(ymax,y) ymin = min(ymin,y) return xmin-border,xmax+border,ymin-border,ymax+border polygons = [ [(-20,0),(0,100),(5,100),(5,0)], [(1,1),(1,10),(10,10),(10,1)], [(10,10),(10,50),(50,50),(50,10)], [(50,50),(50,150),(150,150),(150,50)], ] SVG(tosvg(polygons)) SVG(tosvg(polygons,100)) import xml.etree.ElementTree as ET class SVGScene: def __init__(self): self.items = [] self.height = 400 # override with bbox calculation self.width = 400 # override with bbox calculation return def add(self,item): self.items.append(item) def bbox(self,border=0,BIG=1e10): self.xmin = self.ymin = BIG self.xmax = self.ymax = -BIG for item in self.items: xmin,xmax,ymin,ymax = item.bbox() self.xmin = min(self.xmin,xmin) self.xmax = max(self.xmax,xmax) self.ymin = min(self.ymin,ymin) self.ymax = max(self.ymax,ymax) self.xmin -= border self.ymin -= border self.xmax += border self.ymax += border self.height = self.ymax self.width = self.xmax return def _repr_svg_(self): return self.to_svg() def to_svg(self): self.bbox(10) svg = ET.Element('svg', xmlns="http://www.w3.org/2000/svg", version="1.1", height="%s" % self.height, width="%s" % self.width) g = ET.SubElement(svg,"g",style="fill-opacity:1.0; stroke:black; stroke-width:1;") for item in self.items: item.to_svg(g) #ET.dump(svg) # useful for debugging return ET.tostring(svg) def line(self,start,end): self.items.append(Line(start,end)) def circle(self,center,radius,color='blue'): self.items.append(Circle(center,radius,color)) def rectangle(self,origin,height,width,color='blue'): self.items.append(Rectangle(origin,height,width,color)) def text(self,origin,text,size=24): self.items.append(Text(origin,text,size)) class Line: def __init__(self,start,end): self.start = start #xy tuple self.end = end #xy tuple return def to_svg(self,parent): ET.SubElement(parent,"line",x1=str(self.start[0]),y1=str(self.start[1]),x2=str(self.end[0]),y2=str(self.end[1])) def bbox(self): return min(self.start[0],self.end[0]),max(self.start[0],self.end[0]),min(self.start[1],self.end[1]),max(self.start[1],self.end[1]) class Circle: def __init__(self,center,radius,color): self.center = center #xy tuple self.radius = radius #xy tuple self.color = color #rgb tuple in range(0,256) return def to_svg(self,parent): color = colorstr(self.color) ET.SubElement(parent,"circle",cx=str(self.center[0]),cy=str(self.center[1]),r=str(self.radius), style="fill:%s;" % color) def bbox(self): return self.center[0]-self.radius,self.center[0]+self.radius,self.center[1]-self.radius,self.center[1]+self.radius class Rectangle: def __init__(self,origin,height,width,color): self.origin = origin self.height = height self.width = width self.color = color return def to_svg(self,parent): color = colorstr(self.color) ET.SubElement(parent,"rect",x=str(self.origin[0]),y=str(self.origin[1]),height=str(self.height), width=str(self.width),style="fill:%s;" % color) def bbox(self): return self.origin[0],self.origin[0]+self.width,self.origin[1],self.origin[1]+self.height class Text: def __init__(self,origin,text,size=24): self.origin = origin self.text = text self.size = size return def to_svg(self,parent): fs = "font-size" el = ET.SubElement(parent,"text",x=str(self.origin[0]),y=str(self.origin[1])) el.set("font-size",str(self.size)) el.text = self.text def bbox(self): return self.origin[0],self.origin[0]+self.size,self.origin[1],self.origin[1]+self.size # Guessing here def colorstr(rgb): if type(rgb) == type(""): return rgb return "#%x%x%x" % (rgb[0]/16,rgb[1]/16,rgb[2]/16) scene = SVGScene() scene.rectangle((100,100),200,200,(0,255,255)) scene.line((200,200),(200,300)) scene.line((200,200),(300,200)) scene.line((200,200),(100,200)) scene.line((200,200),(200,100)) scene.circle((200,200),30,(0,0,255)) scene.circle((200,300),30,(0,255,0)) scene.circle((300,200),30,(255,0,0)) scene.circle((100,200),30,(255,255,0)) scene.circle((200,100),30,"fuchsia") scene.text((50,50),"Testing SVG") scene