diff --git a/lasercut/export/goban19x19-300x200-300x100-base-0-0.svg b/lasercut/export/goban19x19-300x200-300x100-base-0-0.svg
new file mode 100644
index 0000000..77e4a08
--- /dev/null
+++ b/lasercut/export/goban19x19-300x200-300x100-base-0-0.svg
@@ -0,0 +1,265 @@
+
+
\ No newline at end of file
diff --git a/lasercut/export/goban19x19-300x200-300x100-base-1-0.svg b/lasercut/export/goban19x19-300x200-300x100-base-1-0.svg
new file mode 100644
index 0000000..247e70b
--- /dev/null
+++ b/lasercut/export/goban19x19-300x200-300x100-base-1-0.svg
@@ -0,0 +1,129 @@
+
+
\ No newline at end of file
diff --git a/lasercut/export/goban19x19-300x200-300x100-recto-0-0.svg b/lasercut/export/goban19x19-300x200-300x100-recto-0-0.svg
new file mode 100644
index 0000000..55e150a
--- /dev/null
+++ b/lasercut/export/goban19x19-300x200-300x100-recto-0-0.svg
@@ -0,0 +1,1596 @@
+
+
\ No newline at end of file
diff --git a/lasercut/export/goban19x19-300x200-300x100-recto-1-0.svg b/lasercut/export/goban19x19-300x200-300x100-recto-1-0.svg
new file mode 100644
index 0000000..3f32fa0
--- /dev/null
+++ b/lasercut/export/goban19x19-300x200-300x100-recto-1-0.svg
@@ -0,0 +1,576 @@
+
+
\ No newline at end of file
diff --git a/lasercut/export/goban19x19-300x200-300x100-verso-0-0.svg b/lasercut/export/goban19x19-300x200-300x100-verso-0-0.svg
new file mode 100644
index 0000000..f8db3dd
--- /dev/null
+++ b/lasercut/export/goban19x19-300x200-300x100-verso-0-0.svg
@@ -0,0 +1,368 @@
+
+
\ No newline at end of file
diff --git a/lasercut/export/goban19x19-300x200-300x100-verso-1-0.svg b/lasercut/export/goban19x19-300x200-300x100-verso-1-0.svg
new file mode 100644
index 0000000..b87f763
--- /dev/null
+++ b/lasercut/export/goban19x19-300x200-300x100-verso-1-0.svg
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/lasercut/goban-lasercut.py b/lasercut/goban-lasercut.py
index 54e50cc..faa0987 100644
--- a/lasercut/goban-lasercut.py
+++ b/lasercut/goban-lasercut.py
@@ -1,70 +1,116 @@
#!/usr/bin/env python3
+#pylint: disable=too-many-instance-attributes
+
import argparse
import math
+from copy import copy
+
import drawSvg as draw
-def is_hoshi(pos, size):
+class Part:
"""
- Is this position a hoshi ?
+ Part of goban
"""
- for axis in (0, 1):
- if size[axis] > 10:
- if pos[axis] not in (3, round(size[axis]/2)-1, size[axis]-4):
- return False
- elif pos[axis] not in (2, round(size[axis]/2)-1, size[axis]-3):
- return False
-
- return True
-
-
-class GobanDraw:
- """
- Drawing a goban and its stones.
-
- @param config: Config from argparse
- """
- def __init__(self, config) -> None:
+ def __init__(self, stones, pos, stonesOffset, config):
+ self.stones = stones
+ self.pos = pos
+ self.stonesOffset = stonesOffset
self.config = config
self.scale = config.resolution
- self.total_size = (
- ((config.size[0]-1)*config.cellsize[0]+
- config.borders[1]+config.borders[3]+
- 2*config.stone_radius+2*config.stone_margin)*self.scale,
- ((config.size[1]-1)*config.cellsize[1]+
- config.borders[0]+config.borders[2]+
- 2*config.stone_radius+2*config.stone_margin)*self.scale)
+ self.size = [
+ (self.stones[0]-1)*config.cellsize[0],
+ (self.stones[1]-1)*config.cellsize[1]
+ ]
- print("Total size: %dx%d" % self.total_size)
+ borders_map = ((3, 1), (0, 2))
- self.recto_drawing = draw.Drawing(*self.total_size)
- self.verso_drawing = draw.Drawing(*self.total_size)
- self.base_drawing = draw.Drawing(*self.total_size)
+ total_stones = [
+ stonesOffset[0] + self.stones[0],
+ stonesOffset[1] + self.stones[1]
+ ]
+
+ self.is_first = [
+ self.pos[0] == 0,
+ self.pos[1] == 0
+ ]
+ self.is_last = [
+ total_stones[0] == config.size[0],
+ total_stones[1] == config.size[1]
+ ]
+
+ for axis in (0, 1):
+ if self.is_first[axis]:
+ self.size[axis] += config.borders[borders_map[axis][0]]
+ self.size[axis] += config.stone_radius + config.stone_margin
+ else:
+ self.size[axis] += config.cellsize[axis]/2
+
+ if self.is_last[axis]:
+ self.size[axis] += config.borders[borders_map[axis][1]]
+ self.size[axis] += config.stone_radius + config.stone_margin
+ else:
+ self.size[axis] += config.cellsize[axis]/2
+
+ self.offset = [0, 0]
+ for axis in (0, 1):
+ if self.is_first[axis]:
+ self.offset[axis] += config.borders[borders_map[axis][0]]
+ self.offset[axis] += config.stone_radius + config.stone_margin
+ else:
+ self.offset[axis] += config.cellsize[axis]/2
+
+ self.size = [size*self.scale for size in self.size]
+ self.offset = [offset*self.scale for offset in self.offset]
+
+ self.recto_drawing = draw.Drawing(*self.size)
+ self.verso_drawing = draw.Drawing(*self.size)
+ self.base_drawing = draw.Drawing(*self.size)
+
+ def is_hoshi(self, pos: list):
+ """
+ Is this position a hoshi ?
+ """
+ size = self.config.size
+ pos = [
+ pos[0] + self.stonesOffset[0],
+ pos[1] + self.stonesOffset[1]
+ ]
+ for axis in (0, 1):
+ if size[axis] > 10:
+ if pos[axis] not in (3, round(size[axis]/2)-1, size[axis]-4):
+ return False
+ elif pos[axis] not in (2, round(size[axis]/2)-1, size[axis]-3):
+ return False
+
+ return True
+
+ def is_engraved(self, pos: list) -> bool:
+ """ Is the stone at pos engraved ? """
+ done = self.stonesOffset[0] + pos[0]
+ done *= self.config.size[0]
+ done += self.stonesOffset[1] + pos[1]
+ return done < self.config.engraved_stones
def drawEngraving(self) -> None:
"""
Draw engraving of stones and goban
"""
- if not self.config.engrave:
- return
-
- engraved_stones = 0
- borders = [b*self.scale for b in self.config.borders]
cellsize = [s*self.scale for s in self.config.cellsize]
radius = self.config.stone_radius * self.scale
margin = self.config.stone_margin * self.scale
thickness = self.config.thickness * self.scale
mark_thickness = self.config.mark_thickness * self.scale
color = self.config.engrave_color
- for x in range(self.config.size[0]):
- for y in range(self.config.size[1]):
+ for x in range(self.stones[0]):
+ for y in range(self.stones[1]):
# pos(recto_x, recto_y, verso_x, verso_y)
pos = (
- borders[3]+x*cellsize[0]+radius+margin,
- borders[0]+y*cellsize[1]+radius+margin,
- self.total_size[0]-borders[3]-x*cellsize[0]-radius-margin,
- borders[0]+y*cellsize[1]+radius+margin
+ self.offset[0]+x*cellsize[0],
+ self.offset[1]+y*cellsize[1],
+ self.size[0]-self.offset[0]-x*cellsize[0],
+ self.offset[1]+y*cellsize[1]
)
# Draw grid on recto
if x > 0:
@@ -89,8 +135,52 @@ class GobanDraw:
stroke_width=thickness
)
)
+ if x == 0 and not self.is_first[0]:
+ self.recto_drawing.append(
+ draw.Line(
+ pos[0]-radius-margin,
+ pos[1],
+ pos[0]-cellsize[0]/2,
+ pos[1],
+ stroke=color,
+ stroke_width=thickness
+ )
+ )
+ if y == 0 and not self.is_first[1]:
+ self.recto_drawing.append(
+ draw.Line(
+ pos[0],
+ pos[1]-radius-margin,
+ pos[0],
+ pos[1]-cellsize[1]/2,
+ stroke=color,
+ stroke_width=thickness
+ )
+ )
+ if x == self.stones[0]-1 and not self.is_last[0]:
+ self.recto_drawing.append(
+ draw.Line(
+ pos[0]+radius+margin,
+ pos[1],
+ pos[0]+cellsize[0]/2,
+ pos[1],
+ stroke=color,
+ stroke_width=thickness
+ )
+ )
+ if y == self.stones[1]-1 and not self.is_last[1]:
+ self.recto_drawing.append(
+ draw.Line(
+ pos[0],
+ pos[1]+radius+margin,
+ pos[0],
+ pos[1]+cellsize[1]/2,
+ stroke=color,
+ stroke_width=thickness
+ )
+ )
# Draw engraving on stones (recto and verso)
- if engraved_stones < self.config.engraved_stones:
+ if self.is_engraved([x, y]):
engraved_radius = mark_thickness
while engraved_radius < radius:
self.recto_drawing.append(
@@ -114,10 +204,9 @@ class GobanDraw:
)
)
engraved_radius += 2*mark_thickness
- engraved_stones += 1
# Draw Hoshi on base
- if is_hoshi((x, y), self.config.size):
+ if self.is_hoshi((x, y)):
self.base_drawing.append(
draw.Circle(
pos[0],
@@ -133,123 +222,87 @@ class GobanDraw:
"""
Draw cutting of stones and goban
"""
- if not self.config.cut:
- return
-
- borders = [b*self.scale for b in self.config.borders]
+ corner_radius = min(self.config.borders)*self.scale / 2
cellsize = [s*self.scale for s in self.config.cellsize]
radius = self.config.stone_radius * self.scale
margin = self.config.stone_margin * self.scale
- corner_radius = min(borders) / 2
thickness = self.config.cut_thickness * self.scale
color = self.config.cut_color
+ corners = [ # x, y, x, y, start, end
+ (0, 0, 0, 0, 0, 0),
+ (self.size[0], 0, self.size[0], 0, 0, 0),
+ (self.size[0], self.size[1], self.size[0], self.size[1], 0, 0),
+ (0, self.size[1], 0, self.size[1], 0, 0)
+ ]
# Cut corners on recto and base
- if self.config.round_corners:
- self.recto_drawing.append(
- draw.Arc(
- corner_radius,
- corner_radius,
- corner_radius,
- 180,
- 270,
- stroke=color,
- stroke_width=thickness,
- fill='none'
+ for drawing in (self.base_drawing, self.recto_drawing):
+ if not self.config.no_round_corners:
+ if self.is_first[0] and self.is_first[1]:
+ corners[0] = (
+ corner_radius, 0,
+ 0, corner_radius,
+ 180, 270
+ )
+
+ if self.is_last[0] and self.is_first[1]:
+ corners[1] = (
+ self.size[0]-corner_radius, 0,
+ self.size[0], corner_radius,
+ 270, 0
+ )
+
+ if self.is_first[0] and self.is_last[1]:
+ corners[3] = (
+ corner_radius, self.size[1],
+ 0, self.size[1]-corner_radius,
+ 90, 180
+ )
+
+ if self.is_last[0] and self.is_last[1]:
+ corners[2] = (
+ self.size[0]-corner_radius, self.size[1],
+ self.size[0], self.size[1]-corner_radius,
+ 0, 90
+ )
+
+ # Cut perimeter
+ for (idx, corner) in enumerate(corners):
+ for drawing in (self.base_drawing, self.recto_drawing):
+ axis = 2 if idx%2==0 else 0
+ drawing.append(
+ draw.Line(
+ corners[idx-1][0+axis],
+ corners[idx-1][1+axis],
+ corner[0+axis],
+ corner[1+axis],
+ stroke=color,
+ stroke_width=thickness
+ )
)
- )
- self.base_drawing.append(
- draw.Arc(
- corner_radius,
- corner_radius,
- corner_radius,
- 180,
- 270,
- stroke=color,
- stroke_width=thickness,
- fill='none'
- )
- )
- self.recto_drawing.append(
- draw.Arc(
- self.total_size[0]-corner_radius,
- corner_radius,
- corner_radius,
- 270,
- 0,
- stroke=color,
- stroke_width=thickness,
- fill='none'
- )
- )
- self.base_drawing.append(
- draw.Arc(
- self.total_size[0]-corner_radius,
- corner_radius,
- corner_radius,
- 270,
- 0,
- stroke=color,
- stroke_width=thickness,
- fill='none'
- )
- )
- self.recto_drawing.append(
- draw.Arc(
- corner_radius,
- self.total_size[1]-corner_radius,
- corner_radius,
- 90,
- 180,
- stroke=color,
- stroke_width=thickness,
- fill='none'
- )
- )
- self.base_drawing.append(
- draw.Arc(
- corner_radius,
- self.total_size[1]-corner_radius,
- corner_radius,
- 90,
- 180,
- stroke=color,
- stroke_width=thickness,
- fill='none'
- )
- )
- self.recto_drawing.append(
- draw.Arc(
- self.total_size[0]-corner_radius,
- self.total_size[1]-corner_radius,
- corner_radius,
- 0,
- 90,
- stroke=color,
- stroke_width=thickness,
- fill='none'
- )
- )
- self.base_drawing.append(
- draw.Arc(
- self.total_size[0]-corner_radius,
- self.total_size[1]-corner_radius,
- corner_radius,
- 0,
- 90,
- stroke=color,
- stroke_width=thickness,
- fill='none'
- )
- )
+ if corner[4] != corner[5]:
+ drawing.append(
+ draw.Arc(
+ corner[0],
+ corner[3],
+ corner_radius,
+ corner[4],
+ corner[5],
+ stroke=color,
+ stroke_width=thickness,
+ fill='none'
+ )
+ )
# Cut stones on recto and holes on base
- for x in range(self.config.size[0]):
- for y in range(self.config.size[1]):
+ for x in range(self.stones[0]):
+ for y in range(self.stones[1]):
# pos(recto_x, recto_y)
pos = (
- borders[3]+x*cellsize[0]+radius+margin,
- borders[0]+y*cellsize[1]+radius+margin,
+ self.offset[0]+x*cellsize[0],
+ self.offset[1]+y*cellsize[1],
+ self.size[0]-self.offset[0]-x*cellsize[0],
+ self.offset[1]+y*cellsize[1]
)
self.recto_drawing.append(
@@ -301,9 +354,67 @@ class GobanDraw:
"""
Save drawing.
"""
- self.recto_drawing.saveSvg(self.config.prefix+'recto.svg')
- self.verso_drawing.saveSvg(self.config.prefix+'verso.svg')
- self.base_drawing.saveSvg(self.config.prefix+'base.svg')
+ prefix = self.config.prefix
+ suffix = f'-{self.pos[0]}-{self.pos[1]}'
+ self.recto_drawing.saveSvg(prefix+'recto'+suffix+'.svg')
+ self.verso_drawing.saveSvg(prefix+'verso'+suffix+'.svg')
+ self.base_drawing.saveSvg(prefix+'base'+suffix+'.svg')
+
+
+class GobanDraw:
+ """
+ Drawing a goban and its stones.
+
+ @param config: Config from argparse
+ """
+ def __init__(self, config) -> None:
+ self.config = config
+ self.parts = []
+
+ stones = list(config.size)
+ part_stones = [0, 0]
+ offset = [0, 0]
+ x = 0
+ y = 0
+
+ while stones[0] > 0:
+ stones[1] = config.size[1]
+ offset[1] = 0
+ y = 0
+ while stones[1] > 0:
+ part_stones = (
+ min(config.max_part_size[0], config.size[0]-offset[0]),
+ min(config.max_part_size[1], config.size[1]-offset[1])
+ )
+ self.parts.append(
+ Part(
+ part_stones,
+ (x, y),
+ copy(offset),
+ config
+ )
+ )
+ offset[1] += part_stones[1]
+ stones[1] -= part_stones[1]
+ y += 1
+
+ offset[0] += part_stones[0]
+ stones[0] -= part_stones[0]
+ x += 1
+
+ for part in self.parts:
+ print(f"Part size: {part.size[0]}x{part.size[1]}")
+
+ if not self.config.no_engrave:
+ part.drawEngraving()
+
+ if not self.config.no_cut:
+ part.drawCutting()
+
+ def save(self):
+ """ Save all sketches """
+ for part in self.parts:
+ part.save()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
@@ -313,6 +424,10 @@ if __name__ == "__main__":
'--size', type=int, nargs=2, default=(19, 19),
help="Size of goban (number of stones)", metavar=('X', 'Y')
)
+ parser.add_argument(
+ '--max-part-size', type=int, nargs=2, default=(19, 19),
+ help="Max size of part of goban (number of stones)", metavar=('X', 'Y')
+ )
parser.add_argument(
'--cellsize', type=float, nargs=2, default=(15, 15),
help="Size of one cell in mm", metavar=('X', 'Y')
@@ -343,16 +458,16 @@ if __name__ == "__main__":
help="Thickness of cut stroke in mm"
)
parser.add_argument(
- '--engrave', default=False, action='store_true',
- help='Enable engraving'
+ '--no-engrave', default=False, action='store_true',
+ help='Disable engraving'
)
parser.add_argument(
- '--cut', default=False, action='store_true',
- help='Enable cutting'
+ '--no-cut', default=False, action='store_true',
+ help='Disable cutting'
)
parser.add_argument(
- '--round-corners', default=False, action='store_true',
- help='Round corners for goban'
+ '--no-round-corners', default=False, action='store_true',
+ help='No round corners for goban'
)
parser.add_argument(
'--engrave-color', type=str, default='#000000',
@@ -378,6 +493,4 @@ if __name__ == "__main__":
)
conf = parser.parse_args()
goban = GobanDraw(conf)
- goban.drawEngraving()
- goban.drawCutting()
goban.save()