Changeset - b9d29dcfe3d8
[Not reviewed]
default
0 4 1
Laman - 8 years ago 2017-02-05 20:46:16

svg rendering
5 files changed with 159 insertions and 91 deletions:
0 comments (0 inline, 0 general)
src/diana.py
Show inline comments
 
@@ -3,11 +3,18 @@ import re
 
import sys
 
 
import go
 
import sgf
 
from go import BLACK,WHITE,EMPTY
 
from sgfParser.collection import Collection
 
 
from drawer.svg import Svg
 
from drawer.tikz import Tikz
 
 
 
templateDir=os.path.join(os.path.dirname(__file__),"templ")
 
with open(os.path.join(templateDir,"templ.svg")) as f:
 
	template=f.read()
 
 
 
if len(sys.argv)>1:
 
	files=sys.argv[1:]
 
else:
 
@@ -19,14 +26,28 @@ minMovesPerDiagram=10
 
t=sys.stdout
 
 
 
def collectMoves(root):
 
	node=root
 
	while len(node.children)>0:
 
		b=node.getProp("B")
 
		w=node.getProp("W")
 
		if b is not None: yield ("b",b)
 
		elif w is not None: yield ("w",w)
 
		# else: yield None # !! not really robust
 
 
		node=node.children[0]
 
 
 
def processFile(fileName):
 
	shortName="".join(re.split(r'[/\\]',fileName)[-1].split('.')[:-1])
 
	
 
	game=go.Go()
 
	global t
 
	
 
	record=sgf.Sgf(open(fileName, 'r', encoding="utf-8").read())
 
	moves=record.getMoves()
 
 
	games=Collection(open(fileName, 'r', encoding="utf-8").read()).listGames()
 
	record=list(games)[0]
 
 
	moves=list(collectMoves(record))
 
 
	localBoard=dict()
 
	overlays=""
 
@@ -37,52 +58,51 @@ def processFile(fileName):
 
	moveNumber=0
 
	
 
	for i in range(diagramsNeeded):
 
		# inicializuj diagram
 
		diagram=Tikz()
 
		# initialize the diagram
 
		diagram=Svg()
 
		
 
		for lineNumber,line in enumerate(game.board):
 
			for itemNumber,item in enumerate(line):
 
				if item==1: diagram.drawStone(itemNumber,lineNumber,"b")
 
				if item==-1: diagram.drawStone(itemNumber,lineNumber,"w")
 
			localBoard={(a,b):game.board[b][a]-1 for a in range(19) for b in range(19) if game.board[b][a]!=0}
 
				if item==BLACK: diagram.addStone(itemNumber,lineNumber,"b")
 
				if item==WHITE: diagram.addStone(itemNumber,lineNumber,"w")
 
			localBoard={(a,b):game.board[b][a]-1 for a in range(19) for b in range(19) if game.board[b][a]!=EMPTY}
 
		
 
		for j in range(movesPerDiagram):
 
			# kresli tahy
 
			# draw the moves
 
			if moveNumber>=len(moves): break
 
			
 
 
			c,(x,y)=moves[moveNumber]
 
			c=c.lower()
 
			
 
			if not game.move(1 if c=='b' else -1,x,y):
 
			if not game.move(BLACK if c=='b' else WHITE, x,y):
 
				print("illegal move: {0} at {1},{2}".format(moveNumber+1,x,y))
 
				moveNumber+=1
 
				continue
 
			
 
			# zapíšu tah na volný průsečík
 
 
			# draw the move on an empty intersection
 
			if not (x,y) in localBoard:
 
				localBoard[(x,y)]=moveNumber+1
 
				diagram.drawMove(x,y,moveNumber+1,c)
 
			# průsečík je obsazený nepopsaným kamenem
 
				diagram.addMove(x,y,c,moveNumber+1)
 
			# intersection occupied by an unlabeled stone
 
			elif localBoard[(x,y)]<1:
 
				# průsečík není popsaný ani písmenem
 
				# intersection not labeled even by a letter
 
				if not (x,y) in letters:
 
					letters[(x,y)]=letter
 
					color='b' if localBoard[(x,y)]==0 else 'w'
 
					diagram.drawMove(x,y,letter,color)
 
					color='b' if localBoard[(x,y)]==EMPTY else 'w'
 
					diagram.addMove(x,y,color,letter)
 
					letter=chr(ord(letter)+1)
 
				overlays+="{0} = {1}\n".format(moveNumber+1,letters[(x,y)])
 
			# průsečík je obsazený očíslovaným kamenem
 
			# intersection occupied by a numbered stone
 
			else: overlays+="{0} = {1}\n".format(moveNumber+1,localBoard[(x,y)])
 
			
 
			moveNumber+=1
 
			
 
		# dokonči a ulož diagram
 
	# TODO rozumně pracovat s adresáři
 
		t=open(os.path.join(os.path.dirname(__file__),"out","{0}-{1}.{2}".format(shortName,i+1,diagram.extension)),'w') # nový soubor
 
		t.write(diagram.getContent())
 
		# finish and save the diagram
 
		t=open(os.path.join("out","{0}-{1}.{2}".format(shortName,i+1,diagram.extension)),'w') # a new file
 
		t.write(diagram.render(template))
 
		t.close()
 
			
 
	notes=open(os.path.join(os.path.dirname(__file__),"out","{0}.txt".format(shortName)),'w')
 
	notes=open(os.path.join("out","{0}.txt".format(shortName)),'w')
 
	notes.write(overlays)
 
	notes.close()
 
	
