HoneyCombExtention/honeycomb.py
Jesse Millwood 13f513e4b8 Refactored algorithm and documented
The new algorithm places circles with more dependencies so that the spacing
between kitty corner and circles on the same line are the same.
2023-09-12 19:56:18 -04:00

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