def VairPattern(self): pattern = SVGdraw.SVGelement(type="pattern", attributes={ "patternContentUnits": "userSpaceOnUse", "height": "8", "id": "vair-in-pale%04d" % blazon.Ordinary.id, "patternUnits": "userSpaceOnUse", "width": "8" }) pattern.addElement( self.color1.fill(SVGdraw.rect(x="0", y="0", width="8", height="8"))) pattern.addElement( self.color2.fill( SVGdraw.SVGelement( type='path', attributes={ "d": "M0,8 l2,-2 l0,-4 l2,-2 l2,2 l0,4 l2,2 z" }))) self.color = "vair-in-pale%04d" % blazon.Ordinary.id blazon.Ordinary.id += 1 return pattern
def fill(self, elt): pattern = SVGdraw.SVGelement('pattern', attributes={ "width": "8", "height": "16", "patternUnits": "userSpaceOnUse", "patternContentUnits": "userSpaceOnUse", "id": "counter-vair%04d" % blazon.Ordinary.id }) blazon.Ordinary.id += 1 pattern.addElement( self.color1.fill(SVGdraw.rect(x="0", y="0", width="8", height="18"))) pattern.addElement( self.color2.fill( SVGdraw.SVGelement( 'path', attributes={ "d": "M0,8 l2,-2 l0,-4 l2,-2 l2,2 l0,4 l2,2 l-2,2 l0,4 l-2,2 l-2,-2 l0,-4 z" }))) elt.attributes["fill"] = "url(#%s)" % pattern.attributes["id"] blazon.Ordinary.defs.append(pattern) return elt
def fill(self,elt): pattern=SVGdraw.SVGelement('pattern',attributes= {"height":"15","width":"15", "patternUnits":"userSpaceOnUse", "patternContentUnits":"userSpaceOnUse", "id":"ermine%04d"%blazon.Ordinary.id}) blazon.Ordinary.id+=1 pattern.addElement(self.color1.fill(SVGdraw.rect(x="0",y="0",width="15",height="15"))) pattern.addElement(self.color2.fill(SVGdraw.SVGelement('path', attributes={"d": "M1,5 c1,-1 1.5,-4 1.5,-4 c0,0 .5,3 1.5,4 l-1.5,1.5 z"}))) pattern.addElement(self.color2.fill(SVGdraw.circle(cx="1.5",cy="2", r=".5"))) pattern.addElement(self.color2.fill(SVGdraw.circle(cx="2.5",cy="1", r=".5"))) pattern.addElement(self.color2.fill(SVGdraw.circle(cx="3.5",cy="2", r=".5"))) pattern.addElement(self.color2.fill(SVGdraw.SVGelement('path', attributes={"d": "M8.5,12.5 c1,-1 1.5,-4 1.5,-4 c0,0 .5,3 1.5,4 l-1.5,1.5 z"}))) pattern.addElement(self.color2.fill(SVGdraw.circle(cx="9",cy="9.5", r=".5"))) pattern.addElement(self.color2.fill(SVGdraw.circle(cx="10",cy="8.5", r=".5"))) pattern.addElement(self.color2.fill(SVGdraw.circle(cx="11",cy="9.5", r=".5"))) blazon.Ordinary.defs.append(pattern) elt.attributes["fill"]="url(#%s)"%pattern.attributes["id"] return elt
def fill(self, elt): pattern = SVGdraw.SVGelement('pattern', attributes={ "height": "20", "width": "20", "patternUnits": "userSpaceOnUse", "patternContentUnits": "userSpaceOnUse", "id": "semy%04d" % blazon.Ordinary.id }) blazon.Ordinary.id += 1 # Just in case we try to countercharge it. It doesn't work anyway. self.colors = (self.background, self.charge.tincture) charge2 = copy.deepcopy(self.charge) self.charge.moveto((5, 15)) self.charge.scale(.1) charge2.moveto((15, 5)) charge2.scale(.1) #pattern.addElement(SVGdraw.rect(0,0,30,30,stroke="black", # stroke_width=".3",fill="none")) pattern.addElement(self.charge.finalizeSVG()) pattern.addElement(charge2.finalizeSVG()) blazon.Ordinary.defs.append(pattern) newelt = SVGdraw.group() elt = self.background.fill(elt) newelt.addElement(elt) newbase = SVGdraw.rect(x=-blazon.Ordinary.FESSPTX, y=-blazon.Ordinary.FESSPTY, width=blazon.Ordinary.WIDTH, height=blazon.Ordinary.HEIGHT, fill="url(#%s)" % pattern.attributes["id"]) newelt.addElement(newbase) return newelt
def fill(self, elt): VIPpattern = self.VairPattern() blazon.Ordinary.defs.append(VIPpattern) pattern = SVGdraw.SVGelement('pattern', attributes={ "width": "16", "height": "16", "patternUnits": "userSpaceOnUse", "patternContentUnits": "userSpaceOnUse", "id": "vair%04d" % blazon.Ordinary.id }) self.color = "vair%04d" % blazon.Ordinary.id blazon.Ordinary.id += 1 pattern.addElement( SVGdraw.rect(x="0", y="0", width="16", height="8", fill="url(#%s)" % VIPpattern.attributes["id"])) pattern.addElement( SVGdraw.rect(x="0", y="8", width="20", height="8", fill="url(#%s)" % VIPpattern.attributes["id"], transform="translate(-4,0)")) blazon.Ordinary.defs.append(pattern) elt.attributes["fill"] = "url(#%s)" % pattern.attributes["id"] return elt
def fill(self,elt): pattern=SVGdraw.SVGelement('pattern',attributes= {"height":"20", "width":"20", "patternUnits":"userSpaceOnUse", "patternContentUnits":"userSpaceOnUse", "id": "maso%04d"%blazon.Ordinary.id}) blazon.Ordinary.id+=1 group=SVGdraw.group() group.addElement(self.color2.fill(SVGdraw.rect(x="0",y="9",width="20",height="2"))) group.addElement(self.color2.fill(SVGdraw.rect(x="0",y="10",width="1",height="10"))) group.addElement(self.color2.fill(SVGdraw.rect(x="19",y="10",width="1",height="10"))) group.addElement(self.color2.fill(SVGdraw.rect(x="9",y="0",width="2", height="10"))) group.addElement(self.color2.fill(SVGdraw.rect(x="0",y="0",width="20",height="1"))) group.addElement(self.color2.fill(SVGdraw.rect(x="0",y="19",width="20",height="1"))) pattern.addElement(group) blazon.Ordinary.defs.append(pattern) elt=self.color1.fill(elt) newelt=SVGdraw.group() newelt.addElement(elt) newbase=SVGdraw.rect(x=-blazon.Ordinary.FESSPTX, y=-blazon.Ordinary.FESSPTY, width=blazon.Ordinary.WIDTH, height=blazon.Ordinary.HEIGHT, fill="url(#%s)"%pattern.attributes["id"]) newelt.addElement(newbase) return newelt
def fill(self, elt): pattern = SVGdraw.SVGelement('pattern', attributes={ "height": "20", "width": "20", "patternUnits": "userSpaceOnUse", "patternContentUnits": "userSpaceOnUse", "id": "fret%04d" % blazon.Ordinary.id }) blazon.Ordinary.id += 1 # Why do I need a background rectangle in the pattern??? # pattern.addElement(self.color1.fill(SVGdraw.rect(x="0",y="0",width="22", # height="22"))) group = SVGdraw.group() group.attributes["stroke"] = "#888888" group.attributes["stroke-width"] = ".1" rect1 = self.color2.fill( SVGdraw.rect(x="-2", y="-20", width="4", height="50")) rect1.attributes["transform"] = "translate(0,10) rotate(45)" rect2 = self.color2.fill( SVGdraw.rect(x="-2", y="-20", width="4", height="50")) rect2.attributes["transform"] = "translate(20,10) rotate(45)" rect3 = self.color2.fill( SVGdraw.rect(x="-2", y="-20", width="4", height="50")) rect3.attributes["transform"] = "translate(0,10) rotate(-45)" rect4 = self.color2.fill( SVGdraw.rect(x="-2", y="-20", width="4", height="50")) rect4.attributes["transform"] = "translate(20,10) rotate(-45)" for e in [rect1, rect2, rect3, rect4]: group.addElement(e) pattern.addElement(group) blazon.Ordinary.defs.append(pattern) elt = self.color1.fill(elt) newelt = SVGdraw.group() newelt.addElement(elt) newbase = SVGdraw.rect(x=-blazon.Ordinary.FESSPTX, y=-blazon.Ordinary.FESSPTY, width=blazon.Ordinary.WIDTH, height=blazon.Ordinary.HEIGHT, fill="url(#%s)" % pattern.attributes["id"]) newelt.addElement(newbase) return newelt
def svgout(self, stroke_width=0.3, scale=20, circle_radius=0.3, startat=None, coloriter=None, crossings=True, circradius=None, circscale=1): # if circradius is some positive number, try to draw a circular(!) diagram # circscale is how much to scale the y-dimension by (how thick a circle) # try: # if type(SVGdraw)!=type(__builtins__): # raise Exception("SVGdraw not a module?") # return None # except NameError: # raise Exception("No SVGDraw found") # return None cols = [ '#000000', '#800000', '#808000', '#008080', '#000080', '#ff2000', '#ffff20', '#20ffff', '#0020ff', '#ff0080', '#ff8000', '#8000ff', '#80ff00' ] if circradius: sz = (2 * self.ymax * circscale + 2 + 2 * circradius) svg = SVGdraw.svg( width="%dpx" % (sz * scale), height="%dpx" % (sz * scale), viewBox=[-sz + self.xmodulus / 2.0, -sz, 2 * sz, 2 * sz]) def transform(x, y): # Have to flip it over... r = self.ymax * circscale + circradius - y * circscale theta = 2 * math.pi * x / self.xmodulus - math.pi return [ sz / 2 + r * math.cos(theta), sz / 2 + r * math.sin(theta) ] else: svg = SVGdraw.svg( width="%dpx" % ((self.xmodulus + 2) * scale), height="%dpx" % ((self.ymax + 2) * scale), viewBox=[-1, -1, self.xmodulus + 2, self.ymax + 2]) def transform(x, y): return [x, y] defs = SVGdraw.defs(id="defs") plusmask = SVGdraw.SVGelement("mask", attributes={"id": "plusmask"}) minusmask = SVGdraw.SVGelement("mask", attributes={"id": "minusmask"}) if circradius: sz = 1 + 2 * self.ymax * circscale + 2 * circradius # Whatever, something big. r = SVGdraw.rect(x=-sz, y=-sz, width=sz * 2, height=sz * 2, fill='white') else: r = SVGdraw.rect(x=-1, y=-1, width=self.xmodulus + 2, height=self.ymax + 2, fill='white') plusmask.addElement(r) minusmask.addElement(r) defs.addElement(plusmask) defs.addElement(minusmask) svg.addElement(defs) maingroup = SVGdraw.group(id="main") # I've come to expect them this way up... maingroup.attributes['transform']='scale(1,-1) translate(0,%d)'% \ (-self.ymax) svg.addElement(maingroup) # Positive slopes and negative slopes. plus = SVGdraw.group(id="plus", mask="url(#plusmask)") minus = SVGdraw.group(id="minus", mask="url(#minusmask)") maingroup.addElement(plus) maingroup.addElement(minus) circgroup = SVGdraw.group(id="circgroup") maingroup.addElement(circgroup) strands = self.strands(self.pivots[0]) circuit = None if coloriter is None: if len(strands) > 1: # Multistranded; color it by strand. def multicoloriter(): counter = 0 lastcircuit = None while True: if circuit != lastcircuit: lastcircuit = circuit counter += 1 yield cols[counter % len(cols)] coloriter = multicoloriter() else: def singlecoloriter(): # for singlestranders! colcounter = 0 colordiv = len(self.pivots) / 6 while True: yield cols[int(colcounter / colordiv) % len(cols)] colcounter += 1 coloriter = singlecoloriter() for circuit in strands: # If there's a startat parameter, and it appears in this list, # slosh the list around so it's first if startat and startat in circuit: ind = circuit.index(startat) circuit = circuit[ind:] + circuit[0:ind] for i in range(0, len(circuit)): here = circuit[i] nxt = circuit[(i + 1) % len(circuit)] col = coloriter.next() if type(col) == int: # let iterator generate indexes col = cols[col % len(cols)] if circradius: path = [here, nxt] else: path = self.pathbetween(here, nxt) pathstring = "" for j in range(0, len(path), 2): # Had hoped that transform() would have been enough, but we need # to go through all the intermediate lattice-points when doing # circular plots, to curve around in the right direction. if circradius: betweens = self.pointsbetween(path[j], path[j + 1]) pathstring += " M %f %f " % tuple( transform(path[j].x, path[j].y)) for k in range(0, len(betweens)): pathstring+=" L %f %f "% \ tuple(transform(betweens[k].x,betweens[k].y)) pathstring+="L %f %f "% \ tuple(transform(path[j+1].x, path[j+1].y)) else: pathstring+=" M %f %f L %f %f"% \ (tuple(transform(path[j].x,path[j].y)+ transform(path[j+1].x,path[j+1].y))) pathelt = SVGdraw.path(pathstring, stroke_width=stroke_width, stroke=col, fill="none") if self.slopebetween(here, nxt) > 0: plus.addElement(pathelt) else: minus.addElement(pathelt) for i in self.pivots: cr = transform(i.x, i.y) c = SVGdraw.circle(cx=cr[0], cy=cr[1], r=circle_radius, fill='black') circgroup.addElement(c) if not circradius: # Mark the wraparound point. circgroup.addElement(SVGdraw.path("M 0 -1 l 0 %d M %d -1 l 0 %d"% \ (self.ymax+2,self.xmodulus, self.ymax+2), stroke='black', stroke_width=0.03)) # Somehow I want to *note* when a knot is single-strand or # multistrand. circgroup.addElement( SVGdraw.text(x=0.2, y=0, text=str(len(strands)), fill='#000408', font_size=1, font_family='sans-serif', transform='scale(1,-1)')) if crossings: # Try multistrand crossings? (not working right) # Need *ALL* the crossing points though. oncircuit = [] for circuit in strands: oncircuit.extend(self.oncircuit(circuit)) masked = set() over = 0 masks = [minusmask, plusmask] # How about this? For each horizontal line _that has intersections on it_, # all crossings go in one direction, and that direction alternates. # # How do we find those lines? points = [] for circuit in strands: for i in range(0, len(circuit)): here = circuit[i] nxt = circuit[(i + 1) % len(circuit)] points += self.pointsbetween(here, nxt) heights = [] howmanyhits = dict() for p in points: howmanyhits[p] = howmanyhits.get(p, 0) + 1 howmanyhits = [(p, howmanyhits[p]) for p in howmanyhits.keys()] howmanyhits = filter((lambda x: x[1] > 1), howmanyhits) heights = [x[0].y for x in howmanyhits] heights.sort() # No "sort unique" so just keep track of the last one we saw and skip it. # DOESN'T WORK EITHER BUT BETTER THAN BEFORE XXXXXX # (testing with python ./knots.py -l 18 17 6 32 6 37) Works with more # symmetrical designs. last = None for h in heights: if h == last: continue last = h mask = masks[over] over = 1 - over for x in range(0, self.xmodulus, 2): p = Point((x if not h % 2 else x + 1), h, self) if p in self.pivots: continue # Skip pivot-points. tp1 = transform(p.x - 0.5, p.y - 0.5) tp2 = transform(p.x - 0.5, p.y + 0.5) tp3 = transform(p.x + 0.5, p.y + 0.5) tp4 = transform(p.x + 0.5, p.y - 0.5) tp = transform(p.x, p.y) if circradius: r = SVGdraw.circle(fill="black", cx=tp[0], cy=tp[1], r=0.6) else: angle = 45 r=SVGdraw.polygon(fill="black", points=[tp1,tp2,tp3,tp4], transform="rotate(%f,%f,%f)"% \ (angle, tp[0], tp[1])) mask.addElement(r) # maingroup.addElement(r) # If it's on the edge, duplicate it on the other side # for ease of viewing. if p.x == 0 and not circradius: mask.addElement( SVGdraw.rect(x=self.xmodulus - 0.5, y=p.y - 0.5, width=1, height=1, fill="#111", transform="rotate(45,%d,%d)" % (self.xmodulus, p.y))) return svg