src/drawer/svg.py
Show inline comments
 
modified file chmod 100644 => 100755
 
from jinja2 import Template
 

	
 

	
 
c=28
 
padding=15
 
highNumbers=True
 

	
 

	
 
class DiagramPoint:
 
	def __init__(self,x,y,color="",label=""):
 
		self.x=x
 
		self.y=y
 
		self.color=color
 
		self.label=label
 

	
 
	def __repr__(self):
 
		return 'DiagramPoint({0},{1},"{2}","{3}")'.format(self.x,self.y,self.color,self.label)
 

	
 

	
 
class Svg:
 
	content=""
 
	footer=""
 
	extension="svg"
 

	
 
	padding=15
 
	gridSize=28
 
	highNumbers=True
 
	
 
	def __init__(self):
 
		self.content='''<?xml version="1.0" standalone="no"?>
 
<svg width="{0}" height="{0}" version="1.1" xmlns="http://www.w3.org/2000/svg" alignment-baseline="center">
 
	<defs>
 
		<style type="text/css"><![CDATA[
 
		text{{font-family:"DejaVu Sans";text-anchor:middle;}}
 
		line{{stroke:black;stroke-width:0.7}}
 
		circle{{stroke:black}}
 
		.b{{fill:black}}
 
		.w{{fill:white}}
 
		]]></style>
 
	</defs>
 
	<rect width="{0}" height="{0}" x="0" y="0" style="fill:white;stroke:white"/>\n'''.format(2*self.padding+18*self.gridSize)
 
		self.footer="</svg>\n"
 
		self.boardSize=480
 
		self.padding=30
 

	
 
		self._index=dict()
 

	
 
	def addStone(self,x,y,color):
 
		self._index[(x,y)]=(len(self._index),DiagramPoint(x,y,color))
 

	
 
		grid='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" />\n'
 
	def addMove(self,x,y,color,label):
 
		if (not self.highNumbers) and isinstance(label,int) and label%100!=0:
 
			label%=100
 

	
 
		# okraje desky
 
		for i in (0,18):
 
			self.content+='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" style="stroke-width:1"/>\n'.format(self.padding, 18*self.gridSize+self.padding, self.gridSize*i+self.padding, self.gridSize*i+self.padding)
 
			self.content+='  <line x1="{0}" x2="{1}" y1="{2}" y2="{3}" style="stroke-width:1"/>\n'.format(self.gridSize*i+self.padding, self.gridSize*i+self.padding, self.padding, 18*self.gridSize+self.padding)
 
		
 
		# mřížka
 
		for i in range(1,18):
 
			self.content+=grid.format(self.padding, 18*self.gridSize+self.padding, c*i+self.padding, c*i+self.padding)
 
			self.content+=grid.format(c*i+self.padding, c*i+self.padding, self.padding, 18*c+self.padding)
 
		self._index[(x,y)]=(len(self._index),DiagramPoint(x,y,color,label))
 

	
 
	def addLabel(self,x,y,label):
 
		self._index[(x,y)]=(len(self._index),DiagramPoint(x,y,"",label))
 

	
 
		# hvězdy
 
		for i in range(3):
 
			for j in range(3):
 
				self.content+='  <circle cx="{0}" cy="{1}" r="{2}" class="b"/>\n'.format(padding+3*c+6*i*c, padding+3*c+6*j*c, 2)
 
	
 
	def __str__(self):
 
		return self.content+self.footer
 
					
 
	def drawStone(self,x,y,color):
 
		self.content+='  <circle cx="{0}" cy="{1}" r="{2}" class="{3}" />\n'.format(padding+x*c, padding+y*c, c/2-1, color)
 
	def render(self, template):
 
		points = [p for (i,p) in sorted(self._index.values())]
 

	
 
	def getFontSize(self,text):
 
		if len(text)<2: return round(0.7*c)
 
		elif len(text)<3: return round(0.55*c)
 
		else: return round(0.4*c)
 
		stones = [p for p in points if p.color and not p.label]
 
		moves = [p for p in points if p.color and p.label]
 
		labels = [p for p in points if not p.color and p.label]
 

	
 
		svg = Template(template)
 
		params = {"boardSize":self.boardSize, "padding":self.padding, "stones":stones, "moves":moves, "labels":labels}
 

	
 
	def writeLabel(self,x,y,label,color):
 
		label=str(label)
 
		fontSize=self.getFontSize(label)
 
		self.content+='  <text x="{0}" y="{1}" class="{2}" font-size="{3}">{4}</text>\n'.format(padding+x*c, padding+y*c+0.35*fontSize, color, fontSize, label)
 
		
 
	def drawMove(self,x,y,label,color):
 
		labelColor="w" if color=="b" else "b"
 
		
 
		if (not self.highNumbers) and isinstance(label,int) and label%100!=0: label=label%100 # dost neobratná logika
 
		
 
		self.drawStone(x,y,color)
 
		self.writeLabel(x,y,label,labelColor)
 
		
 
	def getContent(self):
 
		return self.content+self.footer
 
		return svg.render(params)
 

	
 
	# def getFontSize(self,text):
 
	# 	if len(text)<2: return round(0.7*c)
 
	# 	elif len(text)<3: return round(0.55*c)
 
	# 	else: return round(0.4*c)
 
	#
 
	# def writeLabel(self,x,y,label,color):
 
	# 	label=str(label)
 
	# 	fontSize=self.getFontSize(label)
 
	# 	self.content+='  <text x="{0}" y="{1}" class="{2}" font-size="{3}">{4}</text>\n'.format(padding+x*c, padding+y*c+0.35*fontSize, color, fontSize, label)
