|
|
|
@ -6,13 +6,14 @@ python gen_pattern.py -o out.svg -r 11 -c 8 -T circles -s 20.0 -R 5.0 -u mm -w 2 |
|
|
|
|
-o, --output - output file (default out.svg) |
|
|
|
|
-r, --rows - pattern rows (default 11) |
|
|
|
|
-c, --columns - pattern columns (default 8) |
|
|
|
|
-T, --type - type of pattern, circles, acircles, checkerboard (default circles) |
|
|
|
|
-T, --type - type of pattern, circles, acircles, checkerboard, radon_checkerboard (default circles) |
|
|
|
|
-s, --square_size - size of squares in pattern (default 20.0) |
|
|
|
|
-R, --radius_rate - circles_radius = square_size/radius_rate (default 5.0) |
|
|
|
|
-u, --units - mm, inches, px, m (default mm) |
|
|
|
|
-w, --page_width - page width in units (default 216) |
|
|
|
|
-h, --page_height - page height in units (default 279) |
|
|
|
|
-a, --page_size - page size (default A4), supersedes -h -w arguments |
|
|
|
|
-m, --markers - list of cells with markers for the radon checkerboard |
|
|
|
|
-H, --help - show help |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
@ -22,7 +23,7 @@ from svgfig import * |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PatternMaker: |
|
|
|
|
def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height): |
|
|
|
|
def __init__(self, cols, rows, output, units, square_size, radius_rate, page_width, page_height, markers): |
|
|
|
|
self.cols = cols |
|
|
|
|
self.rows = rows |
|
|
|
|
self.output = output |
|
|
|
@ -31,6 +32,7 @@ class PatternMaker: |
|
|
|
|
self.radius_rate = radius_rate |
|
|
|
|
self.width = page_width |
|
|
|
|
self.height = page_height |
|
|
|
|
self.markers = markers |
|
|
|
|
self.g = SVG("g") # the svg group container |
|
|
|
|
|
|
|
|
|
def make_circles_pattern(self): |
|
|
|
@ -70,6 +72,74 @@ class PatternMaker: |
|
|
|
|
height=spacing, fill="black", stroke="none") |
|
|
|
|
self.g.append(square) |
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
|
def _make_round_rect(x, y, diam, corners=("right", "right", "right", "right")): |
|
|
|
|
rad = diam / 2 |
|
|
|
|
cw_point = ((0, 0), (diam, 0), (diam, diam), (0, diam)) |
|
|
|
|
mid_cw_point = ((0, rad), (rad, 0), (diam, rad), (rad, diam)) |
|
|
|
|
res_str = "M{},{} ".format(x + mid_cw_point[0][0], y + mid_cw_point[0][1]) |
|
|
|
|
n = len(cw_point) |
|
|
|
|
for i in range(n): |
|
|
|
|
if corners[i] == "right": |
|
|
|
|
res_str += "L{},{} L{},{} ".format(x + cw_point[i][0], y + cw_point[i][1], |
|
|
|
|
x + mid_cw_point[(i + 1) % n][0], y + mid_cw_point[(i + 1) % n][1]) |
|
|
|
|
elif corners[i] == "round": |
|
|
|
|
res_str += "A{},{} 0,0,1 {},{} ".format(rad, rad, x + mid_cw_point[(i + 1) % n][0], |
|
|
|
|
y + mid_cw_point[(i + 1) % n][1]) |
|
|
|
|
else: |
|
|
|
|
raise TypeError("unknown corner type") |
|
|
|
|
return res_str |
|
|
|
|
|
|
|
|
|
def _get_type(self, x, y): |
|
|
|
|
corners = ["right", "right", "right", "right"] |
|
|
|
|
is_inside = True |
|
|
|
|
if x == 0: |
|
|
|
|
corners[0] = "round" |
|
|
|
|
corners[3] = "round" |
|
|
|
|
is_inside = False |
|
|
|
|
if y == 0: |
|
|
|
|
corners[0] = "round" |
|
|
|
|
corners[1] = "round" |
|
|
|
|
is_inside = False |
|
|
|
|
if x == self.cols - 1: |
|
|
|
|
corners[1] = "round" |
|
|
|
|
corners[2] = "round" |
|
|
|
|
is_inside = False |
|
|
|
|
if y == self.rows - 1: |
|
|
|
|
corners[2] = "round" |
|
|
|
|
corners[3] = "round" |
|
|
|
|
is_inside = False |
|
|
|
|
return corners, is_inside |
|
|
|
|
|
|
|
|
|
def make_radon_checkerboard_pattern(self): |
|
|
|
|
spacing = self.square_size |
|
|
|
|
xspacing = (self.width - self.cols * self.square_size) / 2.0 |
|
|
|
|
yspacing = (self.height - self.rows * self.square_size) / 2.0 |
|
|
|
|
for x in range(0, self.cols): |
|
|
|
|
for y in range(0, self.rows): |
|
|
|
|
if x % 2 == y % 2: |
|
|
|
|
corner_types, is_inside = self._get_type(x, y) |
|
|
|
|
if is_inside: |
|
|
|
|
square = SVG("rect", x=x * spacing + xspacing, y=y * spacing + yspacing, width=spacing, |
|
|
|
|
height=spacing, fill="black", stroke="none") |
|
|
|
|
else: |
|
|
|
|
square = SVG("path", d=self._make_round_rect(x * spacing + xspacing, y * spacing + yspacing, |
|
|
|
|
spacing, corner_types), fill="black", stroke="none") |
|
|
|
|
self.g.append(square) |
|
|
|
|
if self.markers is not None: |
|
|
|
|
r = self.square_size * 0.17 |
|
|
|
|
pattern_width = ((self.cols - 1.0) * spacing) + (2.0 * r) |
|
|
|
|
pattern_height = ((self.rows - 1.0) * spacing) + (2.0 * r) |
|
|
|
|
x_spacing = (self.width - pattern_width) / 2.0 |
|
|
|
|
y_spacing = (self.height - pattern_height) / 2.0 |
|
|
|
|
for x, y in self.markers: |
|
|
|
|
color = "black" |
|
|
|
|
if x % 2 == y % 2: |
|
|
|
|
color = "white" |
|
|
|
|
dot = SVG("circle", cx=(x * spacing) + x_spacing + r, |
|
|
|
|
cy=(y * spacing) + y_spacing + r, r=r, fill=color, stroke="none") |
|
|
|
|
self.g.append(dot) |
|
|
|
|
|
|
|
|
|
def save(self): |
|
|
|
|
c = canvas(self.g, width="%d%s" % (self.width, self.units), height="%d%s" % (self.height, self.units), |
|
|
|
|
viewBox="0 0 %d %d" % (self.width, self.height)) |
|
|
|
@ -85,7 +155,7 @@ def main(): |
|
|
|
|
type=int) |
|
|
|
|
parser.add_argument("-r", "--rows", help="pattern rows", default="11", action="store", dest="rows", type=int) |
|
|
|
|
parser.add_argument("-T", "--type", help="type of pattern", default="circles", action="store", dest="p_type", |
|
|
|
|
choices=["circles", "acircles", "checkerboard"]) |
|
|
|
|
choices=["circles", "acircles", "checkerboard", "radon_checkerboard"]) |
|
|
|
|
parser.add_argument("-u", "--units", help="length unit", default="mm", action="store", dest="units", |
|
|
|
|
choices=["mm", "inches", "px", "m"]) |
|
|
|
|
parser.add_argument("-s", "--square_size", help="size of squares in pattern", default="20.0", action="store", |
|
|
|
@ -96,8 +166,12 @@ def main(): |
|
|
|
|
dest="page_width", type=float) |
|
|
|
|
parser.add_argument("-h", "--page_height", help="page height in units", default=argparse.SUPPRESS, action="store", |
|
|
|
|
dest="page_height", type=float) |
|
|
|
|
parser.add_argument("-a", "--page_size", help="page size, superseded if -h and -w are set", default="A4", action="store", |
|
|
|
|
dest="page_size", choices=["A0", "A1", "A2", "A3", "A4", "A5"]) |
|
|
|
|
parser.add_argument("-a", "--page_size", help="page size, superseded if -h and -w are set", default="A4", |
|
|
|
|
action="store", dest="page_size", choices=["A0", "A1", "A2", "A3", "A4", "A5"]) |
|
|
|
|
parser.add_argument("-m", "--markers", help="list of cells with markers for the radon checkerboard. Marker " |
|
|
|
|
"coordinates as list of numbers: -m 1 2 3 4 means markers in cells " |
|
|
|
|
"[1, 2] and [3, 4]", |
|
|
|
|
action="store", dest="markers", nargs="+", type=int) |
|
|
|
|
args = parser.parse_args() |
|
|
|
|
|
|
|
|
|
show_help = args.show_help |
|
|
|
@ -121,10 +195,19 @@ def main(): |
|
|
|
|
"A5": [148, 210]} |
|
|
|
|
page_width = page_sizes[page_size][0] |
|
|
|
|
page_height = page_sizes[page_size][1] |
|
|
|
|
pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height) |
|
|
|
|
if len(args.markers) % 2 == 1: |
|
|
|
|
raise ValueError("The length of the markers array={} must be even".format(len(args.markers))) |
|
|
|
|
markers = set() |
|
|
|
|
for x, y in zip(args.markers[::2], args.markers[1::2]): |
|
|
|
|
if x in range(0, columns) and y in range(0, rows): |
|
|
|
|
markers.add((x, y)) |
|
|
|
|
else: |
|
|
|
|
raise ValueError("The marker {},{} is outside the checkerboard".format(x, y)) |
|
|
|
|
|
|
|
|
|
pm = PatternMaker(columns, rows, output, units, square_size, radius_rate, page_width, page_height, markers) |
|
|
|
|
# dict for easy lookup of pattern type |
|
|
|
|
mp = {"circles": pm.make_circles_pattern, "acircles": pm.make_acircles_pattern, |
|
|
|
|
"checkerboard": pm.make_checkerboard_pattern} |
|
|
|
|
"checkerboard": pm.make_checkerboard_pattern, "radon_checkerboard": pm.make_radon_checkerboard_pattern} |
|
|
|
|
mp[p_type]() |
|
|
|
|
# this should save pattern to output |
|
|
|
|
pm.save() |
|
|
|
|