#!/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()