src/go.py
Show inline comments
 
class Go:
 
	# 1: B, 0: _, -1: W
 
	# resp. jakákoli čísla s opačnými znaménky
 
	board=[[0]*19 for i in range(19)]
 
BLACK=1
 
WHITE=-1
 
EMPTY=0
 
 
 
class Go:
 
	board=[[EMPTY]*19 for i in range(19)]
 
	
 
	def __init__(self): self.board=[[0]*19 for i in range(19)]
 
	def __init__(self): self.board=[[EMPTY]*19 for i in range(19)]
 
	
 
	def move(self,color,y,x):
 
		if self.board[x][y]!=0: return False
 
		if self.board[x][y]!=EMPTY: return False
 
 
		self.board[x][y]=color
 
 
@@ -15,14 +18,14 @@
 
			if not self._floodFill(-color,x+i,y+j): self._remove()
 
		self.temp=[[False]*19 for i in range(19)]
 
		if not self._floodFill(color,x,y):
 
			self.board[x][y]=0
 
			self.board[x][y]=EMPTY
 
			return False
 
		return True
 
 
	def _floodFill(self,color,x,y):
 
		if x<0 or x>18 or y<0 or y>18: return False
 
		if self.temp[x][y]: return False
 
		if self.board[x][y]==0: return True
 
		if self.board[x][y]==EMPTY: return True
 
		if self.board[x][y]!=color: return False
 
		self.temp[x][y]=True
 
		return self._floodFill(color,x-1,y) or self._floodFill(color,x+1,y) or self._floodFill(color,x,y-1) or self._floodFill(color,x,y+1)
 
