The new algorithm places circles with more dependencies so that the spacing between kitty corner and circles on the same line are the same.
135 lines
5.4 KiB
Python
135 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import inkex
|
|
import math
|
|
|
|
class HoneyCombExtension(inkex.EffectExtension):
|
|
def add_arguments(self, pars):
|
|
pars.add_argument("--spacing", type=float, default=0.0)
|
|
pars.add_argument("--count_x", type=int, default=0)
|
|
pars.add_argument("--count_y", type=int, default=0)
|
|
|
|
def effect(self):
|
|
|
|
if len(self.svg.selection) != 2:
|
|
inkex.errormsg("Must select a bounding box and a circle to honeycomb")
|
|
return
|
|
|
|
target = self.svg.selection.popitem()[1]
|
|
bounding_box = self.svg.selection.popitem()[1]
|
|
|
|
# Validate bounding box
|
|
if bounding_box is None:
|
|
inkex.errormsg("Must have a bounding box selected")
|
|
return
|
|
bounding_box_h = float(bounding_box.get("height"))
|
|
bounding_box_w = float(bounding_box.get("width"))
|
|
bounding_box_x = float(bounding_box.get("x"))
|
|
bounding_box_y = float(bounding_box.get("y"))
|
|
bounding_box_style = bounding_box.get("style")
|
|
|
|
if (bounding_box_h and bounding_box_w and bounding_box_x and bounding_box_y) \
|
|
is None:
|
|
inkex.errormsg("Bounding Box must be the first selection")
|
|
return
|
|
|
|
# Validate honeycomb target
|
|
if target is None:
|
|
inkex.errormsg("Must make a selection of an object to honycomb")
|
|
|
|
if target.get("r"):
|
|
# Selection is a circle
|
|
|
|
# Draw Honeycomb circles in a bounding box
|
|
# +------ ---------------------------------------------------+
|
|
# | * * |-s1-| * * |
|
|
# | * * | | * * |
|
|
# | * (x1,y1) *| |* (x2,y2) * |
|
|
# | * |\ * * * |
|
|
# | * | \ * * * |
|
|
# | *| *\ c * * |
|
|
# | | \ * * |
|
|
# | a| * * |
|
|
# | | * (x3,y3) * |
|
|
# | |___*____ * |
|
|
# | b * * |
|
|
# | * * |
|
|
# | |
|
|
# +----------------------------------------------------------+
|
|
# Known values:
|
|
# =============
|
|
# Circle centers known: (x1,y1) and (x2,y2)
|
|
# Radius of circles known: r1, r2, r3 and r1=r2=r3
|
|
# Therefore, spacing, s1 is known
|
|
# Spacing Between circle 1 and 2: s1 = x2-x1-2r
|
|
#
|
|
# Calculate coordinages for circle 3:
|
|
# ===================================
|
|
# Triangle to find coordinates of circle 3:
|
|
# |\
|
|
# | \
|
|
# a| \c
|
|
# | \
|
|
# |____\
|
|
# b
|
|
# a^2+b^2=c^2
|
|
# a = (c^2-b^2)^1/2
|
|
# b = (x2-x1)/2
|
|
# c = r1+r3+s1
|
|
# = 2r1+(x1-x2-2r)
|
|
# = x1-x2
|
|
# a = ((x2-x1)^2 -((x2-x1)/2)^2)^1/2
|
|
#
|
|
# x3 = x1 + ((x2-x1)/2)
|
|
# y3 = y2 - sqrt((x2-x1)^2 - ((x2-x1)/2)^2)
|
|
circle_r = float(target.get("r"))
|
|
circle_d = 2 * circle_r
|
|
circle_style = target.get("style")
|
|
|
|
# Calculate spacing between circles in a row to fit a
|
|
# given bounding box
|
|
left_over = bounding_box_w - (circle_d * self.options.count_x)
|
|
spacing = left_over/(self.options.count_x)
|
|
circ_center_diff = spacing + (2 * circle_r)
|
|
|
|
shift_row = False
|
|
y_coord_last = 0
|
|
for y_cnt in range(0, self.options.count_y):
|
|
if y_cnt == 0:
|
|
y_coord = bounding_box_y + circle_r
|
|
else:
|
|
y_coord = y_coord_last + math.sqrt(pow(circ_center_diff,2)-pow(circ_center_diff/2,2))
|
|
if shift_row:
|
|
num_x = self.options.count_x + 1
|
|
else:
|
|
num_x = self.options.count_x
|
|
for x_cnt in range(0, num_x):
|
|
if shift_row is True:
|
|
x_coord_int = bounding_box_x
|
|
else:
|
|
x_coord_int = bounding_box_x + (spacing/2) + circle_r
|
|
x_coord = x_coord_int + (x_cnt * circ_center_diff)
|
|
circ = inkex.Circle(attrib={"cx": f"{x_coord}",
|
|
"cy": f"{y_coord}",
|
|
"r": f"{circle_r}",
|
|
"style": circle_style})
|
|
layer = self.svg.get_current_layer()
|
|
layer.append(circ)
|
|
y_coord_last = y_coord
|
|
shift_row ^= True
|
|
|
|
elif target.get("rx"):
|
|
# Selection is an elipse
|
|
inkex.errormsg("Elipses are not supported\n"
|
|
"Selection must have 'r' attribute")
|
|
|
|
else:
|
|
inkex.errormsg("Other shapes not supported\n"
|
|
"Selection must have 'r' attribute")
|
|
|
|
return
|
|
|
|
|
|
if __name__ == '__main__':
|
|
HoneyCombExtension().run()
|