Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets
1 2from .elements import DrawingElement, DrawingParentElement 3 4 5class DrawingDef(DrawingParentElement): 6 ''' Parent class of SVG nodes that must be direct children of <defs> ''' 7 def getSvgDefs(self): 8 return (self,) 9 10class DrawingDefSub(DrawingParentElement): 11 ''' Parent class of SVG nodes that are meant to be descendants of a Def ''' 12 pass 13 14class LinearGradient(DrawingDef): 15 ''' A linear gradient to use as a fill or other color 16 17 Has <stop> nodes as children. ''' 18 TAG_NAME = 'linearGradient' 19 def __init__(self, x1, y1, x2, y2, gradientUnits='userSpaceOnUse', **kwargs): 20 yShift = 0 21 if gradientUnits != 'userSpaceOnUse': 22 yShift = 1 23 try: y1 = yShift - y1 24 except TypeError: pass 25 try: y2 = yShift - y2 26 except TypeError: pass 27 super().__init__(x1=x1, y1=y1, x2=x2, y2=y2, gradientUnits=gradientUnits, 28 **kwargs) 29 def addStop(self, offset, color, opacity=None, **kwargs): 30 stop = GradientStop(offset=offset, stop_color=color, 31 stop_opacity=opacity, **kwargs) 32 self.append(stop) 33 34class RadialGradient(DrawingDef): 35 ''' A radial gradient to use as a fill or other color 36 37 Has <stop> nodes as children. ''' 38 TAG_NAME = 'radialGradient' 39 def __init__(self, cx, cy, r, gradientUnits='userSpaceOnUse', fy=None, **kwargs): 40 yShift = 0 41 if gradientUnits != 'userSpaceOnUse': 42 yShift = 1 43 try: cy = yShift - cy 44 except TypeError: pass 45 try: fy = yShift - fy 46 except TypeError: pass 47 super().__init__(cx=cx, cy=cy, r=r, gradientUnits=gradientUnits, 48 fy=fy, **kwargs) 49 def addStop(self, offset, color, opacity=None, **kwargs): 50 stop = GradientStop(offset=offset, stop_color=color, 51 stop_opacity=opacity, **kwargs) 52 self.append(stop) 53 54class GradientStop(DrawingDefSub): 55 ''' A control point for a radial or linear gradient ''' 56 TAG_NAME = 'stop' 57 hasContent = False 58 59class ClipPath(DrawingDef): 60 ''' A shape used to crop another element by not drawing outside of this 61 shape 62 63 Has regular drawing elements as children. ''' 64 TAG_NAME = 'clipPath' 65 66class Filter(DrawingDef): 67 ''' A filter to apply to geometry 68 69 For example a blur filter. ''' 70 TAG_NAME = 'filter' 71 72class FilterItem(DrawingDefSub): 73 ''' A child of Filter with any tag name''' 74 hasContent = False 75 def __init__(self, tag_name, **args): 76 super().__init__(**args) 77 self.TAG_NAME = tag_name 78 79class Marker(DrawingDef): 80 ''' A small drawing that can be placed at the ends of (or along) a path. 81 82 This can be used for arrow heads or points on a graph for example. 83 84 By default, units are multiples of stroke width.''' 85 TAG_NAME = 'marker' 86 def __init__(self, minx, miny, maxx, maxy, scale=1, orient='auto', 87 **kwargs): 88 width = maxx - minx 89 height = maxy - miny 90 kwargs = { 91 'markerWidth': width if scale == 1 else float(width) * scale, 92 'markerHeight': height if scale == 1 else float(height) * scale, 93 'viewBox': '{} {} {} {}'.format(minx, -maxy, width, height), 94 'orient': orient, 95 **kwargs, 96 } 97 super().__init__(**kwargs)