@@ -30,4 +33,4 @@
 
	def _remove(self):
 
		for i in range(19):
 
			for j in range(19):
 
				if self.temp[i][j]: self.board[i][j]=0
 
				if self.temp[i][j]: self.board[i][j]=EMPTY
src/sgfParser/node.py
Show inline comments
 
@@ -67,8 +67,8 @@ class Node:
 
		res.setChildren(self.children[:])
 
		return res
 

	
 
	def getProperty(self,name):
 
		if name in self.properties: return self.properties[name]
 
	def getProp(self,name):
 
		if name in self.properties: return self.properties[name].value
 
		else: return None
 

	
 
	## Returns textual representation of the Node itself, but disregards its children.
src/templ/templ.svg
Show inline comments
 
new file 100755
 
<?xml version="1.0" standalone="no"?>
 
{% set cellSize=boardSize/18 %}
 
{% set stoneRadius=cellSize/2-1 %}
 

	
 
<svg width="{{boardSize+padding*2}}" height="{{boardSize+padding*2}}" version="1.1" xmlns="http://www.w3.org/2000/svg" alignment-baseline="center">
 
	<defs>
 
		<style type="text/css"><![CDATA[
 
			rect{fill:white;stroke:black;stroke-width:1}
 
			text{font-family:"DejaVu Sans";text-anchor:middle;}
 
			line{stroke:black;stroke-width:0.7}
 
			circle{stroke:black}
 
			circle.b, .b circle, .w text, .e text{fill:black}
 
			circle.w, .w circle, .b text{fill:white}
 
			.e circle {stroke:none;fill:white}
 
		]]></style>
 
	</defs>
 
	<g width="{{boardSize}}" height="{{boardSize}}" transform="translate({{padding}},{{padding}})">
 
		<rect x="0" y="0" width="{{boardSize}}" height="{{boardSize}}"/>
 
		{# grid #}
 
		{% for i in range(1,18) %}
 
			<line x1="0" x2="{{boardSize}}" y1="{{(i*cellSize) | round(2)}}" y2="{{(i*cellSize) | round(2)}}"/>
 
			<line x1="{{(i*cellSize) | round(2)}}" x2="{{(i*cellSize) | round(2)}}" y1="0" y2="{{boardSize}}"/>
 
		{% endfor %}
 

	
 
		{# stars #}
 
		{% for y in [3,9,15] %}
 
			{% for x in [3,9,15] %}
 
		  <circle cx="{{(x*cellSize) | round(2)}}" cy="{{(y*cellSize) | round(2)}}" r="2" class="b"/>
 
			{% endfor %}
 
		{% endfor %}
 

	
 
		{# stones #}
 
		{% for p in stones %}
 
			<circle cx="{{(p.x*cellSize) | round(2)}}" cy="{{(p.y*cellSize) | round(2)}}" r="{{stoneRadius|round(2)}}" class="{{p.color}}"/>
 
		{% endfor %}
 

	
 
		{# moves #}
 
		{% for p in moves %}
 
			<g width="{{cellSize|round(2)}}" height="{{cellSize|round(2)}}" class="{{p.color}}" transform="translate({{(p.x*cellSize) | round(2)}},{{(p.y*cellSize) | round(2)}})">
 
				<circle cx="0" cy="0" r="{{stoneRadius|round(2)}}"/>
 
				<text y="0.4em">{{p.label}}</text>
 
			</g>
 
		{% endfor %}
 

	
 
		{# labels #}
 
		{% for p in labels %}
 
			<g width="{{cellSize|round(2)}}" height="{{cellSize|round(2)}}" class="e" transform="translate({{(p.x*cellSize) | round(2)}},{{(p.y*cellSize) | round(2)}})">
 
				<circle cx="0" cy="0" r="{{stoneRadius|round(2)}}"/>
 
				<text y="0.4em">{{p.label}}</text>
 
			</g>
 
		{% endfor %}
 
	</g>
 
</svg>
0 comments (0 inline, 0 general)