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()