diff --git a/extensions/fablabchemnitz/colorize_path_lengths/colorize_path_lengths.inx b/extensions/fablabchemnitz/colorize_path_lengths/colorize_path_lengths.inx
index ba3f60b..6b45e1e 100644
--- a/extensions/fablabchemnitz/colorize_path_lengths/colorize_path_lengths.inx
+++ b/extensions/fablabchemnitz/colorize_path_lengths/colorize_path_lengths.inx
@@ -7,13 +7,13 @@
- 12
- 25
- 40
- 60
- 60
- 0.1
- 10
+ 12
+ 25
+ 40
+ 60
+ 60
+ 0.1
+ 10
path
diff --git a/extensions/fablabchemnitz/j_tech_photonics_laser_tool/j_tech_photonics_laser_tool.inx b/extensions/fablabchemnitz/j_tech_photonics_laser_tool/j_tech_photonics_laser_tool.inx
new file mode 100644
index 0000000..6a77bd0
--- /dev/null
+++ b/extensions/fablabchemnitz/j_tech_photonics_laser_tool/j_tech_photonics_laser_tool.inx
@@ -0,0 +1,72 @@
+
+
+ J Tech Photonics Laser Tool
+ fablabchemnitz.de.j_tech_photonics_laser_tool
+
+
+
+
+
+
+ 3000
+ 750
+
+ 1
+ 1
+
+ -- Choose Output Directory --
+ output.gcode
+ false
+ true
+
+
+ M3 S255;
+ M5;
+ 0
+
+ true
+ 0.5
+ 1.0
+
+ 0.01
+
+
+
+
+
+
+ false
+ 0
+
+ false
+
+ true
+ true
+
+
+
+
+
+
+
+ false
+ 200
+ 200
+
+ 0
+ 0
+ 1
+
+
+
+ path
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/j_tech_photonics_laser_tool/j_tech_photonics_laser_tool.py b/extensions/fablabchemnitz/j_tech_photonics_laser_tool/j_tech_photonics_laser_tool.py
new file mode 100644
index 0000000..8b20c73
--- /dev/null
+++ b/extensions/fablabchemnitz/j_tech_photonics_laser_tool/j_tech_photonics_laser_tool.py
@@ -0,0 +1,312 @@
+#!/usr/bin/env python3
+
+import os
+from lxml import etree
+from xml.etree import ElementTree as xml_tree
+from inkex import EffectExtension, Boolean
+
+from svg_to_gcode.svg_parser import parse_root, Transformation, debug_methods
+from svg_to_gcode.geometry import LineSegmentChain
+from svg_to_gcode.compiler import Compiler, interfaces
+from svg_to_gcode import TOLERANCES
+
+svg_name_space = "http://www.w3.org/2000/svg"
+inkscape_name_space = "http://www.inkscape.org/namespaces/inkscape"
+sodipodi_name_space = "http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+
+inx_filename = "j_tech_photonics_laser_tool.inx"
+
+
+def generate_custom_interface(laser_off_command, laser_power_command):
+ """Wrapper function for generating a Gcode interface with a custom laser power command"""
+
+ class CustomInterface(interfaces.Gcode):
+ """A Gcode interface with a custom laser power command"""
+
+ def __init__(self):
+ super().__init__()
+
+ def laser_off(self):
+ return f"{laser_off_command}"
+
+ def set_laser_power(self, _):
+ return f"{laser_power_command}"
+
+ return CustomInterface
+
+
+class JTechPhotonicsLaserTool(EffectExtension):
+ """Inkscape Effect Extension."""
+
+ def __init__(self):
+ EffectExtension.__init__(self)
+
+ def effect(self):
+ """Takes the SVG from Inkscape, generates gcode, returns the SVG after adding debug lines."""
+
+ root = self.document.getroot()
+
+ # Change svg_to_gcode's approximation tolerance
+ TOLERANCES["approximation"] = float(self.options.approximation_tolerance.replace(',', '.'))
+
+ try:
+ assert os.path.isdir(self.options.directory)
+ except:
+ self.debug(f"{self.options.directory} is not a directory")
+ exit(2)
+
+ # Construct output path
+ if self.options.filename:
+ filename = self.options.filename
+ if '.' not in filename:
+ filename += ".gcode"
+ elif self.document_path():
+ filename, extension = self.document_path().split('.')
+ filename = filename.split('/')[-1] + '.gcode'
+ else:
+ filename = "untitled.gcode"
+
+ output_path = os.path.join(self.options.directory, filename)
+
+ if self.options.filename_suffix:
+ filename, extension = output_path.split('.')
+
+ n = 1
+ while os.path.isfile(output_path):
+ output_path = filename + str(n) + '.' + extension
+ n += 1
+
+ # Load header and footer files
+ header = []
+ if self.options.header_path is not None:
+ if os.path.isfile(self.options.header_path):
+ with open(self.options.header_path, 'r') as header_file:
+ header = header_file.read().splitlines()
+ elif self.options.header_path != os.getcwd(): # The Inkscape file selector defaults to the working directory
+ self.debug(f"Header file does not exist at {self.options.header_path}")
+ exit(2)
+
+ footer = []
+ if self.options.footer_path is not None:
+ if os.path.isfile(self.options.footer_path):
+ with open(self.options.footer_path, 'r') as footer_file:
+ footer = footer_file.read().splitlines()
+ elif self.options.footer_path != os.getcwd():
+ self.debug(f"Footer file does not exist at {self.options.footer_path}")
+ exit(2)
+
+ # Customize header/footer
+ custom_interface = generate_custom_interface(self.options.tool_off_command, self.options.tool_power_command)
+ interface_instance = custom_interface()
+
+ if self.options.do_laser_off_start:
+ header.append(interface_instance.laser_off())
+ if self.options.do_laser_off_end:
+ footer.append(interface_instance.laser_off())
+
+ header.append(interface_instance.set_movement_speed(self.options.travel_speed))
+ if self.options.do_z_axis_start:
+ header.append(interface_instance.linear_move(z=self.options.z_axis_start))
+ if self.options.move_to_origin_end:
+ footer.append(interface_instance.linear_move(x=0, y=0))
+
+ # Generate gcode
+ gcode_compiler = Compiler(custom_interface, self.options.travel_speed, self.options.cutting_speed,
+ self.options.pass_depth, dwell_time=self.options.dwell_time, custom_header=header,
+ custom_footer=footer, unit=self.options.unit)
+
+ transformation = Transformation()
+
+ transformation.add_translation(self.options.horizontal_offset, self.options.vertical_offset)
+ transformation.add_scale(self.options.scaling_factor)
+
+ if self.options.machine_origin == "center":
+ transformation.add_translation(-self.options.bed_width / 2, self.options.bed_height / 2)
+ elif self.options.machine_origin == "top-left":
+ transformation.add_translation(0, self.options.bed_height)
+
+ self.clear_debug()
+ curves = parse_root(root, transform_origin=not self.options.invert_y_axis, root_transformation=transformation,
+ canvas_height=self.options.bed_height)
+
+ gcode_compiler.append_curves(curves)
+ gcode_compiler.compile_to_file(output_path, passes=self.options.passes)
+
+ # Draw debug lines
+ if self.options.draw_debug:
+ self.draw_debug_traces(curves)
+ self.draw_unit_reference()
+ self.select_non_debug_layer()
+
+ return self.document
+
+ def draw_debug_traces(self, curves):
+ """Traces arrows over all parsed paths"""
+
+ root = self.document.getroot()
+ origin = self.options.machine_origin
+ bed_width = self.options.bed_width
+ bed_height = self.options.bed_height
+
+ group = etree.Element("{%s}g" % svg_name_space)
+ group.set("id", "debug_traces")
+ group.set("{%s}groupmode" % inkscape_name_space, "layer")
+ group.set("{%s}label" % inkscape_name_space, "debug traces")
+
+ group.append(
+ etree.fromstring(xml_tree.tostring(debug_methods.arrow_defs(arrow_scale=self.options.debug_arrow_scale))))
+
+ for curve in curves:
+ approximation = LineSegmentChain.line_segment_approximation(curve)
+
+ change_origin = Transformation()
+
+ if not self.options.invert_y_axis:
+ change_origin.add_scale(1, -1)
+ change_origin.add_translation(0, -bed_height)
+
+ if origin == "center":
+ change_origin.add_translation(bed_width / 2, bed_height / 2)
+
+ path_string = xml_tree.tostring(
+ debug_methods.to_svg_path(approximation, color="red", opacity="0.5",
+ stroke_width=f"{self.options.debug_line_width}px",
+ transformation=change_origin, draw_arrows=True)
+ )
+
+ group.append(etree.fromstring(path_string))
+
+ root.append(group)
+
+ def draw_unit_reference(self):
+ """Draws reference points to mark the bed's four corners"""
+ root = self.document.getroot()
+ unit = self.options.unit
+ origin = self.options.machine_origin
+ bed_width = self.options.bed_width
+ bed_height = self.options.bed_height
+
+ group = etree.Element("{%s}g" % svg_name_space)
+ group.set("id", "debug_references")
+ group.set("{%s}groupmode" % inkscape_name_space, "layer")
+ group.set("{%s}label" % inkscape_name_space, "debug reference points")
+
+ reference_points_svg = [(0, 0), (0, bed_height), (bed_width, 0), (bed_width, bed_height)]
+ reference_points_gcode = {
+ "bottom-left": [(0, bed_height), (0, 0), (bed_width, bed_height), (bed_width, 0)],
+ "top-left": [(0, 0), (0, bed_height), (bed_width, 0), (bed_width, bed_height)],
+ "center": [(-bed_width / 2, bed_height / 2), (-bed_width / 2, -bed_height / 2),
+ (bed_width / 2, bed_height / 2),
+ (bed_width / 2, -bed_height / 2)]
+ }[origin]
+ for i, (x, y) in enumerate(reference_points_svg):
+ reference_point = etree.Element("{%s}g" % svg_name_space)
+
+ stroke_width = 2
+ size = 7
+
+ x_direction = -1 if x > 0 else 1
+ plus_sign = etree.Element("{%s}g" % svg_name_space)
+ horizontal = etree.Element("{%s}line" % svg_name_space)
+ horizontal.set("x1", str(x - x_direction * stroke_width / 2))
+ horizontal.set("y1", str(y))
+ horizontal.set("x2", str(x + x_direction * size))
+ horizontal.set("y2", str(y))
+ horizontal.set("style", f"stroke:black;stroke-width:{stroke_width}")
+ plus_sign.append(horizontal)
+
+ y_direction = -1 if y > 0 else 1
+ vertical = etree.Element("{%s}line" % svg_name_space)
+ vertical.set("x1", str(x))
+ vertical.set("y1", str(y + stroke_width / 2))
+ vertical.set("x2", str(x))
+ vertical.set("y2", str(y + y_direction * size))
+ vertical.set("style", f"stroke:black;stroke-width:{stroke_width}")
+ plus_sign.append(vertical)
+
+ reference_point.append(plus_sign)
+
+ text_box = etree.Element("{%s}text" % svg_name_space)
+ text_box.set("x", str(x - 28))
+ text_box.set("y", str(y - (y <= 0) * 6 + (y > 0) * 9))
+ text_box.set("font-size", "6")
+ text_box.text = f"{reference_points_gcode[i][0]}{unit}, {reference_points_gcode[i][1]}{unit}"
+ reference_point.append(text_box)
+
+ group.append(reference_point)
+
+ root.append(group)
+
+ def select_non_debug_layer(self):
+ """
+ Select content_layer and create one if it doesn't exist. This helps stop the user from accidentally placing new
+ objects in debug layers.
+ """
+
+ root = self.document.getroot()
+
+ unique_id = "layer89324"
+ content_layer = root.find("{%s}g[@id='%s']" % (svg_name_space, unique_id))
+
+ if content_layer is None:
+ content_layer = etree.Element("{%s}g" % svg_name_space)
+ content_layer.set("id", unique_id)
+ content_layer.set("{%s}groupmode" % inkscape_name_space, "layer")
+ content_layer.set("{%s}label" % inkscape_name_space, "content layer")
+
+ sodipodi = root.find("{%s}namedview" % sodipodi_name_space)
+ if sodipodi is not None:
+ sodipodi.set("{%s}current-layer" % inkscape_name_space, unique_id)
+
+ root.append(content_layer)
+
+ def clear_debug(self):
+ """Removes debug groups. Used before parsing paths for gcode."""
+ root = self.document.getroot()
+
+ debug_traces = root.find("{%s}g[@id='debug_traces']" % svg_name_space)
+ debug_references = root.find("{%s}g[@id='debug_references']" % svg_name_space)
+
+ if debug_traces is not None:
+ root.remove(debug_traces)
+
+ if debug_references is not None:
+ root.remove(debug_references)
+
+ def add_arguments(self, arg_parser):
+ """Tell inkscape what arguments to stick in self.options (behind the hood it's more complicated, see docs)"""
+ arguments = self.read_arguments()
+
+ for arg in arguments:
+ arg_parser.add_argument("--" + arg["name"], type=arg["type"], dest=arg["name"])
+
+ @staticmethod
+ def read_arguments():
+ """
+ This method reads arguments off of the inx file so you don't have to explicitly declare them in self.add_arguments()
+ """
+ root = etree.parse(inx_filename).getroot()
+
+ arguments = [] # [{name, type, ...}]
+ namespace = "http://www.inkscape.org/namespace/inkscape/extension"
+ for arg in root.iter("{%s}param" % namespace):
+
+ name = arg.attrib["name"]
+
+ arg_type = arg.attrib["type"]
+
+ if arg_type in ["description", "notebook"]:
+ continue
+
+ types = {"int": int, "float": float, "bool": Boolean, "string": str, "optiongroup": str, "path": str}
+
+ arguments.append({"name": name, "type": types[arg_type]})
+
+ if next(root.iter("{%s}page" % namespace)) is not None:
+ arguments.append({"name": "tabs", "type": str})
+
+ return arguments
+
+
+if __name__ == '__main__':
+ JTechPhotonicsLaserTool().run()
diff --git a/extensions/fablabchemnitz/j_tech_photonics_laser_tool/meta.json b/extensions/fablabchemnitz/j_tech_photonics_laser_tool/meta.json
new file mode 100644
index 0000000..c309d31
--- /dev/null
+++ b/extensions/fablabchemnitz/j_tech_photonics_laser_tool/meta.json
@@ -0,0 +1,30 @@
+[
+ {
+ "name": "J Tech Photonics Laser Tool",
+ "id": "fablabchemnitz.de.j_tech_photonics_laser_tool",
+ "path": "j_tech_photonics_laser_tool",
+ "dependent_extensions": null,
+ "original_name": "J Tech Community Laser Tool",
+ "original_id": "community.jtechphotonics.com",
+ "license": "GNU GPL v2",
+ "license_url": "https://jtechphotonics.com/Downloads/Inkscape/JTP-Laser-Tool_v1.1-beta_ink1.0.zip",
+ "comment": "",
+ "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/j_tech_photonics_laser_tool",
+ "fork_url": "https://github.com/JTechPhotonics/J-Tech-Photonics-Laser-Tool",
+ "documentation_url": "https://stadtfabrikanten.org/display/IFM/J+Tech+Photonics+Laser+Tool",
+ "inkscape_gallery_url": null,
+ "main_authors": [
+ "github.com/JTechPhotonics",
+ "github.com/PadLex",
+ "github.com/odaki",
+ "github.com/themanyone",
+ "github.com/drewler",
+ "github.com/nineff",
+ "github.com/4cello",
+ "github.com/dapperfu",
+ "github.com/nmeurer",
+ "github.com/aspeteRakete",
+ "github.com/vmario89"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Bendy.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Bendy.py
new file mode 100755
index 0000000..d954e36
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Bendy.py
@@ -0,0 +1,378 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+from math import pi, sin, cos, tan, asin, acos, atan, sqrt
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+MIN = 0.0001
+
+
+class Bendy_Straw(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--pattern', type=self.str, default='bendy_straw')
+ self.add_argument('--pattern_type', type=self.str, default='origami')
+ self.add_argument('--parameter_type', type=self.str, default='angles')
+ self.add_argument('--n', type=self.int, default=6)
+ self.add_argument('--lines', type=self.int, default=3)
+ self.add_argument('--radius', type=self.float, default=25.0)
+ # self.add_argument('--attachment_length', type=self.float, default=3.0)
+ # self.add_argument('--attachment_length', type=self.int, default=20)
+ self.add_argument('--radial_ratio', type=self.float, default=0.75)
+ # self.add_argument('--alpha1', type=self.float, default=45.0)
+ # self.add_argument('--alpha2', type=self.float, default=45.0)
+ self.add_argument('--alpha1', type=self.int, default=45)
+ self.add_argument('--alpha2', type=self.int, default=35)
+ self.add_argument('--h1', type=self.float, default=1)
+ self.add_argument('--h2', type=self.float, default=2)
+
+ self.add_argument('--vertex_base_outer_bool', type=self.bool, default=False)
+ self.add_argument('--vertex_base_inner_bool', type=self.bool, default=False)
+ self.add_argument('--vertex_radius_outer_bool', type=self.bool, default=False)
+ self.add_argument('--vertex_radius_inner_bool', type=self.bool, default=False)
+
+ self.add_argument('--add_attachment', type=self.bool, default=False)
+
+ # slot options for support ring
+ self.add_argument('--base_height', type=self.float, default=5.0)
+ self.add_argument('--add_base_slot', type=self.bool, default=False)
+ self.add_argument('--center_base_slot', type=self.bool, default=False)
+ self.add_argument('--base_slot_height', type=self.float, default=3.0)
+ self.add_argument('--base_slot_width', type=self.float, default=3.0)
+ self.add_argument('--distance', type=self.float, default=3.0)
+ self.add_argument('--add_distance_slot', type=self.bool, default=False)
+ self.add_argument('--distance_slot_height', type=self.float, default=3.0)
+ self.add_argument('--distance_slot_width', type=self.float, default=3.0)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+ vertex_radius = self.options.vertex_radius * unit_factor
+
+ # retrieve saved parameters, and apply unit factor where needed
+ pattern_type = self.options.pattern_type
+ n = self.options.n
+ lines = self.options.lines
+ radial_ratio = self.options.radial_ratio
+ R = self.options.radius * unit_factor
+ distance = self.options.distance * unit_factor
+ base_height = self.options.base_height * unit_factor
+ # add_attachment = self.options.add_attachment
+ # attachment_length = self.options.attachment_length * unit_factor
+ r = R * radial_ratio
+
+ if (self.options.parameter_type == 'angles'):
+ alpha1 = self.options.alpha1 * pi / 180
+ alpha2 = self.options.alpha2 * pi / 180
+ elif (self.options.parameter_type == 'heights'):
+ alpha1 = atan(self.options.h1 * unit_factor / (R - r))
+ alpha2 = atan(self.options.h2 * unit_factor / (R - r))
+
+ # calculating pattern parameters
+ l1 = (R - r) / cos(alpha1)
+ l2 = (R - r) / cos(alpha2)
+ A = 2 * R * sin(pi / n)
+ # attachment_length = 0.01 * self.options.attachment_length * A
+ a = A * radial_ratio
+ dx = (A - a) / 2
+ beta1 = acos(cos(alpha1) * sin(pi / n))
+ beta2 = acos(cos(alpha2) * sin(pi / n))
+ b1 = l1 * sin(beta1)
+ b2 = l2 * sin(beta2)
+ height = (b1 + b2) * lines + distance * (lines - 1)
+
+ if self.options.add_attachment:
+ n = n+1
+
+ #
+ # big horizontal mountains grid
+ #
+ mountain_horizontal_stroke = Path([(0, base_height), (A * n, base_height)], 'm')
+ horizontal_grid_mountain = []
+ for i in range(1, lines):
+ horizontal_grid_mountain.append(
+ mountain_horizontal_stroke + (0, distance * (i - 1) + b1 * (i + 0) + b2 * (i + 0)))
+ horizontal_grid_mountain.append(
+ mountain_horizontal_stroke + (0, distance * (i + 0) + b1 * (i + 0) + b2 * (i + 0)))
+ if distance < MIN:
+ horizontal_grid_mountain = horizontal_grid_mountain[::2]
+ if base_height > MIN:
+ horizontal_grid_mountain.insert(0, mountain_horizontal_stroke)
+ horizontal_grid_mountain.append(
+ mountain_horizontal_stroke + (0, distance * (lines - 1) + b1 * lines + b2 * lines))
+
+ # reverse every other horizontal stroke for faster laser-cutting
+ for i in range(len(horizontal_grid_mountain)):
+ if (i % 2 == 0):
+ horizontal_grid_mountain[i].points.reverse()
+
+ #
+ # diamond shapes
+ #
+
+ # full diamond patterns styles, depending on pattern type
+ style_diag_left = 'm'
+ style_diag_right = 'm'
+ style_diag = 'v'
+ style_vert = 'm'
+ style_hori_left = 'm'
+ style_hori_right = 'm'
+ if pattern_type == 'origami' or pattern_type == 'origami_bent':
+ style_hori_left = 'v'
+ style_diag_left = 'n'
+ style_diag_right = 'v'
+ elif pattern_type == 'origami2':
+ style_hori_right = 'v'
+ style_diag_left = 'v'
+ style_diag_right = 'n'
+ elif pattern_type == 'kirigami1':
+ style_vert = 'v'
+ style_hori_left = 'c'
+ style_hori_right = 'c'
+ elif pattern_type == 'kirigami2':
+ style_diag_left = 'c'
+ style_diag_right = 'c'
+ style_hori_left = 'n'
+ style_hori_right = 'n'
+ style_vert = 'n'
+
+ # diamond pattern with strokes of different styles
+ stroke_base = Path([(0, 0), (0, base_height)], 'm')
+ diamond_diagonals_left = Path([(0, base_height), (-dx, base_height + b1), (0, base_height + b1 + b2)],
+ style_diag_left)
+ diamond_diagonals_right = Path([(0, base_height + b1 + b2), (dx, base_height + b1), (0, base_height)],
+ style_diag_right)
+ diamond_vertical = Path([(0, base_height), (0, base_height + b1 + b2)], style_vert)
+ stroke_distance = Path([(0, base_height + b1 + b2), (0, distance + base_height + b1 + b2)], 'm')
+ diamond_horizontal_left = Path([(-dx, 0), (0, 0)], style_hori_left)
+ diamond_horizontal_right = Path([(0, 0), (dx, 0)], style_hori_right)
+
+ diamond = [diamond_diagonals_left, diamond_diagonals_right, diamond_vertical]
+
+ if pattern_type == 'origami_bent':
+ bent_diagonal = Path([(0, base_height + b1 + b2), (dx, base_height + b1), (0, base_height)], 'm')
+ bent_horizontal = Path([(0, 0), (dx, 0)], 'v')
+ line_bent = []
+
+
+ # drawing lines with the diamond shapes
+ line_left = []
+ line_middle = []
+ line_right = []
+ if base_height > MIN:
+ line_middle.append(stroke_base)
+ if pattern_type == 'origami_bent':
+ line_bent.append(stroke_base)
+ for i in range(lines):
+ delta = (0, (distance + b1 + b2) * i)
+ if pattern_type != 'kirigami2':
+ line_left.append(diamond_diagonals_right + delta)
+ line_middle = line_middle + Path.list_add(diamond, delta)
+ if pattern_type != 'kirigami2':
+ line_right.append(diamond_diagonals_left + delta)
+ if distance > MIN and i < lines - 1:
+ line_middle.append(stroke_distance + delta)
+
+ if pattern_type == 'origami_bent':
+ line_bent = line_bent + [bent_diagonal + delta]
+ if distance > MIN and i < lines - 1:
+ line_bent.append(stroke_distance + delta)
+
+ if base_height > MIN:
+ line_middle.append(stroke_base + (0, base_height + height))
+ if pattern_type == 'origami_bent':
+ line_bent.append(stroke_base + (0, base_height + height))
+
+ # creating full diamond patterns
+ line_left = line_left[::-1]
+ diamond_patterns_full = [line_left]
+ for i in range(n - 1):
+ delta = (A * (i + 1), 0)
+ if pattern_type == 'origami_bent' and i == 2:
+ diamond_patterns_full.append(Path.list_add(line_bent, delta))
+ else:
+ diamond_patterns_full.append(Path.list_add(line_middle, delta))
+ diamond_patterns_full.append(Path.list_add(line_right, (A * n, 0)))
+
+ #
+ # small horizontal alternate style grid
+ #
+ valley_points = [(dx, 0),
+ (dx + a, 0)]
+ valley_stroke = Path(valley_points, 'v')
+ if pattern_type == 'kirigami2':
+ horizontal_line = []
+ else:
+ horizontal_line = [diamond_horizontal_right + (0, 0)]
+ for i in range(n):
+ if not (pattern_type == 'origami_bent' and i == 3):
+ horizontal_line.append(valley_stroke + ((a + 2 * dx) * i, 0))
+ if (pattern_type != 'kirigami2'):
+ if not (pattern_type == 'origami_bent' and i == 3):
+ horizontal_line.append(diamond_horizontal_left + (A + (a + 2 * dx) * i, 0))
+ if pattern_type == 'origami_bent' and i==2:
+ horizontal_line.append(bent_horizontal + (A + (a + 2 * dx) * i, 0))
+ elif i < n - 1:
+ horizontal_line.append(diamond_horizontal_right + (A + (a + 2 * dx) * i, 0))
+ horizontal_grid_alternate = []
+ for i in range(lines):
+ horizontal_grid_alternate.append(
+ Path.list_add(horizontal_line, (0, base_height + distance * (i + 0) + b1 * (i + 1) + b2 * (i + 0))))
+
+ # reverse every other horizontal stroke for faster laser-cutting
+ for i in range(len(horizontal_grid_alternate)):
+ if (i % 2 == 0):
+ horizontal_grid_alternate[i] = Path.list_invert(horizontal_grid_alternate[i])
+
+ # for i in range(len(horizontal_grid_alternate)):
+ # inkex.debug(i)
+ # Path.debug_points(horizontal_grid_alternate[i])
+ # inkex.debug('\n')
+
+ #
+ # edge drawing
+ #
+ self.edge_points = [(0, 0)]
+ self.edge_points.append((A * n, 0))
+
+ # rectangles for attachments at base and between cells
+
+ # add upper base attachment
+ self.edge_points.append((A * n, base_height))
+
+ # draw attachment between cells and inside cells
+ for i in range(lines):
+ self.edge_points.append((A * n + 0, base_height + (b1 + b2) * (i + 0) + distance * i))
+ if pattern_type == 'kirigami2':
+ self.edge_points.append((A * n - dx, base_height + b1 * (i + 1) + (b2 + distance) * i))
+ self.edge_points.append((A * n + 0, base_height + (b1 + b2) * (i + 1) + distance * i))
+
+ self.edge_points.append((A * n, height + 2 * base_height))
+ self.edge_points.append((0, height + 2 * base_height))
+
+ # if full kirigami selected, cut left side next to cells
+ if pattern_type == 'kirigami2':
+ for i in range(lines):
+ self.edge_points.append((0, height + base_height - (b1 + b2) * (i + 0) - distance * i))
+ self.edge_points.append((dx, height + base_height - b2 * (i + 1) - (b1 + distance) * i))
+ self.edge_points.append((0, height + base_height - (b1 + b2) * (i + 1) - distance * i))
+
+ #
+ # slots drawing
+ #
+ center_slot = self.options.center_base_slot
+ base_slots = []
+ if self.options.add_base_slot:
+ base_slot_height = self.options.base_slot_height
+ base_slot_width = self.options.base_slot_width
+ if base_slot_height > base_height or base_slot_width > A:
+ inkex.debug('Base slot dimensions are too big')
+ base_slot_height = min(base_height, base_slot_height)
+ base_slot_width = min(A, base_slot_width)
+ if base_slot_height > 0 and base_slot_width > 0:
+ points = [(0, 0),
+ (0, base_slot_height),
+ (base_slot_width, base_slot_height),
+ (base_slot_width, 0,)]
+ base_slot = Path(points, 'c', closed=True) + ((A - base_slot_width)/2, (base_height - base_slot_height)/(1+center_slot))
+ base_slots_line = []
+ for i in range(n):
+ base_slots_line.append(base_slot + (A*i, 0))
+ base_slots = [base_slots_line]
+ base_slots.append(Path.list_add(base_slots_line, (0, height+base_slot_height + (base_height - base_slot_height)*center_slot)))
+
+ dist_slots = []
+ if self.options.add_distance_slot:
+ dist_slot_height = self.options.distance_slot_height
+ dist_slot_width = self.options.distance_slot_width
+ if dist_slot_height > distance or dist_slot_width > A:
+ inkex.debug('Dimensions of slots between cells are too big')
+ dist_slot_height = min(distance, dist_slot_height)
+ dist_slot_width = min(A, dist_slot_width)
+
+ if dist_slot_height > 0 and dist_slot_width > 0:
+ points = [(0, 0),
+ (0, dist_slot_height),
+ (dist_slot_width, dist_slot_height),
+ (dist_slot_width, 0,)]
+ dist_slot = Path(points, 'c', closed=True) + ((A - dist_slot_width)/2, base_height+b1+b2 + (distance - dist_slot_height)/2)
+ dist_slots_line = []
+ for i in range(n):
+ dist_slots_line.append(dist_slot + (A*i, 0))
+
+ for i in range(lines-1):
+ dist_slots.append(Path.list_add(dist_slots_line, (0, i*(b1+b2+distance))))
+
+
+
+ # sending lines to draw
+ self.path_tree = [horizontal_grid_mountain, horizontal_grid_alternate, diamond_patterns_full, base_slots, dist_slots]
+
+ #
+ # vertices drawing
+ #
+ # outer base vertices?
+ if self.options.vertex_base_outer_bool:
+ self.vertex_points = self.vertex_points + [(A * i, height + base_height * 2) for i in range(n + 1)]
+ self.vertex_points = self.vertex_points + [(A*i, 0) for i in range(n+1)]
+
+ # inner base vertices?
+ if self.options.vertex_base_inner_bool:
+ self.vertex_points = self.vertex_points + [(A*i, base_height) for i in range(n+1)]
+ self.vertex_points = self.vertex_points + [(A*i, height+base_height) for i in range(n+1)]
+ for j in range(lines-1):
+ self.vertex_points = self.vertex_points + [(A*i, base_height+((b1+b2)*(j+1))+distance*j) for i in range(n+1)] + \
+ [(A*i, base_height+((b1+b2)*(j+1))+distance*(j+1)) for i in range(n+1)]
+
+ # radius vertices?
+ if self.options.vertex_radius_outer_bool and pattern_type != 'kirigami2':
+ for j in range(lines):
+ i_range = list(range(3)) + list(range(3+(pattern_type=='origami_bent'), n + 1))
+ self.vertex_points = self.vertex_points + [(A * i, base_height + b1*(j+1) + (b2+distance)*j) for i in i_range]
+ if self.options.vertex_radius_inner_bool:
+ for j in range(lines):
+ if pattern_type != 'origami2':
+ # pass
+ self.vertex_points = self.vertex_points + [(dx + A * i, base_height + b1*(j+1) + (b2+distance)*j) for i in range(n)]
+ if pattern_type[:7] != 'origami':
+ # pass
+ self.vertex_points = self.vertex_points + [(-dx + A * (i + 1), base_height + b1 * (j + 1) + (b2 + distance) * j) for i in range(n)]
+ # for j in range(lines):
+
+
+
+
+ # self.vertex_points = self.vertex_points + vertices_base
+
+ # self.vertex_points = self.vertex_points + self.edge_points
+ # diamond_patterns_full_simple = Path.list_simplify(diamond_patterns_full)
+ # for path in diamond_patterns_full:
+ # # path = Path.list_simplify(path)
+ # # path = Path.list_simplify(path[0])
+ # if path.style != 'n':
+ # self.vertex_points = self.vertex_points + path.points
+
+
+
+#
+
+# Main function, creates an instance of the Class and calls self.draw() to draw the origami on inkscape
+# self.draw() is either a call to inkex.affect() or to svg.run(), depending on python version
+if __name__ == '__main__':
+ e = Bendy_Straw() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Boxes_Masu.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Boxes_Masu.py
new file mode 100644
index 0000000..25c33e7
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Boxes_Masu.py
@@ -0,0 +1,98 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import math
+import numpy as np
+from math import pi
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+
+def reflections_diag(path):
+ new_paths = [path]
+ new_paths = new_paths + Path.list_reflect(new_paths, (0, 0), (1, 1))
+ return new_paths + Path.list_reflect(new_paths, (0, 0), (1, -1))
+
+def reflections_rect(path):
+ new_paths = [path]
+ new_paths = new_paths + Path.list_reflect(new_paths, (0, 0), (0, 1))
+ return new_paths + Path.list_reflect(new_paths, (0, 0), (1, 0))
+
+def recenter(paths, dist):
+ # paths_new = Path.list_simplify(paths)
+ paths_new = Path.list_rotate(paths, pi/4)
+ return Path.list_add(paths_new, (dist, dist))
+
+class MasuBox(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--pattern', type=self.str, default='boxes_masu')
+ self.add_argument('--width', type=self.float, default=10.0)
+ self.add_argument('--height', type=self.float, default=10.0)
+ self.add_argument('--width_delta', type=self.float, default=0.0)
+ self.add_argument('--width_delta_bool', type=self.bool, default=False)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+
+ # retrieve saved parameters, and apply unit factor where needed
+ width = self.options.width * unit_factor
+ height = self.options.height * unit_factor
+ if self.options.width_delta_bool:
+ width_delta = self.options.width_delta * unit_factor
+ width += width_delta
+ height -= width_delta/2
+ length = math.sqrt(2)*(width + 2*height)
+ half_fold = 90 if self.options.simulation_mode else 180
+
+ # bottom of box
+ ring_inner = Path.generate_square(width, width, center = [0,0], style = 'm', fold_angle=half_fold)
+
+ # ring making the corners
+ lengths = [height, width, height]
+ points = Path.get_square_points(sum(lengths), sum(lengths), [0,0])
+ styles = ['vmv','mmm']
+ ring_middle = [Path([points[i], points[(i + 1)%4]], 'm').
+ break_path(lengths, styles[i % 2]) for i in range(4)]
+
+ # arms along width and length
+ points = [(-width / 2, -(width / 2 + 0 * height)),
+ (-width / 2, -(width / 2 + 1 * height)),
+ (-width / 2, -(width / 2 + 2 * height)),
+ (+width / 2, -(width / 2 + 2 * height)),
+ (+width / 2, -(width / 2 + 1 * height)),
+ (+width / 2, -(width / 2 + 0 * height))]
+
+ arms_ = [Path.list_create_from_points(points, 'mmvmm', fold_angles = [180, 180, half_fold, 180, 180]),
+ Path.list_create_from_points(points, 'mvvvm', half_fold)]
+ arms = [Path.list_rotate(arms_[i % 2], i * pi / 2) for i in range(4)]
+
+ # tiny corner diagonals
+ diag = Path([(width/2, width/2), (width/2 + height, width/2 + height)], 'v')
+ corner_diagonals = [diag * (1, i*pi/2) for i in range(4)]
+
+ self.edge_points = Path.get_square_points(length, length)
+
+ self.path_tree = [ring_inner, ring_middle, arms, corner_diagonals]
+ self.path_tree = recenter(self.path_tree, length/2)
+
+# Main function, creates an instance of the Class and calls self.draw() to draw the origami on inkscape
+# self.draw() is either a call to inkex.affect() or to svg.run(), depending on python version
+if __name__ == '__main__':
+ e = MasuBox() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Boxes_Masu_Traditional.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Boxes_Masu_Traditional.py
new file mode 100644
index 0000000..e4f1d28
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Boxes_Masu_Traditional.py
@@ -0,0 +1,54 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+from math import pi
+
+import inkex
+import math
+from Path import Path
+from Boxes_Masu import MasuBox
+from Pattern import Pattern
+
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+
+def reflections_diag(path):
+ new_paths = [path]
+ new_paths = new_paths + Path.list_reflect(new_paths, (0, 0), (1, 1))
+ return new_paths + Path.list_reflect(new_paths, (0, 0), (1, -1))
+
+def reflections_rect(path):
+ new_paths = [path]
+ new_paths = new_paths + Path.list_reflect(new_paths, (0, 0), (0, 1))
+ return new_paths + Path.list_reflect(new_paths, (0, 0), (1, 0))
+
+
+class MasuBoxSquare(MasuBox):
+
+ def __init__(self):
+ """ Constructor
+ """
+ MasuBox.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--length', type=self.float, default=10.0)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve saved parameters, and apply unit factor where needed
+ self.options.width = self.options.length / (2 * math.sqrt(2))
+ self.options.height = self.options.width/2
+ self.options.width_delta = 0.0
+ self.options.width_delta_bool = False
+ MasuBox.generate_path_tree(self)
+
+
+# Main function, creates an instance of the Class and calls self.draw() to draw the origami on inkscape
+# self.draw() is either a call to inkex.affect() or to svg.run(), depending on python version
+if __name__ == '__main__':
+ e = MasuBoxSquare() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical.py
new file mode 100755
index 0000000..d308b95
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical.py
@@ -0,0 +1,321 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+from abc import abstractmethod
+from math import pi, sin, cos, tan, asin, acos, atan, sqrt
+from itertools import accumulate
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+
+def generate_slot_line(n, slot_position,
+ slot_height, slot_width,
+ base_height, base_width):
+
+ if slot_height == 0 and slot_width == 0:
+ return []
+
+ slot_height = min(slot_height, base_height)
+ slot_width = min(slot_width, base_width)
+
+ rect = [ (0, 0),
+ (0, slot_height),
+ (slot_width, slot_height),
+ (slot_width, 0)]
+
+ dx = (base_width - slot_width) / 2
+ if slot_position == -1:
+ dy = 0
+ elif slot_position == 0:
+ dy = (base_height - slot_height) / 2
+ elif slot_position == +1:
+ dy = base_height - slot_height
+
+ slot = Path(rect, 'c', closed=True) + (dx, dy)
+ slots = [slot + (base_width * i, 0) for i in range(n)]
+
+ divider = Path([(base_width, 0), (base_width, base_height)], style='m')
+ dividers = [divider + (base_width*i, 0) for i in range(n-1)]
+ return slots + dividers
+
+class Cylindrical(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--radius', type=self.float, default=10.0)
+ self.add_argument('--sides', type=self.int, default=6)
+ self.add_argument('--rows', type=self.int, default=3)
+ self.add_argument('--extra_column', type=self.bool, default=False)
+
+ # slot options for support ring
+ self.add_argument('--add_base_slot', type=self.bool, default=False)
+ self.add_argument('--base_slot_position', type=self.str, default="1")
+ self.add_argument('--base_height', type=self.float, default=5.0)
+ self.add_argument('--base_slot_height', type=self.float, default=3.0)
+ self.add_argument('--base_slot_width', type=self.float, default=3.0)
+
+ self.add_argument('--add_middle_slot', type=self.bool, default=False)
+ self.add_argument('--middle_slot_position', type=self.str, default="0")
+ self.add_argument('--distance', type=self.float, default=3.0)
+ self.add_argument('--middle_slot_height', type=self.float, default=3.0)
+ self.add_argument('--middle_slot_width', type=self.float, default=3.0)
+
+ @abstractmethod
+ def parse_parameters(self):
+ """
+ """
+ pass
+
+ @abstractmethod
+ def generate_cell(self):
+ """ Generate the the origami cell
+ """
+ pass
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # zero distances when slot option not selected
+ if not self.options.add_base_slot:
+ self.options.base_height = 0
+ self.options.base_slot_height = 0
+ if not self.options.add_middle_slot:
+ self.options.distance = 0
+ self.options.middle_slot_height = 0
+ if self.options.base_height == 0:
+ self.options.add_base_slot = False
+ if self.options.distance == 0:
+ self.options.add_middle_slot = False
+
+ self.parse_parameters()
+
+ # pre-calculate width before adding one to sides, for easier attachment
+ self.options.width = 2 * self.options.radius * sin(pi / self.options.sides)
+
+ self.options.cols = self.options.sides + self.options.extra_column
+
+ # get cell definitions
+ cell_data = self.generate_cell()
+
+ # calculate divider if it doesn't exist
+ if 'divider' not in cell_data:
+ points = Path.get_points(cell_data['interior'])
+ x = [p[0] for p in points if p[1] == 0]
+ cell_data['divider'] = Path([(min(x), 0),
+ (max(x), 0)], style='m')
+
+ points = Path.get_points(cell_data['divider'])
+ x = [p[0] for p in points]
+ DX = max(x) - min(x)
+ # DX = 0
+
+ # if 'edge_right' not in cell_data and 'edge_left' not in cell_data:
+ # cell_data['edge_left'] = []
+ # for interior in cell_data['interior']:
+ # points = Path.get_points(interior)
+ # x = [p[0] for p in points]
+ # y = [p[1] for p in points]
+ # top = [p for p in points if p[1] == min(y)]
+ # top_x = [p[0] for p in top]
+ # # top_y = [p[1] for p in top]
+ # bot = [p for p in points if p[1] == max(y)]
+ # bot_x = [p[0] for p in bot]
+ # # bot_y = [p[1] for p in bot]
+ # top_left = [p for p in top if p[0] == min(top_x)][0]
+ # bot_left = [p for p in bot if p[0] == min(bot_x)][0]
+ # cell_data['edge_left'].append(Path([top_left, bot_left], 'e'))
+
+
+ if 'edge_right' not in cell_data and 'edge_left' in cell_data:
+ cell_data['edge_right'] = []
+ for edge_left in cell_data['edge_left']:
+ edge_right = edge_left + (DX, 0)
+ edge_right.invert()
+ cell_data['edge_right'].append(edge_right)
+
+ if 'edge_right' in cell_data and 'edge_left' not in cell_data:
+ cell_data['edge_left'] = []
+ for edge_right in cell_data['edge_right']:
+ edge_left = edge_right + (-DX, 0)
+ edge_left.invert()
+ cell_data['edge_left'].append(edge_left)
+
+ cell_data['dx'], cell_data['dy'] = self.get_dxdy(cell_data)
+
+ # get all slots and vertical dividers between slots
+ base, middle = self.generate_all_slots(cell_data)
+ slots = [[base['slots'], middle['slots']]]
+
+ # get horizontal dividers between cells
+ dividers = self.generate_horizontal_dividers(cell_data)
+
+ # finish by replicating the actual interior
+ interior = self.generate_interior(cell_data)
+
+ # use slots and cell data to create the full edge paths
+ self.edge_points = self.generate_fused_edge_points(base, middle, cell_data)
+
+ self.path_tree = [dividers, interior]
+ if len(self.vertex_points) == 0:
+ self.vertex_points = Path.get_points(self.path_tree)
+ self.path_tree.append(slots)
+
+ def get_dxdy(self, cell_data):
+ dx = [0]
+ dy = [0]
+ for edge in cell_data['edge_left']:
+ dx.append(edge.points[1][0] - edge.points[0][0])
+ dy.append(edge.points[1][1] - edge.points[0][1])
+ return list(accumulate(dx)), list(accumulate(dy))
+
+
+ def generate_interior(self, cell_data):
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+ rows = self.options.rows
+ base_height = self.options.base_height * unit_factor
+ distance = self.options.distance * unit_factor
+
+ interiors = []
+
+ for i in range(rows):
+ dx = cell_data['dx'][i]
+ dy = cell_data['dy'][i] + base_height + i * distance
+ pattern = cell_data['interior'][i]
+ interiors.append(Path.list_add(pattern, (dx, dy)))
+
+ return interiors
+
+
+ def generate_horizontal_dividers(self, cell_data):
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+ rows = self.options.rows
+ base_height = self.options.base_height * unit_factor
+ distance = self.options.distance * unit_factor
+
+ divider = cell_data['divider']
+ dividers = []
+
+ if self.options.add_base_slot:
+ dividers.append(divider + (0, base_height))
+
+ for i in range(1, rows):
+ dx = cell_data['dx'][i]
+ dy = cell_data['dy'][i] + base_height + (i - 1) * distance
+ dividers.append(divider + (dx, dy))
+ if self.options.add_middle_slot:
+ dividers.append(divider + (dx, dy + distance))
+
+ if self.options.add_base_slot:
+ dx = cell_data['dx'][-1]
+ dy = cell_data['dy'][-1] + base_height + (rows - 1) * distance
+ dividers.append(divider + (dx, dy))
+
+ return dividers
+ # pass
+
+
+ def generate_all_slots(self, cell_data):
+ dx_ = cell_data['dx']
+ dy_ = cell_data['dy']
+
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+
+ # retrieve saved parameters, and apply unit factor where needed
+ cols = self.options.cols
+ rows = self.options.rows
+ width = self.options.width * unit_factor
+
+ dist = self.options.distance * unit_factor
+ base_height = self.options.base_height * unit_factor
+ height = [dy_[i] + base_height + (i - 1) * dist for i in range(rows+1)]
+
+ base = {'left': [],
+ 'right': [],
+ 'slots': []}
+
+ if self.options.add_base_slot:
+ base_slot_height = self.options.base_slot_height * unit_factor
+ base_slot_width = self.options.base_slot_width * unit_factor
+ base_slot_sizes = [base_slot_height, base_slot_width, base_height, width]
+
+ base_slot_top = generate_slot_line(cols, +int(self.options.base_slot_position), *base_slot_sizes)
+ base_slot_bot = generate_slot_line(cols, -int(self.options.base_slot_position), *base_slot_sizes)
+
+ base['slots'] = [base_slot_top, Path.list_add(base_slot_bot, (dx_[-1], height[-1]))]
+
+ base['left'] = [Path([(0, 0), (0, base_height)], style = 'e'),
+ Path([(dx_[-1], height[-1]),
+ (dx_[-1], height[-1] + base_height)], style = 'e')]
+
+ base['right'] = [Path([(dx_[-1] + cols*width, height[-1] + base_height),
+ (dx_[-1] + cols*width, height[-1] + base_height)], style = 'e'),
+ Path([(cols*width, base_height),
+ (cols*width, 0)], style = 'e')]
+
+ middle = {'left': [],
+ 'right': [],
+ 'slots': []}
+
+ if self.options.add_middle_slot:
+ middle_slot_height = self.options.middle_slot_height * unit_factor
+ middle_slot_width = self.options.middle_slot_width * unit_factor
+ middle_slot_sizes = [middle_slot_height, middle_slot_width, dist, width]
+
+ middle_slot = generate_slot_line(cols, +int(self.options.middle_slot_position), *middle_slot_sizes)
+
+ middle['slots'] = [Path.list_add(middle_slot,
+ (dx_[i], height[i]))
+ for i in range(1, rows)]
+
+ middle['left'] = [Path([(0, 0),
+ (0, dist)], style='e') +
+ (dx_[i+1], dy_[1] + dist + height[i]) for i in range(rows-1)]
+ middle['right'] = [
+ Path([(0, height[rows-2] + base_height + (rows - 1) * dist),
+ (0, dy_[-2] + base_height + (rows - 2) * dist)], style='e') +
+ (dx_[-(i+2)] + cols*width, -dy_[i] - i*dist) for i in range(rows - 1)]
+
+ return base, middle
+
+ def generate_fused_edge_points(self, base, middle, cell_data):
+ unit_factor = self.calc_unit_factor()
+ base_height = self.options.base_height * unit_factor
+ distance = self.options.distance * unit_factor
+ rows = self.options.rows
+
+ edges = []
+ if self.options.add_base_slot: edges.append(base['left'][0])
+ for i in range(rows):
+ cell_left = cell_data['edge_left'][i]
+ dx = cell_data['dx'][i]
+ edges.append(cell_left + (dx, cell_data['dy'][i] + base_height + i * distance))
+ if self.options.add_middle_slot and i < rows - 1:
+ edges.append(middle['left'][i] + (0, 0))
+ if self.options.add_base_slot: edges.append(base['left'][1])
+
+ if self.options.add_base_slot: edges.append(base['right'][0])
+ for i in range(rows):
+ cell_right = cell_data['edge_right'][-(i + 1)]
+ dx = cell_data['dx'][-(i + 2)]
+ edges.append(cell_right + (dx, cell_data['dy'][rows - i - 1] + base_height + (rows - i - 1) * distance))
+ if self.options.add_middle_slot and i < rows - 1:
+ edges.append(middle['right'][i] + (0, 0))
+ if self.options.add_base_slot: edges.append(base['right'][1])
+
+ return Path.get_points(edges)
+
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Bendy.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Bendy.py
new file mode 100755
index 0000000..3355f50
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Bendy.py
@@ -0,0 +1,377 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+from math import pi, sin, cos, tan, asin, acos, atan, sqrt
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+MIN = 0.0001
+
+
+class Bendy_Straw(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--pattern', type=self.str, default='bendy_straw')
+ self.add_argument('--pattern_type', type=self.str, default='origami')
+ self.add_argument('--parameter_type', type=self.str, default='angles')
+ self.add_argument('--n', type=self.int, default=6)
+ self.add_argument('--lines', type=self.int, default=3)
+ self.add_argument('--radius', type=self.float, default=25.0)
+ # self.add_argument('--attachment_length', type=self.float, default=3.0)
+ # self.add_argument('--attachment_length', type=self.int, default=20)
+ self.add_argument('--radial_ratio', type=self.float, default=0.75)
+ # self.add_argument('--alpha1', type=self.float, default=45.0)
+ # self.add_argument('--alpha2', type=self.float, default=45.0)
+ self.add_argument('--alpha1', type=self.int, default=45)
+ self.add_argument('--alpha2', type=self.int, default=35)
+ self.add_argument('--h1', type=self.float, default=1)
+ self.add_argument('--h2', type=self.float, default=2)
+
+ self.add_argument('--vertex_base_outer_bool', type=self.bool, default=False)
+ self.add_argument('--vertex_base_inner_bool', type=self.bool, default=False)
+ self.add_argument('--vertex_radius_outer_bool', type=self.bool, default=False)
+ self.add_argument('--vertex_radius_inner_bool', type=self.bool, default=False)
+
+ self.add_argument('--add_attachment', type=self.bool, default=False)
+
+ # slot options for support ring
+ self.add_argument('--base_height', type=self.float, default=5.0)
+ self.add_argument('--add_base_slot', type=self.bool, default=False)
+ self.add_argument('--center_base_slot', type=self.bool, default=False)
+ self.add_argument('--base_slot_height', type=self.float, default=3.0)
+ self.add_argument('--base_slot_width', type=self.float, default=3.0)
+ self.add_argument('--distance', type=self.float, default=3.0)
+ self.add_argument('--add_distance_slot', type=self.bool, default=False)
+ self.add_argument('--distance_slot_height', type=self.float, default=3.0)
+ self.add_argument('--distance_slot_width', type=self.float, default=3.0)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+ vertex_radius = self.options.vertex_radius * unit_factor
+
+ # retrieve saved parameters, and apply unit factor where needed
+ pattern_type = self.options.pattern_type
+ n = self.options.n
+ lines = self.options.lines
+ radial_ratio = self.options.radial_ratio
+ R = self.options.radius * unit_factor
+ distance = self.options.distance * unit_factor
+ base_height = self.options.base_height * unit_factor
+ # add_attachment = self.options.add_attachment
+ # attachment_length = self.options.attachment_length * unit_factor
+ r = R * radial_ratio
+
+ if (self.options.parameter_type == 'angles'):
+ alpha1 = self.options.alpha1 * pi / 180
+ alpha2 = self.options.alpha2 * pi / 180
+ elif (self.options.parameter_type == 'heights'):
+ alpha1 = atan(self.options.h1 * unit_factor / (R - r))
+ alpha2 = atan(self.options.h2 * unit_factor / (R - r))
+
+ # calculating pattern parameters
+ l1 = (R - r) / cos(alpha1)
+ l2 = (R - r) / cos(alpha2)
+ A = 2 * R * sin(pi / n)
+ # attachment_length = 0.01 * self.options.attachment_length * A
+ a = A * radial_ratio
+ dx = (A - a) / 2
+ beta1 = acos(cos(alpha1) * sin(pi / n))
+ beta2 = acos(cos(alpha2) * sin(pi / n))
+ b1 = l1 * sin(beta1)
+ b2 = l2 * sin(beta2)
+ height = (b1 + b2) * lines + distance * (lines - 1)
+
+ if self.options.add_attachment: n = n+1
+
+ #
+ # big horizontal mountains grid
+ #
+ mountain_horizontal_stroke = Path([(0, base_height), (A * n, base_height)], 'm')
+ horizontal_grid_mountain = []
+ for i in range(1, lines):
+ horizontal_grid_mountain.append(
+ mountain_horizontal_stroke + (0, distance * (i - 1) + b1 * (i + 0) + b2 * (i + 0)))
+ horizontal_grid_mountain.append(
+ mountain_horizontal_stroke + (0, distance * (i + 0) + b1 * (i + 0) + b2 * (i + 0)))
+ if distance < MIN:
+ horizontal_grid_mountain = horizontal_grid_mountain[::2]
+ if base_height > MIN:
+ horizontal_grid_mountain.insert(0, mountain_horizontal_stroke)
+ horizontal_grid_mountain.append(
+ mountain_horizontal_stroke + (0, distance * (lines - 1) + b1 * lines + b2 * lines))
+
+ # reverse every other horizontal stroke for faster laser-cutting
+ for i in range(len(horizontal_grid_mountain)):
+ if (i % 2 == 0):
+ horizontal_grid_mountain[i].points.reverse()
+
+ #
+ # diamond shapes
+ #
+
+ # full diamond patterns styles, depending on pattern type
+ style_diag_left = 'm'
+ style_diag_right = 'm'
+ style_diag = 'v'
+ style_vert = 'm'
+ style_hori_left = 'm'
+ style_hori_right = 'm'
+ if pattern_type == 'origami' or pattern_type == 'origami_bent':
+ style_hori_left = 'v'
+ style_diag_left = 'n'
+ style_diag_right = 'v'
+ elif pattern_type == 'origami2':
+ style_hori_right = 'v'
+ style_diag_left = 'v'
+ style_diag_right = 'n'
+ elif pattern_type == 'kirigami1':
+ style_vert = 'v'
+ style_hori_left = 'c'
+ style_hori_right = 'c'
+ elif pattern_type == 'kirigami2':
+ style_diag_left = 'c'
+ style_diag_right = 'c'
+ style_hori_left = 'n'
+ style_hori_right = 'n'
+ style_vert = 'n'
+
+ # diamond pattern with strokes of different styles
+ stroke_base = Path([(0, 0), (0, base_height)], 'm')
+ diamond_diagonals_left = Path([(0, base_height), (-dx, base_height + b1), (0, base_height + b1 + b2)],
+ style_diag_left)
+ diamond_diagonals_right = Path([(0, base_height + b1 + b2), (dx, base_height + b1), (0, base_height)],
+ style_diag_right)
+ diamond_vertical = Path([(0, base_height), (0, base_height + b1 + b2)], style_vert)
+ stroke_distance = Path([(0, base_height + b1 + b2), (0, distance + base_height + b1 + b2)], 'm')
+ diamond_horizontal_left = Path([(-dx, 0), (0, 0)], style_hori_left)
+ diamond_horizontal_right = Path([(0, 0), (dx, 0)], style_hori_right)
+
+ diamond = [diamond_diagonals_left, diamond_diagonals_right, diamond_vertical]
+
+ if pattern_type == 'origami_bent':
+ bent_diagonal = Path([(0, base_height + b1 + b2), (dx, base_height + b1), (0, base_height)], 'm')
+ bent_horizontal = Path([(0, 0), (dx, 0)], 'v')
+ line_bent = []
+
+
+ # drawing lines with the diamond shapes
+ line_left = []
+ line_middle = []
+ line_right = []
+ if base_height > MIN:
+ line_middle.append(stroke_base)
+ if pattern_type == 'origami_bent':
+ line_bent.append(stroke_base)
+ for i in range(lines):
+ delta = (0, (distance + b1 + b2) * i)
+ if pattern_type != 'kirigami2':
+ line_left.append(diamond_diagonals_right + delta)
+ line_middle = line_middle + Path.list_add(diamond, delta)
+ if pattern_type != 'kirigami2':
+ line_right.append(diamond_diagonals_left + delta)
+ if distance > MIN and i < lines - 1:
+ line_middle.append(stroke_distance + delta)
+
+ if pattern_type == 'origami_bent':
+ line_bent = line_bent + [bent_diagonal + delta]
+ if distance > MIN and i < lines - 1:
+ line_bent.append(stroke_distance + delta)
+
+ if base_height > MIN:
+ line_middle.append(stroke_base + (0, base_height + height))
+ if pattern_type == 'origami_bent':
+ line_bent.append(stroke_base + (0, base_height + height))
+
+ # creating full diamond patterns
+ line_left = line_left[::-1]
+ diamond_patterns_full = [line_left]
+ for i in range(n - 1):
+ delta = (A * (i + 1), 0)
+ if pattern_type == 'origami_bent' and i == 2:
+ diamond_patterns_full.append(Path.list_add(line_bent, delta))
+ else:
+ diamond_patterns_full.append(Path.list_add(line_middle, delta))
+ diamond_patterns_full.append(Path.list_add(line_right, (A * n, 0)))
+
+ #
+ # small horizontal alternate style grid
+ #
+ valley_points = [(dx, 0),
+ (dx + a, 0)]
+ valley_stroke = Path(valley_points, 'v')
+ if pattern_type == 'kirigami2':
+ horizontal_line = []
+ else:
+ horizontal_line = [diamond_horizontal_right + (0, 0)]
+ for i in range(n):
+ if not (pattern_type == 'origami_bent' and i == 3):
+ horizontal_line.append(valley_stroke + ((a + 2 * dx) * i, 0))
+ if (pattern_type != 'kirigami2'):
+ if not (pattern_type == 'origami_bent' and i == 3):
+ horizontal_line.append(diamond_horizontal_left + (A + (a + 2 * dx) * i, 0))
+ if pattern_type == 'origami_bent' and i==2:
+ horizontal_line.append(bent_horizontal + (A + (a + 2 * dx) * i, 0))
+ elif i < n - 1:
+ horizontal_line.append(diamond_horizontal_right + (A + (a + 2 * dx) * i, 0))
+ horizontal_grid_alternate = []
+ for i in range(lines):
+ horizontal_grid_alternate.append(
+ Path.list_add(horizontal_line, (0, base_height + distance * (i + 0) + b1 * (i + 1) + b2 * (i + 0))))
+
+ # reverse every other horizontal stroke for faster laser-cutting
+ for i in range(len(horizontal_grid_alternate)):
+ if (i % 2 == 0):
+ horizontal_grid_alternate[i] = Path.list_invert(horizontal_grid_alternate[i])
+
+ # for i in range(len(horizontal_grid_alternate)):
+ # inkex.debug(i)
+ # Path.debug_points(horizontal_grid_alternate[i])
+ # inkex.debug('\n')
+
+ #
+ # edge drawing
+ #
+ self.edge_points = [(0, 0)]
+ self.edge_points.append((A * n, 0))
+
+ # rectangles for attachments at base and between cells
+
+ # add upper base attachment
+ self.edge_points.append((A * n, base_height))
+
+ # draw attachment between cells and inside cells
+ for i in range(lines):
+ self.edge_points.append((A * n + 0, base_height + (b1 + b2) * (i + 0) + distance * i))
+ if pattern_type == 'kirigami2':
+ self.edge_points.append((A * n - dx, base_height + b1 * (i + 1) + (b2 + distance) * i))
+ self.edge_points.append((A * n + 0, base_height + (b1 + b2) * (i + 1) + distance * i))
+
+ self.edge_points.append((A * n, height + 2 * base_height))
+ self.edge_points.append((0, height + 2 * base_height))
+
+ # if full kirigami selected, cut left side next to cells
+ if pattern_type == 'kirigami2':
+ for i in range(lines):
+ self.edge_points.append((0, height + base_height - (b1 + b2) * (i + 0) - distance * i))
+ self.edge_points.append((dx, height + base_height - b2 * (i + 1) - (b1 + distance) * i))
+ self.edge_points.append((0, height + base_height - (b1 + b2) * (i + 1) - distance * i))
+
+ #
+ # slots drawing
+ #
+ center_slot = self.options.center_base_slot
+ base_slots = []
+ if self.options.add_base_slot:
+ base_slot_height = self.options.base_slot_height
+ base_slot_width = self.options.base_slot_width
+ if base_slot_height > base_height or base_slot_width > A:
+ inkex.debug('Base slot dimensions are too big')
+ base_slot_height = min(base_height, base_slot_height)
+ base_slot_width = min(A, base_slot_width)
+ if base_slot_height > 0 and base_slot_width > 0:
+ points = [(0, 0),
+ (0, base_slot_height),
+ (base_slot_width, base_slot_height),
+ (base_slot_width, 0,)]
+ base_slot = Path(points, 'c', closed=True) + ((A - base_slot_width)/2, (base_height - base_slot_height)/(1+center_slot))
+ base_slots_line = []
+ for i in range(n):
+ base_slots_line.append(base_slot + (A*i, 0))
+ base_slots = [base_slots_line]
+ base_slots.append(Path.list_add(base_slots_line, (0, height+base_slot_height + (base_height - base_slot_height)*center_slot)))
+
+ dist_slots = []
+ if self.options.add_distance_slot:
+ dist_slot_height = self.options.distance_slot_height
+ dist_slot_width = self.options.distance_slot_width
+ if dist_slot_height > distance or dist_slot_width > A:
+ inkex.debug('Dimensions of slots between cells are too big')
+ dist_slot_height = min(distance, dist_slot_height)
+ dist_slot_width = min(A, dist_slot_width)
+
+ if dist_slot_height > 0 and dist_slot_width > 0:
+ points = [(0, 0),
+ (0, dist_slot_height),
+ (dist_slot_width, dist_slot_height),
+ (dist_slot_width, 0,)]
+ dist_slot = Path(points, 'c', closed=True) + ((A - dist_slot_width)/2, base_height+b1+b2 + (distance - dist_slot_height)/2)
+ dist_slots_line = []
+ for i in range(n):
+ dist_slots_line.append(dist_slot + (A*i, 0))
+
+ for i in range(lines-1):
+ dist_slots.append(Path.list_add(dist_slots_line, (0, i*(b1+b2+distance))))
+
+
+
+ # sending lines to draw
+ self.path_tree = [horizontal_grid_mountain, horizontal_grid_alternate, diamond_patterns_full, base_slots, dist_slots]
+
+ #
+ # vertices drawing
+ #
+ # outer base vertices?
+ if self.options.vertex_base_outer_bool:
+ self.vertex_points = self.vertex_points + [(A * i, height + base_height * 2) for i in range(n + 1)]
+ self.vertex_points = self.vertex_points + [(A*i, 0) for i in range(n+1)]
+
+ # inner base vertices?
+ if self.options.vertex_base_inner_bool:
+ self.vertex_points = self.vertex_points + [(A*i, base_height) for i in range(n+1)]
+ self.vertex_points = self.vertex_points + [(A*i, height+base_height) for i in range(n+1)]
+ for j in range(lines-1):
+ self.vertex_points = self.vertex_points + [(A*i, base_height+((b1+b2)*(j+1))+distance*j) for i in range(n+1)] + \
+ [(A*i, base_height+((b1+b2)*(j+1))+distance*(j+1)) for i in range(n+1)]
+
+ # radius vertices?
+ if self.options.vertex_radius_outer_bool and pattern_type != 'kirigami2':
+ for j in range(lines):
+ i_range = list(range(3)) + list(range(3+(pattern_type=='origami_bent'), n + 1))
+ self.vertex_points = self.vertex_points + [(A * i, base_height + b1*(j+1) + (b2+distance)*j) for i in i_range]
+ if self.options.vertex_radius_inner_bool:
+ for j in range(lines):
+ if pattern_type != 'origami2':
+ # pass
+ self.vertex_points = self.vertex_points + [(dx + A * i, base_height + b1*(j+1) + (b2+distance)*j) for i in range(n)]
+ if pattern_type[:7] != 'origami':
+ # pass
+ self.vertex_points = self.vertex_points + [(-dx + A * (i + 1), base_height + b1 * (j + 1) + (b2 + distance) * j) for i in range(n)]
+ # for j in range(lines):
+
+
+
+
+ # self.vertex_points = self.vertex_points + vertices_base
+
+ # self.vertex_points = self.vertex_points + self.edge_points
+ # diamond_patterns_full_simple = Path.list_simplify(diamond_patterns_full)
+ # for path in diamond_patterns_full:
+ # # path = Path.list_simplify(path)
+ # # path = Path.list_simplify(path[0])
+ # if path.style != 'n':
+ # self.vertex_points = self.vertex_points + path.points
+
+
+
+#
+
+# Main function, creates an instance of the Class and calls self.draw() to draw the origami on inkscape
+# self.draw() is either a call to inkex.affect() or to svg.run(), depending on python version
+if __name__ == '__main__':
+ e = Bendy_Straw() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Kresling.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Kresling.py
new file mode 100644
index 0000000..c407327
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Kresling.py
@@ -0,0 +1,163 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+from math import pi, sin, asin, cos, tan, acos, sqrt
+import inkex
+import os
+
+from Path import Path
+from Pattern import Pattern
+from Cylindrical import Cylindrical
+
+
+class Kresling(Cylindrical):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Cylindrical.__init__(self) # Must be called in order to parse common options
+
+ self.add_argument('--pattern', type=self.str, default="kresling")
+
+ self.add_argument('--measure_value', type=self.float, default=10.0)
+ self.add_argument('--measure_type', type=self.str, default=60)
+ self.add_argument('--parameter_type', type=self.str, default=60)
+ self.add_argument('--radial_ratio', type=self.float, default=0.5)
+ self.add_argument('--angle_ratio', type=self.float, default=0.5)
+ self.add_argument('--lambdatheta', type=self.float, default=45)
+
+ def parse_parameters(self):
+ n = self.options.sides
+ theta = pi * (n - 2) / (2 * n)
+ # define ratio parameter
+ parameter = self.options.parameter_type
+ if parameter == 'radial_ratio':
+ radial_ratio = self.options.radial_ratio
+ max_radial_ratio = sin((pi / 4) * (1. - 2. / n))
+ if radial_ratio > max_radial_ratio:
+ inkex.errormsg(
+ _("For polygon of {} sides, the maximal radial ratio is = {}".format(n, max_radial_ratio)))
+ radial_ratio = max_radial_ratio
+ self.options.angle_ratio = 1 - 2 * n * asin(radial_ratio) / ((n - 2) * pi)
+
+ elif parameter == 'lambdatheta':
+ lambdatheta = self.options.lambdatheta
+ angle_min = 45. * (1 - 2. / n)
+ angle_max = 2 * angle_min
+ if lambdatheta < angle_min:
+ inkex.errormsg(_(
+ "For polygon of {} sides, phi must be between {} and {} degrees, \nsetting lambda*theta = {}\n".format(
+ n, angle_min, angle_max, angle_min)))
+ lambdatheta = angle_min
+ elif lambdatheta > angle_max:
+ inkex.errormsg(_(
+ "For polygon of {} sides, phi must be between {} and {} degrees, \nsetting lambda*theta = {}\n".format(
+ n, angle_min, angle_max, angle_max)))
+ lambdatheta = angle_max
+ self.options.angle_ratio = lambdatheta * n / (90. * (n - 2.))
+
+ # define some length
+ mtype = self.options.measure_type
+ mvalue = self.options.measure_value
+ angle_ratio = self.options.angle_ratio
+ if mtype == 'a':
+ radius = 0.5 * mvalue / (sin(pi / n))
+ if mtype == 'b':
+ A = cos(theta * (1 - angle_ratio))
+ B = sin(pi / n)
+ C = cos(theta * angle_ratio)
+ radius = 0.5 * mvalue / sqrt(A ** 2 + B ** 2 - 2 * A * B * C)
+ elif mtype == 'l':
+ radius = 0.5 * mvalue / cos(theta * (1 - angle_ratio))
+ elif mtype == 'radius_external':
+ radius = mvalue
+ elif mtype == 'radius_internal':
+ radius = mvalue / (sin(theta * (1 - angle_ratio)))
+ elif mtype == 'diameter_external':
+ radius = 0.5 * mvalue
+ elif mtype == 'diameter_internal':
+ radius = 0.5 * mvalue / sin(theta * (1 - angle_ratio))
+
+ if self.options.pattern == 'mirrowed':
+ self.options.mirror_cells = True
+ else:
+ self.options.mirror_cells = False
+ self.options.radius = radius
+
+ def generate_cell(self):
+ """ Generate the the origami cell
+ """
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+ rows = self.options.rows
+ sides = self.options.sides
+ cols = self.options.cols
+ radius = self.options.radius * unit_factor
+ width = self.options.width * unit_factor
+ # vertex_radius = self.options.vertex_radius * unit_factor
+
+ angle_ratio = self.options.angle_ratio
+ mirror_cells = self.options.mirror_cells
+
+ theta = (pi/2.)*(1 - 2./sides)
+ l = 2.*radius*cos(theta*(1.-angle_ratio))
+ dy = l * sin(theta * angle_ratio)
+ dx = l * cos(theta * angle_ratio) - width
+
+ # init dict that holds everything
+ cell_data = {}
+
+ # divider (supposed to be the same)
+ cell_data['divider'] = Path([(0, 0), (width * cols, 0)], style='m')
+
+ # IMPORTANT: left edges from TOP to BOTTOM
+ edge_left = [Path([(0, 0), (dx, dy)], style='e')]
+ if mirror_cells:
+ edge_left.append(Path([(0, 0), (-dx, dy)], style='e'))
+ cell_data['edge_left'] = [edge_left[i % (1 + mirror_cells)] for i in range(rows)]
+
+ # IMPORTANT: right edges from BOTTOM to TOP
+ edge_right = [Path([(cols * width + dx, dy), (cols * width, 0)], style='e')]
+ if mirror_cells:
+ edge_right.append(Path([(cols * width - dx, dy), (cols * width, 0)], style='e'))
+ cell_data['edge_right'] = [edge_right[i % (1 + mirror_cells)] for i in range(rows)]
+
+ # rest of cell
+ zigzags = [Kresling.generate_kresling_zigzag(sides, cols, radius, angle_ratio)]
+ if mirror_cells:
+ zigzags.append(Path.list_reflect(zigzags[0], (0, dy / 2), (dx, dy / 2)))
+ zigzags[1] = Path.list_add(zigzags[1], (-dx, 0))
+
+ cell_data['interior'] = [zigzags[i % (1 + mirror_cells)] for i in range(rows)]
+
+ return cell_data
+
+ @staticmethod
+ def generate_kresling_zigzag(sides, cols, radius, angle_ratio):
+ # def generate_kresling_zigzag(sides, radius, angle_ratio, add_attachment):
+
+ theta = (pi / 2.) * (1 - 2. / sides)
+ l = 2. * radius * cos(theta * (1. - angle_ratio))
+ a = 2. * radius * sin(pi / sides)
+ dy = l * sin(theta * angle_ratio)
+ dx = l * cos(theta * angle_ratio) - a
+
+ points = []
+ styles = []
+
+ for i in range(cols):
+ points.append((i * a, 0))
+ points.append(((i + 1) * a + dx, dy))
+ styles.append('v')
+ if i != cols - 1:
+ styles.append('m')
+ # elif add_attachment:
+ # points.append((sides * a, 0))
+ # styles.append('m')
+
+ path = Path.generate_separated_paths(points, styles)
+ return path
+
+if __name__ == '__main__':
+
+ e = Kresling()
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Template.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Template.py
new file mode 100644
index 0000000..c384740
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Cylindrical_Template.py
@@ -0,0 +1,73 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+from abc import abstractmethod
+from math import pi, sin, cos, tan, asin, acos, atan, sqrt
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+from Cylindrical import Cylindrical
+
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+
+class Template(Cylindrical):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Cylindrical.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--pattern', type=self.str, default='cylindrical_template')
+ self.add_argument('--length', type=self.float, default=10.)
+ self.add_argument('--angle', type=self.int, default=0)
+
+ def generate_cell(self):
+ """ Generate the the origami cell
+ """
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+ sides = self.options.sides
+ cols = self.options.cols
+ rows = self.options.rows
+
+ length = self.options.length * unit_factor
+ angle = self.options.angle * unit_factor
+ width = self.options.width * unit_factor # use pre-calculated width
+
+ dx = length * sin(pi * angle / 180)
+ dy = length * cos(pi * angle / 180)
+
+ # init dict that holds everything
+ cell_data = {}
+
+ # divider can be set for performance, or it can be calculated automatically, supposing a mountain fold
+ # cell_data['divider'] = Path([(0,0), (width*cols, 0)], style='m')
+
+ # Only left or right edges can be implemented, or both for performance
+ # IMPORTANT: left edges from TOP to BOTTOM
+ cell_data['edge_left'] = [Path([(0,0), (dx, dy)], style='e')]*rows
+
+ # # IMPORTANT: right edges from BOTTOM to TOP
+ # cell_data['edge_right'] = [Path([(cols*width + dx, dy), (cols*width, 0)], style='e')]*rows
+
+ # rest of cell
+ single = [Path([(0, 0), (width + dx, dy)], 'v'), Path([(width + dx, dy), (width, 0)], 'm')]
+ pattern = [Path.list_add(single, (width*i, 0)) for i in range(cols)]
+ pattern = Path.list_simplify(pattern)
+ cell_data['interior'] = [pattern]*rows
+
+ return cell_data
+
+
+# Main function, creates an instance of the Class and calls self.draw() to draw the origami on inkscape
+# self.draw() is either a call to inkex.affect() or to svg.run(), depending on python version
+if __name__ == '__main__':
+ e = Template() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Hypar.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Hypar.py
new file mode 100755
index 0000000..6b8675b
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Hypar.py
@@ -0,0 +1,122 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+
+from math import pi, tan, sqrt, sin, cos
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+
+class Hypar(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--pattern', type=self.str, default='template1')
+ self.add_argument('--radius', type=self.float, default=10.0)
+ self.add_argument('--sides', type=self.int, default=4)
+ self.add_argument('--rings', type=self.int, default=7)
+ self.add_argument('--simplify_center', type=self.bool, default=0)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve saved parameters
+ unit_factor = self.calc_unit_factor()
+ vertex_radius = self.options.vertex_radius * unit_factor
+ pattern = self.options.pattern
+ radius = self.options.radius * unit_factor
+ sides = self.options.sides
+ rings = self.options.rings
+ simplify_center = self.options.simplify_center
+ sin_ = sin(pi / float(sides))
+ a = radius*sin_ # half of length of polygon side
+ H = radius*sqrt(1 - sin_**2)
+
+ polygon = Path.generate_polygon(sides, radius, 'e')
+
+ # # OLD diagonals generation with universal creases
+ # diagonals = []
+ # for i in range(sides):
+ # diagonals.append(Path([(0, 0), polygon.points[i]], 'u'))
+ # points = [(x, y) for x, y in polygon.points]
+ # diagonals = diagonals + [Path.generate_separated_paths(points, 'm')]
+
+ # # modify center if needed
+ # if simplify_center:
+ # for i in range(sides):
+ # if i % 2 == 0:
+ # p2 = diagonals[i].points[1]
+ # diagonals[i].points[0] = (1. / (rings + 1) * p2[0], 1. / (rings + 1) * p2[1])
+
+ # separate generic closed ring to create edges
+ self.edge_points = polygon.points
+
+ # vertex and diagonal lines creation
+ vertex_line = []
+ diagonal_line = []
+ for i in range(1, rings + 2):
+ y1 = a * (float(i - 1) / (rings + 1.))
+ x1 = H * float(i - 1) / (rings + 1.)
+ y2 = a * (float(i) / (rings + 1.))
+ x2 = H * float(i) / (rings + 1.)
+ vertex_line.append((Path((x2, y2), style='p', radius=vertex_radius)))
+ diagonal_line.append((Path([(x1, y1), (x2, y2)], style='m' if i % 2 else 'v')))
+
+ # rotation of vertices and diagonals for completing the drawing
+ diagonals = []
+ vertices = [Path((0, 0), style='p', radius=vertex_radius)]
+ for i in range(sides):
+ vertices = vertices+Path.list_rotate(vertex_line, i * 2 * pi / float(sides))
+ diagonals = diagonals+[Path.list_rotate(diagonal_line, i * 2 * pi / float(sides))]
+
+ # modify center if needed
+ if simplify_center:
+ for i in range(sides):
+ if i % 2 == 0:
+ del diagonals[i][0]
+
+ # inkex.debug(len(diagonals))
+ # inkex.debug(len(diagonals[0]))
+ # diagonals = diagonals + diagonal
+
+ # scale generic closed ring to create inner rings
+ inner_rings = []
+ for i in range(rings + 1):
+ inner_rings.append(polygon * (float(i)/(rings+1)))
+ inner_rings[i].style = 'v' if i % 2 else 'm'
+
+ # create points for zig zag pattern
+ zig_zags = []
+ if pattern != "classic":
+ zig_zag = []
+ for i in range(1, rings + 1):
+ y_out = a * ((i + 1.) / (rings + 1.))
+ y_in = a * (float(i) / (rings + 1.))
+ x_out = H * (i + 1.) / (rings + 1.)
+ x_in = H * float(i) / (rings + 1.)
+
+ if pattern == "alternate_asymmetric" and i % 2:
+ zig_zag.append(Path([(x_in, -y_in), (x_out, +y_out)], style='u'))
+ else:
+ zig_zag.append(Path([(x_in, +y_in), (x_out, -y_out)], style='u'))
+
+ # reflect zig zag pattern to create all sides
+ zig_zags.append(zig_zag)
+ for i in range(sides - 1):
+ points = diagonals[i][0].points
+ zig_zags.append(Path.list_reflect(zig_zags[i], points[0], points[1]))
+
+ self.translate = (radius, radius)
+ self.path_tree = [diagonals, zig_zags, inner_rings, vertices]
+
+# Main function, creates an instance of the Class and calls inkex.affect() to draw the origami on inkscape
+if __name__ == '__main__':
+ e = Hypar() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Kresling.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Kresling.py
new file mode 100755
index 0000000..614006e
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Kresling.py
@@ -0,0 +1,162 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+from math import pi, sin, cos, tan, acos, sqrt
+import inkex
+import os
+
+from Path import Path
+from Pattern import Pattern
+
+
+class Kresling(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ self.add_argument('--pattern', type=self.str, default="kresling")
+ self.add_argument('--lines', type=self.int, default=1)
+ self.add_argument('--sides', type=self.int, default=3)
+ self.add_argument('--add_attachment', type=self.bool, default=False)
+ self.add_argument('--attachment_percentage', type=self.float, default=100.)
+ self.add_argument('--mirror_cells', type=self.bool, default=False)
+
+ @staticmethod
+ def generate_kresling_zigzag(sides, radius, angle_ratio, add_attachment):
+
+ theta = (pi / 2.) * (1 - 2. / sides)
+ l = 2. * radius * cos(theta * (1. - angle_ratio))
+ a = 2. * radius * sin(pi / sides)
+ # b = sqrt(a * a + l * l - 2 * a * l * cos(angle_ratio * theta))
+ # phi = abs(acos((l * l + b * b - a * a) / (2 * l * b)))
+ # gamma = pi / 2 - angle_ratio * theta - phi
+ dy = l * sin(theta * angle_ratio)
+ dx = l * cos(theta * angle_ratio) - a
+
+ points = []
+ styles = []
+
+ for i in range(sides):
+ points.append((i * a, 0))
+ points.append(((i + 1) * a + dx, -dy))
+ styles.append('v')
+ if i != sides - 1:
+ styles.append('m')
+ elif add_attachment:
+ points.append((sides * a, 0))
+ styles.append('m')
+
+ path = Path.generate_separated_paths(points, styles)
+ return path
+
+ def generate_path_tree(self):
+ """ Specialized path generation for Waterbomb tesselation pattern
+ """
+ unit_factor = self.calc_unit_factor()
+ vertex_radius = self.options.vertex_radius * unit_factor
+ lines = self.options.lines
+ sides = self.options.sides
+ radius = self.options.radius * unit_factor
+ angle_ratio = self.options.angle_ratio
+ mirror_cells = self.options.mirror_cells
+
+ theta = (pi/2.)*(1 - 2./sides)
+ l = 2.*radius*cos(theta*(1.-angle_ratio))
+ a = 2.*radius*sin(pi/sides)
+ # b = sqrt(a*a + l*l - 2*a*l*cos(angle_ratio*theta))
+ # phi = abs(acos((l*l + b*b - a*a)/(2*l*b)))
+ # gamma = pi/2 - angle_ratio*theta - phi
+ # dy = b*cos(gamma)
+ # dx = b*sin(gamma)
+ dy = l * sin(theta * angle_ratio)
+ dx = l * cos(theta * angle_ratio) - a
+
+ add_attachment = self.options.add_attachment
+ attachment_percentage = self.options.attachment_percentage/100.
+ attachment_height = a*(attachment_percentage-1)*tan(angle_ratio*theta)
+
+ vertices = []
+ for i in range(sides + 1):
+ for j in range(lines + 1):
+ if mirror_cells:
+ vertices.append(Path((dx*((lines - j)%2) + a*i, dy*j), style='p', radius=vertex_radius))
+ else:
+ vertices.append(Path((dx*(lines - j) + a*i, dy*j), style='p', radius=vertex_radius))
+
+ # create a horizontal grid, then offset each line according to angle
+ grid_h = Path.generate_hgrid([0, a * sides], [0, dy * lines], lines, 'm')
+
+ if not mirror_cells:
+ # shift every mountain line of the grid to the right by increasing amounts
+ grid_h = Path.list_add(grid_h, [(i * dx, 0) for i in range(lines - 1, 0, -1)])
+ else:
+ # shift every OTHER mountain line of the grid a bit to the right
+ grid_h = Path.list_add(grid_h, [((i%2)*dx, 0) for i in range(lines-1, 0, -1)])
+ if add_attachment:
+ for i in range(lines%2, lines-1, 2):
+ # hacky solution, changes length of every other mountain line
+ grid_h[i].points[1-i%2] = (grid_h[i].points[1-i%2][0] + a*attachment_percentage, grid_h[i].points[1-i%2][1])
+
+ # create MV zigzag for Kresling pattern
+ zigzag = Kresling.generate_kresling_zigzag(sides, radius, angle_ratio, add_attachment)
+ zigzags = []
+
+ # duplicate zigzag pattern for desired number of cells
+ if not mirror_cells:
+ for i in range(lines):
+ zigzags.append(Path.list_add(zigzag, (i * dx, (lines - i) * dy)))
+ else:
+ zigzag_mirror = Path.list_reflect(zigzag, (0, lines * dy / 2), (dx, lines * dy / 2))
+ for i in range(lines):
+ if i % 2 == 1:
+ zigzags.append(Path.list_add(zigzag_mirror, (0, -(lines - i + (lines-1)%2) * dy)))
+ else:
+ zigzags.append(Path.list_add(zigzag, (0, (lines - i) * dy)))
+
+ # create edge strokes
+ if not mirror_cells:
+ self.edge_points = [
+ (a * sides , dy * lines), # bottom right
+ (0 , dy * lines), # bottom left
+ (dx * lines , 0), # top left
+ (dx * lines + a * sides, 0)] # top right
+
+ if add_attachment:
+ for i in range(lines):
+ x = dx * (lines - i) + a * (sides + attachment_percentage)
+ self.edge_points.append((x, dy * i))
+ self.edge_points.append((x, dy * i - attachment_height))
+ if i != lines - 1:
+ self.edge_points.append((x-dx-a*attachment_percentage, dy * (i + 1)))
+ pass
+
+ else:
+ self.edge_points = [(a * sides + (lines % 2)*dx, 0)]
+
+ for i in range(lines+1):
+ self.edge_points.append([((lines+i) % 2)*dx, dy*i])
+
+ self.edge_points.append([a * sides + ((lines+i) %2)*dx, lines*dy])
+
+ if add_attachment:
+ for i in range(lines + 1):
+
+ if not i%2 == 0:
+ self.edge_points.append([a*sides + (i%2)*(dx+a*attachment_percentage), dy*(lines - i) - (i%2)*attachment_height])
+ self.edge_points.append([a*sides + (i%2)*(dx+a*attachment_percentage), dy*(lines - i)])
+ if (i != lines):
+ self.edge_points.append([a * sides + (i % 2) * (dx + a * attachment_percentage), dy * (lines - i) + (i % 2) * attachment_height])
+ else:
+ self.edge_points.append([a * sides + (i % 2) * (dx + a * attachment_percentage), dy * (lines - i)])
+ else:
+ for i in range(lines + 1):
+ self.edge_points.append([a*sides + (i%2)*dx, dy*(lines - i)])
+
+ self.path_tree = [grid_h, zigzags, vertices]
+
+
+if __name__ == '__main__':
+
+ e = Kresling()
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Kresling_full.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Kresling_full.py
new file mode 100755
index 0000000..daacf7e
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Kresling_full.py
@@ -0,0 +1,92 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+from math import sin, cos, sqrt, asin, pi, ceil
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+from Kresling import Kresling
+
+
+class Kresling_Full(Kresling):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Kresling.__init__(self) # Must be called in order to parse common options
+
+ self.add_argument('--measure_value', type=self.float, default=10.0)
+ self.add_argument('--measure_type', type=self.str, default=60)
+ self.add_argument('--parameter_type', type=self.str, default=60)
+ self.add_argument('--radial_ratio', type=self.float, default=0.5)
+ self.add_argument('--angle_ratio', type=self.float, default=0.5)
+ self.add_argument('--lambdatheta', type=self.float, default=45)
+
+ def generate_path_tree(self):
+ """ Convert radial to angular ratio, then call regular Kresling constructor
+ """
+ n = self.options.sides
+ theta = pi*(n-2)/(2*n)
+ # define ratio parameter
+ parameter = self.options.parameter_type
+ if parameter == 'radial_ratio':
+ radial_ratio = self.options.radial_ratio
+ max_radial_ratio = sin((pi/4)*(1. - 2./n))
+ if radial_ratio > max_radial_ratio:
+ inkex.errormsg(_("For polygon of {} sides, the maximal radial ratio is = {}".format(n, max_radial_ratio)))
+ radial_ratio = max_radial_ratio
+ self.options.angle_ratio = 1 - 2*n*asin(radial_ratio)/((n-2)*pi)
+
+ elif parameter == 'lambdatheta':
+ lambdatheta = self.options.lambdatheta
+ angle_min = 45. * (1 - 2. / n)
+ angle_max = 2 * angle_min
+ if lambdatheta < angle_min:
+ inkex.errormsg(_(
+ "For polygon of {} sides, phi must be between {} and {} degrees, \nsetting lambda*theta = {}\n".format(
+ n, angle_min, angle_max, angle_min)))
+ lambdatheta = angle_min
+ elif lambdatheta > angle_max:
+ inkex.errormsg(_(
+ "For polygon of {} sides, phi must be between {} and {} degrees, \nsetting lambda*theta = {}\n".format(
+ n, angle_min, angle_max, angle_max)))
+ lambdatheta = angle_max
+ self.options.angle_ratio = lambdatheta * n / (90. * (n - 2.))
+
+
+ # define some length
+ mtype = self.options.measure_type
+ mvalue = self.options.measure_value
+ angle_ratio = self.options.angle_ratio
+ if mtype == 'a':
+ radius = 0.5*mvalue / (sin(pi/n))
+ if mtype == 'b':
+ A = cos(theta*(1-angle_ratio))
+ B = sin(pi/n)
+ C = cos(theta*angle_ratio)
+ radius = 0.5*mvalue / sqrt(A**2 + B**2 - 2*A*B*C)
+ elif mtype == 'l':
+ radius = 0.5*mvalue/cos(theta*(1-angle_ratio))
+ elif mtype == 'radius_external':
+ radius = mvalue
+ elif mtype == 'radius_internal':
+ radius = mvalue/(sin(theta*(1-angle_ratio)))
+ elif mtype == 'diameter_external':
+ radius = 0.5*mvalue
+ elif mtype == 'diameter_internal':
+ radius = 0.5*mvalue/sin(theta*(1-angle_ratio))
+
+ # inkex.errormsg(_("Value = {}, Mode = {}, Radius = {}".format(mvalue, mtype, radius)))
+
+ if self.options.pattern == 'mirrowed':
+ self.options.mirror_cells = True
+ else:
+ self.options.mirror_cells = False
+ self.options.radius = radius
+
+ Kresling.generate_path_tree(self)
+
+
+if __name__ == '__main__':
+ e = Kresling_Full()
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Path.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Path.py
new file mode 100755
index 0000000..18d29a0
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Path.py
@@ -0,0 +1,787 @@
+"""
+Path Class
+
+Defines a path and what it is supposed to be (mountain, valley, edge)
+
+"""
+
+import inkex # Required
+import simplestyle # will be needed here for styles support
+
+# compatibility hack
+try:
+ from lxml import etree
+ inkex.etree = etree
+except:
+ pass
+
+from math import sin, cos, pi, sqrt
+
+# compatibility hack for formatStyle
+def format_style(style):
+ try:
+ return str(inkex.Style(style)) # new
+ except:
+ return simplestyle.formatStyle(style) # old
+# def format_style(style):
+ # return simplestyle.formatStyle(style)
+
+
+
+class Path:
+ """ Class that defines an svg stroke to be drawn in Inkscape
+
+ Attributes
+ ---------
+ points: tuple or list of tuples
+ Points defining stroke lines.
+ style: str
+ Single character defining style of stroke. Default values are:
+ 'm' for mountain creases
+ 'v' for valley creases
+ 'e' for edge borders
+ Extra possible values:
+ 'u' for universal creases
+ 's' for semicreases
+ 'c' for kirigami cuts
+ angle: float
+ From 0 to 180 degrees, converted to an opacity level from 0 to 1. This is how OrigamiSimulator encodes maximum
+ fold angles
+ closed: bool
+ Tells if desired path should contain a last stroke from the last point to the first point, closing the path
+ radius: float
+ If only one point is given, it's assumed to be a circle and radius sets the radius
+
+
+ Methods
+ ---------
+ invert(self)
+ Inverts path
+
+ Overloaded Operators
+ ---------
+ __add__(self, offsets)
+ Adding a tuple to a Path returns a new path with all points having an offset defined by the tuple
+
+ __mul__(self, transform)
+ Define multiplication of a Path to a vector in complex exponential representation
+
+
+ Static Methods
+ ---------
+ draw_paths_recursively(path_tree, group, styles_dict)
+ Draws strokes defined on "path_tree" to "group". Styles dict maps style of path_tree element to the definition
+ of the style. Ex.:
+ if path_tree[i].style = 'm', styles_dict must have an element 'm'.
+
+
+ generate_hgrid(cls, xlims, ylims, nb_of_divisions, style, include_edge=False)
+ Generate list of Path instances, in which each Path is a stroke defining a horizontal grid dividing the space
+ xlims * ylims nb_of_divisions times.
+
+ generate_vgrid(cls, xlims, ylims, nb_of_divisions, style, include_edge=False)
+ Generate list of Path instances, in which each Path is a stroke defining a vertical grid dividing the space
+ xlims * ylims nb_of_divisions times.
+
+ generate_separated_paths(cls, points, styles, closed=False)
+ Generate list of Path instances, in which each Path is the stroke between each two point tuples, in case each
+ stroke must be handled separately
+
+ reflect(cls, path, p1, p2)
+ Reflects each point of path on line defined by two points and return new Path instance with new reflected points
+
+ list_reflect(cls, paths, p1, p2)
+ Generate list of new Path instances, rotation each path by transform
+
+ list_rotate(cls, paths, theta, translation=(0, 0))
+ Generate list of new Path instances, rotation each path by transform
+
+ list_add(cls, paths, offsets)
+ Generate list of new Path instances, adding a different tuple for each list
+
+ list_mul(cls, paths, transf)
+ Generate list of new Path instances, multiplying a different tuple for each list
+
+ list_simplify(cls, paths)
+ Gets complicated path-tree list and converts it into
+ a simple list.
+
+ list_invert(cls, paths)
+ Invert list of paths and points of each path.
+
+ debug_points(cls, paths):
+ Plots points of path tree in drawing order.
+ """
+
+ def __init__(self, points, style, closed=False, invert=False, radius=0.1, separated=False, fold_angle = 180.0):
+ """ Constructor
+
+ Parameters
+ ---------
+ points: list of 2D tuples
+ stroke will connect all points
+ style: str
+ Single character defining style of stroke. For use with the OrigamiPatterns class (probably the only
+ project that will ever use this file) the default values are:
+ 'm' for mountain creases
+ 'v' for valley creases
+ 'e' for edge borders
+ closed: bool
+ if true, last point will be connected to first point at the end
+ invert: bool
+ if true, stroke will start at the last point and go all the way to the first one
+ """
+ if type(points) == list and len(points) != 1:
+ self.type = 'linear'
+ if invert:
+ self.points = points[::-1]
+ else:
+ self.points = points
+
+ elif (type(points) == list and len(points) == 1):
+ self.type = 'circular'
+ self.points = points
+ self.radius = radius
+
+ elif (type(points) == tuple and len(points) == 2):
+ self.type = 'circular'
+ self.points = [points]
+ self.radius = radius
+
+ else:
+ raise TypeError("Points must be tuple of length 2 (for a circle) or a list of tuples of length 2 each")
+
+ self.fold_angle = max(min(fold_angle, 180.), 0.)
+ self.style = style
+ self.closed = closed
+
+ def invert(self):
+ """ Inverts path
+ """
+ self.points = self.points[::-1]
+
+ """
+ Draw path recursively
+ - Static method
+ - Draws strokes defined on "path_tree" to "group"
+ - Inputs:
+ -- path_tree [nested list] of Path instances
+ -- group [inkex.etree.SubElement]
+ -- styles_dict [dict] containing all styles for path_tree
+ """
+ @staticmethod
+ def draw_paths_recursively(path_tree, group, styles_dict):
+ """ Static method, draw list of Path instances recursively
+ """
+ for subpath in path_tree:
+ if type(subpath) == list:
+ if len(subpath) == 1:
+ subgroup = group
+ else:
+ subgroup = inkex.etree.SubElement(group, 'g')
+ Path.draw_paths_recursively(subpath, subgroup, styles_dict)
+ else:
+ # ~ if subpath.style != 'n':
+ if subpath.style != 'n' and styles_dict[subpath.style]['draw']:
+ if subpath.type == 'linear':
+
+ points = subpath.points
+ path = 'M{},{} '.format(*points[0])
+ for i in range(1, len(points)):
+ path = path + 'L{},{} '.format(*points[i])
+ if subpath.closed:
+ path = path + 'L{},{} Z'.format(*points[0])
+
+ attribs = {'style': format_style(styles_dict[subpath.style]),
+ 'd': path,
+ 'opacity': str(subpath.fold_angle/180)}
+ inkex.etree.SubElement(group, inkex.addNS('path', 'svg'), attribs)
+ else:
+ attribs = {'style': format_style(styles_dict[subpath.style]),
+ 'cx': str(subpath.points[0][0]), 'cy': str(subpath.points[0][1]),
+ 'r': str(subpath.radius),
+ 'opacity': str(subpath.fold_angle/180)}
+ inkex.etree.SubElement(group, inkex.addNS('circle', 'svg'), attribs)
+
+ @classmethod
+ def get_average_point(cls, paths):
+ points = cls.get_points(paths)
+ n = len(points)
+ x, y = 0, 0
+ for p in points:
+ x += p[0]
+ y += p[1]
+ return (x/n, y/n)
+
+
+ @classmethod
+ def get_square_points(cls, width, height, center = None, rotation = 1):
+ """ Get points of a square at given center or origin
+
+ Parameters
+ ---------
+ width: float
+ height: float
+ center: list of floats
+ rotation: float
+ rotation in degrees
+
+ Returns
+ ---------
+ points: list of tuples
+ """
+ if center is None:
+ center = [width/2, height/2]
+
+ #TODO: Implement rotation
+ # c = cos(rotation * pi / 180)
+ # s = sin(rotation * pi / 180)
+
+ points = [
+ (center[0] - 0.5*width, center[1] + 0.5*height), # top left
+ (center[0] + 0.5*width, center[1] + 0.5*height), # top right
+ (center[0] + 0.5*width, center[1] - 0.5*height), # bottom right
+ (center[0] - 0.5*width, center[1] - 0.5*height)] # bottom left
+ # points = [
+ # (center[0] + (-0.5*width*c - 0.5*height*s), center[1] + (-0.5*width*s + 0.5*height*c)), # top left
+ # (center[0] + (+0.5*width*c - 0.5*height*s), center[1] + (+0.5*width*s + 0.5*height*c)), # top right
+ # (center[0] + (+0.5*width*c + 0.5*height*s), center[1] - (+0.5*width*s - 0.5*height*c)), # bottom right
+ # (center[0] + (-0.5*width*c + 0.5*height*s), center[1] - (-0.5*width*s - 0.5*height*c))] # bottom left
+ return points
+
+ @classmethod
+ def generate_square(cls, width, height, style ='e', fold_angle=180, center = None, rotation = 0):
+ """ Generate a closed square at given center or origin
+
+ Parameters
+ ---------
+ width: float
+ height: float
+ style: str
+ fold_angle: float
+ center: list of floats
+ rotation: float
+ rotation in degrees
+
+ Returns
+ ---------
+ path: path instance
+ """
+ points = cls.get_square_points(width, height, center, rotation)
+ return Path(points, style, fold_angle=fold_angle, closed=True)
+
+ @classmethod
+ def generate_hgrid(cls, xlims, ylims, nb_of_divisions, style, include_edge=False, fold_angle = 180):
+ """ Generate list of Path instances, in which each Path is a stroke defining
+ a horizontal grid dividing the space xlims * ylims nb_of_divisions times.
+
+ All lines are alternated, to minimize Laser Cutter unnecessary movements
+
+ Parameters
+ ---------
+ xlims: tuple
+ Defines x_min and x_max for space that must be divided.
+ ylims: tuple
+ Defines y_min and y_max for space that must be divided.
+ nb_of_divisions: int
+ Defines how many times it should be divided.
+ style: str
+ Single character defining style of stroke.
+ include_edge: bool
+ Defines if edge should be drawn or not.
+ fold_angle: float
+
+ Returns
+ ---------
+ paths: list of Path instances
+ """
+ rect_len = (ylims[1] - ylims[0])/nb_of_divisions
+ hgrid = []
+ for i in range(1 - include_edge, nb_of_divisions + include_edge):
+ hgrid.append(cls([(xlims[0], ylims[0]+i*rect_len),
+ (xlims[1], ylims[0]+i*rect_len)],
+ style=style, invert=i % 2 == 0, fold_angle = fold_angle))
+ return hgrid
+
+ @classmethod
+ def generate_vgrid(cls, xlims, ylims, nb_of_divisions, style, include_edge=False, fold_angle = 180):
+ """ Generate list of Path instances, in which each Path is a stroke defining
+ a vertical grid dividing the space xlims * ylims nb_of_divisions times.
+
+ All lines are alternated, to minimize Laser Cutter unnecessary movements
+
+ Parameters
+ ---------
+ -> refer to generate_hgrid
+
+ Returns
+ ---------
+ paths: list of Path instances
+ """
+ rect_len = (xlims[1] - xlims[0])/nb_of_divisions
+ vgrid = []
+ for i in range(1 - include_edge, nb_of_divisions + include_edge):
+ vgrid.append(cls([(xlims[0]+i*rect_len, ylims[0]),
+ (xlims[0]+i*rect_len, ylims[1])],
+ style=style, invert=i % 2 == 0, fold_angle = fold_angle))
+ return vgrid
+
+ @classmethod
+ def generate_polygon(cls, sides, radius, style, center=(0, 0), fold_angle = 180):
+ points = []
+ for i in range(sides):
+ points.append((radius * cos((1 + i * 2) * pi / sides),
+ radius * sin((1 + i * 2) * pi / sides)))
+ return Path(points, style, closed=True, fold_angle = fold_angle)
+
+ @classmethod
+ def generate_separated_paths(cls, points, styles, closed=False, fold_angle = 180):
+ """ Generate list of Path instances, in which each Path is the stroke
+ between each two point tuples, in case each stroke must be handled separately.
+
+ Returns
+ ---------
+ paths: list
+ list of Path instances
+ """
+ paths = []
+ if type(styles) == str:
+ styles = [styles] * (len(points) - 1 + int(closed))
+ elif len(styles) != len(points) - 1 + int(closed):
+ raise TypeError("Number of paths and styles don't match")
+ for i in range(len(points) - 1 + int(closed)):
+ j = (i+1)%len(points)
+ paths.append(cls([points[i], points[j]],
+ styles[i], fold_angle = fold_angle))
+ return paths
+
+
+ def __add__(self, offsets):
+ """ " + " operator overload.
+ Adding a tuple to a Path returns a new path with all points having an offset
+ defined by the tuple
+ """
+ if type(offsets) == list:
+ if len(offsets) != 1 or len(offsets) != len(self.points):
+ raise TypeError("Paths can only be added by a tuple of a list of N tuples, "
+ "where N is the same number of points")
+
+ elif type(offsets) != tuple:
+ raise TypeError("Paths can only be added by tuples")
+ else:
+ offsets = [offsets] * len(self.points)
+
+ # if type(self.points) == list:
+ points_new = []
+ for point, offset in zip(self.points, offsets):
+ points_new.append((point[0]+offset[0],
+ point[1]+offset[1]))
+
+ if self.type == 'circular':
+ radius = self.radius
+ else:
+ radius = 0.2
+
+ # if self.type == 'circular' else 0.1
+
+ return Path(points_new, self.style, self.closed, radius=radius, fold_angle=self.fold_angle)
+
+ @classmethod
+ def list_add(cls, paths, offsets):
+ """ Generate list of new Path instances, adding a different tuple for each list
+
+ Parameters
+ ---------
+ paths: Path or list
+ list of N Path instances
+ offsets: tuple or list
+ list of N tuples
+
+ Returns
+ ---------
+ paths_new: list
+ list of N Path instances
+ """
+ if type(paths) == Path and type(offsets) == tuple:
+ paths = [paths]
+ offsets = [offsets]
+ elif type(paths) == list and type(offsets) == tuple:
+ offsets = [offsets] * len(paths)
+ elif type(paths) == Path and type(offsets) == list:
+ paths = [paths] * len(offsets)
+ elif type(paths) == list and type(offsets) == list:
+ if len(paths) == 1:
+ paths = [paths[0]] * len(offsets)
+ elif len(offsets) == 1:
+ offsets = [offsets[0]] * len(paths)
+ elif len(offsets) != len(paths):
+ raise TypeError("List of paths and list of tuples must have same length. {} paths and {} offsets "
+ " where given".format(len(paths), len(offsets)))
+ else:
+ pass
+
+ paths_new = []
+ for path, offset in zip(paths, offsets):
+ if type(path) == Path:
+ paths_new.append(path+offset)
+ elif type(path) == list:
+ paths_new.append(
+ cls.list_add(path, offset)
+ )
+
+ return paths_new
+
+ @classmethod
+ def list_mul(cls, paths, offsets):
+ """ Generate list of new Path instances, multiplying a different tuple for each list
+
+ Parameters
+ ---------
+ paths: Path or list
+ list of N Path instances
+ offsets: tuple or list
+ list of N tuples
+
+ Returns
+ ---------
+ paths_new: list
+ list of N Path instances
+ """
+ if type(paths) == Path and type(offsets) == tuple:
+ paths = [paths]
+ offsets = [offsets]
+ elif type(paths) == list and type(offsets) == tuple:
+ offsets = [offsets] * len(paths)
+ elif type(paths) == Path and type(offsets) == list:
+ paths = [paths] * len(offsets)
+ elif type(paths) == list and type(offsets) == list:
+ if len(paths) == 1:
+ paths = [paths[0]] * len(offsets)
+ elif len(offsets) == 1:
+ offsets = [offsets[0]] * len(paths)
+ elif len(offsets) != len(paths):
+ raise TypeError("List of paths and list of tuples must have same length. {} paths and {} offsets "
+ " where given".format(len(paths), len(offsets)))
+ else:
+ pass
+
+ paths_new = []
+ for path, offset in zip(paths, offsets):
+ paths_new.append(path*offset)
+
+ return paths_new
+
+ def break_path(self, lengths, styles = None):
+ if len(self.points) != 2:
+ raise ValueError('Path breaking only implemented for straight lines with 2 points')
+
+ if styles is None:
+ styles = [self.style]*len(lengths)
+ elif len(styles) != len(lengths):
+ raise ValueError('Different number of lenghts and styles')
+
+ p0 = self.points[0]
+ p1 = self.points[1]
+ d = (p1[0]-p0[0], p1[1]-p0[1])
+ L = sqrt(d[0]**2 + d[1]**2)
+ dx = d[0] / L
+ dy = d[1] / L
+ paths = []
+ start = 0
+ p0_ = p0
+ for l, s in zip(lengths, styles):
+ p1_ = (p0_[0] + dx*l, p0_[1] + dy*l)
+ paths.append(Path([p0_, p1_], style = s))
+ p0_ = p1_
+ return paths
+
+
+
+ def __mul__(self, transform):
+ """ " * " operator overload.
+ Define multiplication of a Path to a vector in complex exponential representation
+
+ Parameters
+ ---------
+ transform: float of tuple of length 2 or 4
+ if float, transform represents magnitude
+ Example: path * 3
+ if tuple length 2, transform[0] represents magnitude and transform[1] represents angle of rotation
+ Example: path * (3, pi)
+ if tuple length 4, transform[2],transform[3] define a different axis of rotation
+ Example: path * (3, pi, 1, 1)
+ """
+ points_new = []
+
+ # "temporary" (probably permanent) compatibility hack
+ try:
+ long_ = long
+ except:
+ long_ = int
+
+ if isinstance(transform, (int, long_, float)):
+ for p in self.points:
+ points_new.append((transform * p[0],
+ transform * p[1]))
+
+ elif isinstance(transform, (list, tuple)):
+ if len(transform) == 2:
+ u = transform[0]*cos(transform[1])
+ v = transform[0]*sin(transform[1])
+ x_, y_ = 0, 0
+ elif len(transform) == 4:
+ u = transform[0]*cos(transform[1])
+ v = transform[0]*sin(transform[1])
+ x_, y_ = transform[2:]
+ else:
+ raise IndexError('Paths can only be multiplied by a number or a tuple/list of length 2 or 4')
+
+ for p in self.points:
+ x, y = p[0]-x_, p[1]-y_
+ points_new.append((x_ + x * u - y * v,
+ y_ + x * v + y * u))
+ else:
+ raise TypeError('Paths can only be multiplied by a number or a tuple/list of length 2 or 4')
+
+ if self.type == 'circular':
+ radius = self.radius
+ else:
+ radius = 0.2
+
+ return Path(points_new, self.style, self.closed, radius=radius, fold_angle=self.fold_angle)
+
+ def shape(self):
+ points = self.points
+ x = [p[0] for p in points]
+ y = [p[1] for p in points]
+ return [min(x), max(x), min(y), max(y)]
+
+ @classmethod
+ def list_create_from_points(cls, points, styles, fold_angles = None):
+ """ Generate list of new Path instances, between each two points
+
+ Parameters
+ ---------
+ points: list of tuples
+ list of points
+ styles: str or list of str
+ styles
+ fold_angles: list of floats
+ list of maximum fold angle values
+
+ Returns
+ ---------
+ paths_new: list
+ list of N Path instances
+ """
+ if fold_angles is None:
+ fold_angles = [180]
+ elif type(fold_angles) != list:
+ fold_angles = [fold_angles]
+
+ return [Path([points[i], points[i+1]],
+ styles[i % len(styles)],
+ fold_angle=fold_angles[i % len(fold_angles)]) for i in range(len(points)-1)]
+
+ @classmethod
+ def list_rotate_symmetry(cls, paths, n, translation=(0,0)):
+ """ Generate list of new Path instances, rotation each path by transform
+
+ Parameters
+ ---------
+ paths: Path or list
+ list of N Path instances
+ n: int
+ number of rotations
+ translation: tuple or list 2
+ axis of rotation
+
+ Returns
+ ---------
+ paths_new: list
+ list of N Path instances
+ """
+
+ theta = 2*pi/n
+ paths_new = []
+ for i in range(n):
+ ith_rotation = cls.list_rotate(paths, theta, translation=translation)
+ if type(paths) == list:
+ paths_new += ith_rotation
+ else:
+ paths_new.append(ith_rotation)
+ return paths_new
+
+
+ @classmethod
+ def list_rotate(cls, paths, theta, translation=(0, 0)):
+ """ Generate list of new Path instances, rotation each path by transform
+
+ Parameters
+ ---------
+ paths: Path or list
+ list of N Path instances
+ theta: float (radians)
+ angle of rotation
+ translation: tuple or list 2
+ axis of rotation
+
+ Returns
+ ---------
+ paths_new: list
+ list of N Path instances
+ """
+ if len(translation) != 2:
+ TypeError("Translation must have length 2")
+
+ if type(paths) != list:
+ paths = [paths]
+
+ paths_new = []
+ for path in paths:
+ if type(path) == Path:
+ paths_new.append(path*(1, theta, translation[0], translation[1]))
+ elif type(path) == list:
+ paths_new.append(cls.list_rotate(path, theta, translation))
+
+ if len(paths_new) == 1:
+ paths_new = paths_new[0]
+ return paths_new
+
+ # TODO:
+ # Apparently it's not working properly, must be debugged and tested
+ @classmethod
+ def reflect(cls, path, p1, p2):
+ """ Reflects each point of path on line defined by two points and return new Path instance with new reflected points
+
+ Parameters
+ ---------
+ path: Path
+ p1: tuple or list of size 2
+ p2: tuple or list of size 2
+
+ Returns
+ ---------
+ path_reflected: Path
+ """
+
+ (x1, y1) = p1
+ (x2, y2) = p2
+
+ if x1 == x2 and y1 == y2:
+ ValueError("Duplicate points don't define a line")
+ elif x1 == x2:
+ t_x = [-1, 0, 2*x1, 1]
+ t_y = [0, 1, 0, 1]
+ else:
+ m = (y2 - y1)/(x2 - x1)
+ t = y1 - m*x1
+ t_x = [1 - m**2, 2*m, -2*m*t, m**2 + 1]
+ t_y = [2*m, m**2 - 1, +2*t, m**2 + 1]
+
+ points_new = []
+ for p in path.points:
+ x_ = (t_x[0]*p[0] + t_x[1]*p[1] + t_x[2]) / t_x[3]
+ y_ = (t_y[0]*p[0] + t_y[1]*p[1] + t_y[2]) / t_y[3]
+ points_new.append((x_, y_))
+
+ return Path(points_new, path.style, path.closed, fold_angle=path.fold_angle)
+
+ # TODO:
+ # Apparently it's not working properly, must be debugged and tested
+ @classmethod
+ def list_reflect(cls, paths, p1, p2):
+ """ Generate list of new Path instances, rotation each path by transform
+
+ Parameters
+ ---------
+ paths: Path or list
+ list of N Path instances
+ p1: tuple or list of size 2
+ p2: tuple or list of size 2
+
+ Returns
+ ---------
+ paths_new: list
+ list of N Path instances
+ """
+
+ if type(paths) == Path:
+ paths = [paths]
+
+ paths_new = []
+ for path in paths:
+ paths_new.append(Path.reflect(path, p1, p2))
+
+ return paths_new
+
+ @classmethod
+ def list_simplify(cls, paths):
+ """ Gets complicated path-tree list and converts it into
+ a simple list.
+
+ Returns
+ ---------
+ paths: list
+ list of Path instances
+ """
+ if type(paths) == Path:
+ return paths
+
+ simple_list = []
+ for i in range(len(paths)):
+ if type(paths[i]) == Path:
+ simple_list.append(paths[i])
+ elif type(paths[i]) == list:
+ simple_list = simple_list + Path.list_simplify(paths[i])
+ return simple_list
+
+ @classmethod
+ def list_invert(cls, paths):
+ """ Invert list of paths and points of each path.
+
+ Returns
+ ---------
+ paths: list
+ list of Path instances
+ """
+
+ if type(paths) == Path:
+ # return Path(paths.points[::-1], paths.style, paths.closed, paths.invert)
+ return Path(paths.points, paths.style, paths.closed, True)
+ elif type(paths) == list:
+ paths_inverted = []
+ # n = len(paths)
+ # for i in range(n):
+ # # paths_inverted.append(Path.list_invert(paths[n-1-i]))
+ # paths_inverted.append(Path.list_invert(paths[i]))
+ for path in paths:
+ # paths_inverted.append(Path.list_invert(paths[n-1-i]))
+ paths_inverted.append(Path.list_invert(path))
+ return paths_inverted[::-1]
+
+ @classmethod
+ def debug_points(cls, paths):
+ """ Plots points of path tree in drawing order.
+
+ """
+ if type(paths) == Path:
+ inkex.debug(paths.points)
+ elif type(paths) == list:
+ for sub_path in paths:
+ Path.debug_points(sub_path)
+
+ @classmethod
+ def get_points(cls, paths):
+ """ Get points of path tree in drawing order.
+
+ """
+ points = []
+ if type(paths) == Path:
+ points = points + paths.points
+ elif type(paths) == list:
+ for sub_path in paths:
+ points = points + Path.get_points(sub_path)
+ return points
+
+
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Pattern.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Pattern.py
new file mode 100755
index 0000000..fafdffc
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Pattern.py
@@ -0,0 +1,378 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Helper functions
+
+"""
+import os
+from abc import abstractmethod
+
+from Path import Path, inkex, simplestyle
+
+class Pattern(inkex.Effect):
+ """ Class that inherits inkex.Effect and further specializes it for different
+ Patterns generation
+
+ Attributes
+ ---------
+ styles_dict: dict
+ defines styles for every possible stroke. Default values are:
+ styles_dict = {'m' : mountain_style,
+ 'v' : valley_style,
+ 'e' : edge_style}
+
+ topgroup: inkex.etree.SubElement
+ Top Inkscape group element
+
+ path_tree: nested list
+ Contains "tree" of Path instances, defining new groups for each
+ sublist
+
+ translate: 2 sized tuple
+ Defines translation to be added when drawing to Inkscape (default: 0,0)
+
+ Methods
+ ---------
+ effect(self)
+ Main function, called when the extension is run.
+
+ create_styles_dict(self)
+ Get stroke style parameters and use them to create the styles dictionary.
+
+ calc_unit_factor(self)
+ Return the scale factor for all dimension conversions
+
+ add_text(self, node, text, position, text_height=12)
+ Create and insert a single line of text into the svg under node.
+
+ get_color_string(self, longColor, verbose=False)
+ Convert the long into a #RRGGBB color value
+
+ Abstract Methods
+ ---------
+ __init__(self)
+ Parse all options
+
+ generate_path_tree(self)
+ Generate nested list of Path
+
+
+ """
+
+ @abstractmethod
+ def generate_path_tree(self):
+ """ Generate nested list of Path instances
+ Abstract method, must be defined in all child classes
+ """
+ pass
+
+ @abstractmethod
+ def __init__(self):
+ """ Parse all common options
+
+ Must be reimplemented in child classes to parse specialized options
+ """
+
+ inkex.Effect.__init__(self) # initialize the super class
+
+ # backwards compatibility
+ try:
+ self.add_argument = self.arg_parser.add_argument
+ self.str = str
+ self.int = int
+ self.float = float
+ self.bool = inkex.Boolean
+ except:
+ self.add_argument = self.OptionParser.add_option
+ self.str = "string"
+ self.int = "int"
+ self.float = "float"
+ self.bool = "inkbool"
+
+ # Two ways to get debug info:
+ # OR just use inkex.debug(string) instead...
+ try:
+ self.tty = open("/dev/tty", 'w')
+ except:
+ self.tty = open(os.devnull, 'w') # '/dev/null' for POSIX, 'nul' for Windows.
+
+ self.add_argument('-u', '--units', type=self.str, default='mm')
+
+ # bypass most style options for OrigamiSimulator
+ self.add_argument('--simulation_mode', type=self.bool, default=False)
+
+ # mountain options
+ self.add_argument('--mountain_stroke_color', type=self.str, default=4278190335) # Red
+ self.add_argument('--mountain_stroke_width', type=self.float, default=0.1)
+ self.add_argument('--mountain_dashes_len', type=self.float, default=1.0)
+ self.add_argument('--mountain_dashes_duty', type=self.float, default=0.5)
+ self.add_argument('--mountain_dashes_bool', type=self.bool, default=True)
+ self.add_argument('--mountain_bool', type=self.bool, default=True)
+ self.add_argument('--mountain_bool_only', type=self.bool, default=False)
+
+ # valley options
+ self.add_argument('--valley_stroke_color', type=self.str, default=65535) # Blue
+ self.add_argument('--valley_stroke_width', type=self.float, default=0.1)
+ self.add_argument('--valley_dashes_len', type=self.float, default=1.0)
+ self.add_argument('--valley_dashes_duty', type=self.float, default=0.25)
+ self.add_argument('--valley_dashes_bool', type=self.bool, default=True)
+ self.add_argument('--valley_bool', type=self.bool, default=True)
+ self.add_argument('--valley_bool_only', type=self.bool, default=False)
+
+ # edge options
+ self.add_argument('--edge_stroke_color', type=self.str, default=255) # Black
+ self.add_argument('--edge_stroke_width', type=self.float, default=0.1)
+ self.add_argument('--edge_dashes_len', type=self.float, default=1.0)
+ self.add_argument('--edge_dashes_duty', type=self.float, default=0.25)
+ self.add_argument('--edge_dashes_bool', type=self.bool, default=False)
+ self.add_argument('--edge_bool', type=self.bool, default=True)
+ self.add_argument('--edge_bool_only', type=self.bool, default=False)
+ self.add_argument('--edge_single_path', type=self.bool, default=True)
+
+ # universal crease options
+ self.add_argument('--universal_stroke_color', type=self.str, default=4278255615) # Magenta
+ self.add_argument('--universal_stroke_width', type=self.float, default=0.1)
+ self.add_argument('--universal_dashes_len', type=self.float, default=1.0)
+ self.add_argument('--universal_dashes_duty', type=self.float, default=0.25)
+ self.add_argument('--universal_dashes_bool', type=self.bool, default=False)
+ self.add_argument('--universal_bool', type=self.bool, default=True)
+ self.add_argument('--universal_bool_only', type=self.bool, default=False)
+
+ # semicrease options
+ self.add_argument('--semicrease_stroke_color', type=self.str, default=4294902015) # Yellow
+ self.add_argument('--semicrease_stroke_width', type=self.float, default=0.1)
+ self.add_argument('--semicrease_dashes_len', type=self.float, default=1.0)
+ self.add_argument('--semicrease_dashes_duty', type=self.float, default=0.25)
+ self.add_argument('--semicrease_dashes_bool', type=self.bool, default=False)
+ self.add_argument('--semicrease_bool', type=self.bool, default=True)
+ self.add_argument('--semicrease_bool_only', type=self.bool, default=False)
+
+ # cut options
+ self.add_argument('--cut_stroke_color', type=self.str, default=16711935) # Green
+ self.add_argument('--cut_stroke_width', type=self.float, default=0.1)
+ self.add_argument('--cut_dashes_len', type=self.float, default=1.0)
+ self.add_argument('--cut_dashes_duty', type=self.float, default=0.25)
+ self.add_argument('--cut_dashes_bool', type=self.bool, default=False)
+ self.add_argument('--cut_bool', type=self.bool, default=True)
+ self.add_argument('--cut_bool_only', type=self.bool, default=False)
+
+ # vertex options
+ self.add_argument('--vertex_stroke_color', type=self.str, default=255) # Black
+ self.add_argument('--vertex_stroke_width', type=self.float, default=0.1)
+ self.add_argument('--vertex_radius', type=self.float, default=0.1)
+ self.add_argument('--vertex_dashes_bool', type=self.bool, default=False)
+ self.add_argument('--vertex_bool', type=self.bool, default=True)
+ self.add_argument('--vertex_bool_only', type=self.bool, default=False)
+
+ # here so we can have tabs - but we do not use it directly - else error
+ self.add_argument('--active-tab', type=self.str, default='title') # use a legitimate default
+
+ self.path_tree = []
+ self.edge_points = []
+ self.vertex_points = []
+ self.translate = (0, 0)
+
+ def effect(self):
+ """ Main function, called when the extension is run.
+ """
+ # bypass most style options if simulation mode is choosen
+ self.check_simulation_mode()
+
+ # check if any selected to print only some of the crease types:
+ bool_only_list = [self.options.mountain_bool_only,
+ self.options.valley_bool_only,
+ self.options.edge_bool_only,
+ self.options.universal_bool_only,
+ self.options.semicrease_bool_only,
+ self.options.cut_bool_only,
+ self.options.vertex_bool_only]
+ if sum(bool_only_list) > 0:
+ self.options.mountain_bool = self.options.mountain_bool and self.options.mountain_bool_only
+ self.options.valley_bool = self.options.valley_bool and self.options.valley_bool_only
+ self.options.edge_bool = self.options.edge_bool and self.options.edge_bool_only
+ self.options.universal_bool = self.options.universal_bool and self.options.universal_bool_only
+ self.options.semicrease_bool = self.options.semicrease_bool and self.options.semicrease_bool_only
+ self.options.cut_bool = self.options.cut_bool and self.options.cut_bool_only
+ self.options.vertex_bool = self.options.vertex_bool and self.options.vertex_bool_only
+
+ # construct dictionary containing styles
+ self.create_styles_dict()
+
+ # get paths for selected origami pattern
+ self.generate_path_tree()
+
+ # ~ accuracy = self.options.accuracy
+ # ~ unit_factor = self.calc_unit_factor()
+ # what page are we on
+ # page_id = self.options.active_tab # sometimes wrong the very first time
+
+ # get vertex points and add them to path tree
+ vertex_radius = self.options.vertex_radius * self.calc_unit_factor()
+ vertices = []
+ self.vertex_points = list(set([i for i in self.vertex_points])) # remove duplicates
+ for vertex_point in self.vertex_points:
+ vertices.append(Path(vertex_point, style='p', radius=vertex_radius))
+ self.path_tree.append(vertices)
+
+
+ # Translate according to translate attribute
+ g_attribs = {inkex.addNS('label', 'inkscape'): '{} Origami pattern'.format(self.options.pattern),
+ # inkex.addNS('transform-center-x','inkscape'): str(-bbox_center[0]),
+ # inkex.addNS('transform-center-y','inkscape'): str(-bbox_center[1]),
+ inkex.addNS('transform-center-x', 'inkscape'): str(0),
+ inkex.addNS('transform-center-y', 'inkscape'): str(0),
+ 'transform': 'translate(%s,%s)' % self.translate}
+
+ # add the group to the document's current layer
+ if type(self.path_tree) == list and len(self.path_tree) != 1:
+ self.topgroup = inkex.etree.SubElement(self.get_layer(), 'g', g_attribs)
+ else:
+ self.topgroup = self.get_layer()
+
+ if len(self.edge_points) == 0:
+ Path.draw_paths_recursively(self.path_tree, self.topgroup, self.styles_dict)
+ elif self.options.edge_single_path:
+ edges = Path(self.edge_points, 'e', closed=True)
+ Path.draw_paths_recursively(self.path_tree + [edges], self.topgroup, self.styles_dict)
+ else:
+ edges = Path.generate_separated_paths(self.edge_points, 'e', closed=True)
+ Path.draw_paths_recursively(self.path_tree + edges, self.topgroup, self.styles_dict)
+
+ # self.draw_paths_recursively(self.path_tree, self.topgroup, self.styles_dict)
+
+ # compatibility hack, "affect()" is replaced by "run()"
+ def draw(self):
+ try:
+ self.run() # new
+ except:
+ self.affect() # old
+ # close(self.tty)
+ self.tty.close()
+
+ # compatibility hack
+ def get_layer(self):
+ try:
+ return self.svg.get_current_layer() # new
+ except:
+ return self.current_layer # old
+
+ def check_simulation_mode(self):
+ if not self.options.simulation_mode:
+ pass
+ else:
+ self.options.mountain_stroke_color = 4278190335
+ self.options.mountain_dashes_len = 0
+ self.options.mountain_dashes_bool = False
+ self.options.mountain_bool_only = False
+ self.options.mountain_bool = True
+
+ self.options.valley_stroke_color = 65535
+ self.options.valley_dashes_len = 0
+ self.options.valley_dashes_bool = False
+ self.options.valley_bool_only = False
+ self.options.valley_bool = True
+
+ self.options.edge_stroke_color = 255
+ self.options.edge_dashes_len = 0
+ self.options.edge_dashes_bool = False
+ self.options.edge_bool_only = False
+ self.options.edge_bool = True
+
+ self.options.universal_stroke_color = 4278255615
+ self.options.universal_dashes_len = 0
+ self.options.universal_dashes_bool = False
+ self.options.universal_bool_only = False
+ self.options.universal_bool = True
+
+ self.options.cut_stroke_color = 16711935
+ self.options.cut_dashes_len = 0
+ self.options.cut_dashes_bool = False
+ self.options.cut_bool_only = False
+ self.options.cut_bool = True
+
+ self.options.vertex_bool = False
+
+
+ def create_styles_dict(self):
+ """ Get stroke style parameters and use them to create the styles dictionary, used for the Path generation
+ """
+ unit_factor = self.calc_unit_factor()
+
+ def create_style(type):
+ style = {'draw': getattr(self.options,type+"_bool"),
+ 'stroke': self.get_color_string(getattr(self.options,type+"_stroke_color")),
+ 'fill': 'none',
+ 'stroke-width': getattr(self.options,type+"_stroke_width") * unit_factor}
+ if getattr(self.options,type+"_dashes_bool"):
+ dash_gap_len = getattr(self.options,type+"_dashes_len")
+ duty = getattr(self.options,type+"_dashes_duty")
+ dash = (dash_gap_len * unit_factor) * duty
+ gap = (dash_gap_len * unit_factor) * (1 - duty)
+ style['stroke-dasharray'] = "{} {}".format(dash, gap)
+ return style
+
+ self.styles_dict = {'m': create_style("mountain"),
+ 'v': create_style("valley"),
+ 'u': create_style("universal"),
+ 's': create_style("semicrease"),
+ 'c': create_style("cut"),
+ 'e': create_style("edge"),
+ 'p': create_style("vertex")}
+
+ def get_color_string(self, longColor, verbose=False):
+ """ Convert the long into a #RRGGBB color value
+ - verbose=true pops up value for us in defaults
+ conversion back is A + B*256^1 + G*256^2 + R*256^3
+ """
+ # compatibility hack, no "long" in Python 3
+ try:
+ longColor = long(longColor)
+ if longColor < 0: longColor = long(longColor) & 0xFFFFFFFF
+ hexColor = hex(longColor)[2:-3]
+ except:
+ longColor = int(longColor)
+ hexColor = hex(longColor)[2:-2]
+ inkex.debug = inkex.utils.debug
+
+ hexColor = '#' + hexColor.rjust(6, '0').upper()
+ if verbose: inkex.debug("longColor = {}, hex = {}".format(longColor,hexColor))
+
+ return hexColor
+
+ def add_text(self, node, text, position, text_height=12):
+ """ Create and insert a single line of text into the svg under node.
+ """
+ line_style = {'font-size': '%dpx' % text_height, 'font-style':'normal', 'font-weight': 'normal',
+ 'fill': '#F6921E', 'font-family': 'Bitstream Vera Sans,sans-serif',
+ 'text-anchor': 'middle', 'text-align': 'center'}
+ line_attribs = {inkex.addNS('label','inkscape'): 'Annotation',
+ 'style': simplestyle.formatStyle(line_style),
+ 'x': str(position[0]),
+ 'y': str((position[1] + text_height) * 1.2)
+ }
+ line = inkex.etree.SubElement(node, inkex.addNS('text','svg'), line_attribs)
+ line.text = text
+
+
+ def calc_unit_factor(self):
+ """ Return the scale factor for all dimension conversions.
+
+ - The document units are always irrelevant as
+ everything in inkscape is expected to be in 90dpi pixel units
+ """
+ # namedView = self.document.getroot().find(inkex.addNS('namedview', 'sodipodi'))
+ # doc_units = self.getUnittouu(str(1.0) + namedView.get(inkex.addNS('document-units', 'inkscape')))
+ # backwards compatibility
+ try:
+ return self.svg.unittouu(str(1.0) + self.options.units)
+ except:
+ try:
+ return inkex.unittouu(str(1.0) + self.options.units)
+ except AttributeError:
+ return self.unittouu(str(1.0) + self.options.units)
+
+
+
+
+
+
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Pleat_Circular.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Pleat_Circular.py
new file mode 100755
index 0000000..d129dad
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Pleat_Circular.py
@@ -0,0 +1,101 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+
+from math import pi, sin, cos
+
+from Path import Path
+from Pattern import Pattern
+
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+
+
+class PleatCircular(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--pattern', type=self.str, default='pleat_circular')
+ self.add_argument('--radius', type=self.float, default=55.0)
+ self.add_argument('--ratio', type=self.float, default=0.4)
+ self.add_argument('--rings', type=self.int, default=15)
+ self.add_argument('--sides', type=self.int, default=20)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve saved parameters
+ unit_factor = self.calc_unit_factor()
+ R = self.options.radius * unit_factor
+ ratio = self.options.ratio
+ r = R * ratio
+ rings = self.options.rings
+ dr = (1.-ratio)*R/rings
+ self.translate = (R, R)
+
+ if not self.options.simulation_mode:
+ inner_circles = []
+ for i in range(1, rings):
+ inner_circles.append(Path((0, 0), radius=r + i*dr, style='m' if i % 2 else 'v'))
+
+ edges = [Path((0, 0), radius=R, style='e'),
+ Path((0, 0), radius=r, style='e')]
+
+ self.path_tree = [inner_circles, edges]
+
+ # append semicreases for simulation
+ else:
+ sides = self.options.sides
+ dtheta = pi / sides
+ # create diagonals
+ diagonals = []
+ for i in range(sides):
+ p1 = (0, 0)
+ p2 = (R * cos((1 + i * 2) * dtheta), R * sin((1 + i * 2) * dtheta))
+ diagonals.append(Path([p1, p2], 'u'))
+
+ s = sin(dtheta)
+ c = cos(dtheta)
+
+ # Edge
+ paths = [Path([(c * R, -s * R), (R, 0), (c * R, s * R)], style='e'),
+ Path([(c * r, -s * r), (r, 0), (c * r, s * r)], style='e')]
+
+ # MV circles
+ for i in range(1, rings):
+ r_i = r + i * dr
+ paths.append(Path([(c * r_i, -s * r_i), (r_i, 0), (c * r_i, s * r_i)],
+ style='m' if i % 2 else 'v'))
+
+ # Semicreases
+ top = []
+ bottom = []
+ for i in range(rings + 1):
+ r_i = r + i*dr
+ top.append((r_i*(1 + (i % 2)*(c-1)), -(i % 2)*s*r_i))
+ bottom.append((r_i*(1 + (i % 2)*(c-1)), (i % 2)*s*r_i))
+ paths = paths + [Path([(r, 0), (R, 0)], 's'), # straight line 1
+ Path([(r*c, r*s), (R*c, R*s)], 's', invert=True), # straight line 2
+ Path(top, 's'), # top half of semicrease pattern
+ Path(bottom, 's')] # bottom half of semicrease pattern
+
+ all_paths = [paths]
+ for i in range(1, sides):
+ all_paths.append(Path.list_rotate(all_paths[0], i*2*dtheta))
+
+ self.path_tree = all_paths
+
+
+
+
+# Main function, creates an instance of the Class and calls inkex.affect() to draw the origami on inkscape
+if __name__ == '__main__':
+ e = PleatCircular() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/SupportRing.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/SupportRing.py
new file mode 100755
index 0000000..ccbbd31
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/SupportRing.py
@@ -0,0 +1,129 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+from math import pi, sin, cos
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+
+
+class SupportRing(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--sides', type=self.int, default=3)
+ self.add_argument('--radius_external', type=self.float, default=10.0)
+ self.add_argument('--inverted', type=self.bool, default=False)
+ self.add_argument('--single_stroke', type=self.bool, default=True)
+ self.add_argument('--radius_ratio', type=self.float, default=0.5)
+ self.add_argument('--radius_type', type=self.str, default='polygonal')
+ self.add_argument('--radius_draw', type=self.bool, default=True)
+ self.add_argument('--connector_length', type=self.float, default=3.0)
+ self.add_argument('--connector_thickness', type=self.float, default=3.0)
+ self.add_argument('--head_length', type=self.float, default=1.0)
+ self.add_argument('--head_thickness', type=self.float, default=1.0)
+ self.add_argument('--pattern', type=self.str, default='support ring')
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+
+ # retrieve saved parameters, and apply unit factor where needed
+
+ inverted = self.options.inverted
+ sign = -1 if inverted else 1
+ single_stroke = self.options.single_stroke
+ radius_external = self.options.radius_external * unit_factor
+ radius_type = self.options.radius_type
+ radius_ratio = self.options.radius_ratio
+ radius_internal = radius_external / radius_ratio if inverted else radius_external * radius_ratio
+ # dradius = abs(radius_external-radius_internal)
+ sides = self.options.sides
+ connector_length = self.options.connector_length * unit_factor
+ connector_thickness = self.options.connector_thickness * unit_factor
+ head_length = self.options.head_length * unit_factor
+ head_thickness = self.options.head_thickness * unit_factor
+
+ angle = pi / sides
+ length_external = 2 * radius_external * sin(angle)
+ length_internal = length_external / radius_ratio if inverted else length_external * radius_ratio
+
+ external_points = [(-length_external/2, 0),
+ (-connector_thickness / 2, 0),
+ (-connector_thickness / 2, -connector_length*sign),
+ (-connector_thickness / 2 - head_thickness / 2, -connector_length*sign),
+ (-connector_thickness / 2, -(connector_length + head_length)*sign),
+ (0, -(connector_length + head_length)*sign),
+ (+connector_thickness / 2, -(connector_length + head_length)*sign),
+ (+connector_thickness / 2 + head_thickness / 2, -connector_length*sign),
+ (+connector_thickness / 2, -connector_length*sign),
+ (+connector_thickness / 2, 0),
+ (length_external/2, 0)]
+
+ internal_points = [(0, 0), (length_internal, 0)]
+
+ external_lines_0 = Path(external_points, 'm') + (length_external / 2, 0)
+ external_lines = [external_lines_0]
+
+ for i in range(sides-1):
+ x, y = external_lines[-1].points[-1]
+ external_lines.append(external_lines_0*(1, 2*(i+1)*angle) + (x, y))
+
+ if single_stroke:
+ external_lines = Path(Path.get_points(external_lines), 'm')
+
+ self.path_tree = [external_lines]
+
+ if self.options.radius_draw == True:
+
+ # center point of main strokes
+ outer_average = Path.get_average_point(external_lines)
+
+ if radius_type == 'polygonal':
+ internal_lines_0 = Path(internal_points, 'm')
+ internal_lines = [internal_lines_0]
+ for i in range(sides - 1):
+ x, y = internal_lines[-1].points[-1]
+ internal_lines.append(internal_lines_0*(1, 2*(i+1)*angle) + (x, y))
+
+ # move to center
+ inner_average = Path.get_average_point(internal_lines)
+ delta = ((outer_average[0] - inner_average[0]),
+ (outer_average[1] - inner_average[1]))
+
+ if single_stroke:
+ internal_lines = Path(Path.get_points(internal_lines), 'm')
+
+ internal_lines = Path.list_add(internal_lines, delta)
+ elif radius_type == 'circular':
+
+ internal_lines = Path(outer_average, radius=radius_internal, style='m')
+
+ self.path_tree.append(internal_lines)
+
+
+
+
+
+
+
+
+# Main function, creates an instance of the Class and calls self.draw() to draw the origami on inkscape
+# self.draw() is either a call to inkex.affect() or to svg.run(), depending on python version
+if __name__ == '__main__':
+ e = SupportRing() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Template.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Template.py
new file mode 100755
index 0000000..288bc89
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Template.py
@@ -0,0 +1,113 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import numpy as np
+from math import pi
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+
+# Select name of class, inherits from Pattern
+# TODO:
+# 1) Implement __init__ method to get all custom options and then call Pattern's __init__
+# 2) Implement generate_path_tree to define all of the desired strokes
+
+
+class Template(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ # save all custom parameters defined on .inx file
+ self.add_argument('--pattern', type=self.str, default='template1')
+ self.add_argument('--length', type=self.float, default=10.0)
+ self.add_argument('--angle', type=self.int, default=0)
+ self.add_argument('--fold_angle_valley', type=self.int, default=180)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for your origami pattern
+ """
+ # retrieve conversion factor for selected unit
+ unit_factor = self.calc_unit_factor()
+
+ # retrieve saved parameters, and apply unit factor where needed
+ length = self.options.length * unit_factor
+ vertex_radius = self.options.vertex_radius * unit_factor
+ pattern = self.options.pattern
+ angle = self.options.angle * pi / 180
+ fold_angle_valley = self.options.fold_angle_valley
+
+ # create all Path instances defining strokes
+ # first define its points as a list of tuples...
+ left_right_stroke_points = [(length / 2, 0),
+ (length / 2, length)]
+ up_down_stroke_points = [(0, length / 2),
+ (length, length / 2)]
+
+ # doing the same for diagonals
+ diagonal_1_stroke_points = [(0, 0),
+ (length, length)]
+ diagonal_2_stroke_points = [(0, length),
+ (length, 0)]
+
+ # ... and then create the Path instances, defining its type ('m' for mountain, etc...)
+ if pattern == 'template1':
+ up_down = [Path(left_right_stroke_points, 'm', fold_angle = 180.),
+ Path(up_down_stroke_points, 'm', fold_angle = 180.)]
+
+ diagonals = [Path(diagonal_1_stroke_points, 'v', fold_angle = fold_angle_valley),
+ Path(diagonal_2_stroke_points, 'v', fold_angle = fold_angle_valley)]
+
+ else:
+ up_down = [Path(left_right_stroke_points, 'v', fold_angle = fold_angle_valley),
+ Path(up_down_stroke_points, 'v', fold_angle = fold_angle_valley)]
+
+ diagonals = [Path(diagonal_1_stroke_points, 'm', fold_angle = 180.),
+ Path(diagonal_2_stroke_points, 'm', fold_angle = 180. )]
+
+ vertices = []
+ for i in range(3):
+ for j in range(3):
+ vertices.append(Path(((i/2.) * length, (j/2.) * length), style='p', radius=vertex_radius))
+
+ # multiplication is implemented as a rotation, and list_rotate implements rotation for list of Path instances
+ vertices = Path.list_rotate(vertices, angle, (1 * length, 1 * length))
+ up_down = Path.list_rotate(up_down, angle, (1 * length, 1 * length))
+ diagonals = Path.list_rotate(diagonals, angle, (1 * length, 1 * length))
+
+ # if Path constructor is called with more than two points, a single stroke connecting all of then will be
+ # created. Using method generate_separated_paths, you can instead return a list of separated strokes
+ # linking each two points
+
+ # create a list for edge strokes
+ # create path from points to be able to use the already built rotate method
+ edges = Path.generate_square(length, length, 'e', rotation = angle)
+ edges = Path.list_rotate(edges, angle, (1 * length, 1 * length))
+
+ # IMPORTANT: the attribute "path_tree" must be created at the end, saving all strokes
+ self.path_tree = [up_down, diagonals, vertices]
+
+ # IMPORTANT: at the end, save edge points as "self.edge_points", to simplify selection of single or multiple
+ # strokes for the edge
+ self.edge_points = edges.points
+
+ # if you decide not to declare "self.edge_points", then the edge must be explicitly created in the path_tree:
+ # self.path_tree = [mountains, valleys, vertices, edges]
+
+ # FINAL REMARKS:
+ # division is implemented as a reflection, and list_reflect implements it for a list of Path instances
+ # here's a commented example:
+ # line_reflect = (0 * length, 2 * length, 1 * length, 1 * length)
+ # mountains = Path.list_reflect(mountains, line_reflect)
+ # valleys = Path.list_reflect(valleys, line_reflect)
+ # edges = Path.list_reflect(edges, line_reflect)
+
+# Main function, creates an instance of the Class and calls self.draw() to draw the origami on inkscape
+# self.draw() is either a call to inkex.affect() or to svg.run(), depending on python version
+if __name__ == '__main__':
+ e = Template() # remember to put the name of your Class here!
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Waterbomb.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Waterbomb.py
new file mode 100755
index 0000000..b8db86b
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/Waterbomb.py
@@ -0,0 +1,112 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+import math
+import numpy as np
+
+import inkex
+
+from Path import Path
+from Pattern import Pattern
+
+# TODO:
+# Add fractional column number option
+
+
+class Waterbomb(Pattern):
+
+ def __init__(self):
+ """ Constructor
+ """
+ Pattern.__init__(self) # Must be called in order to parse common options
+
+ self.add_argument('--pattern', type=self.str, default='waterbomb')
+ self.add_argument('--pattern_first_line', type=self.str, default='waterbomb')
+ self.add_argument('--pattern_last_line', type=self.str, default='waterbomb')
+ self.add_argument('--lines', type=self.int, default=8)
+ self.add_argument('--columns', type=self.int, default=16)
+ self.add_argument('--length', type=self.float, default=10.0)
+ self.add_argument('--phase_shift', type=self.bool, default=True)
+
+ def generate_path_tree(self):
+ """ Specialized path generation for Waterbomb tesselation pattern
+ """
+ unit_factor = self.calc_unit_factor()
+ length = self.options.length * unit_factor
+ vertex_radius = self.options.vertex_radius * unit_factor
+ cols = self.options.columns
+ lines = self.options.lines
+ phase_shift = self.options.phase_shift
+ pattern_first_line = self.options.pattern_first_line
+ pattern_last_line = self.options.pattern_last_line
+
+ # create vertices
+ vertex_line_types = [[Path(((i / 2.) * length, 0), style='p', radius=vertex_radius) for i in range(2*cols + 1)],
+ [Path((i * length, 0), style='p', radius=vertex_radius) for i in range(cols + 1)],
+ [Path(((i + 0.5) * length, 0), style='p', radius=vertex_radius) for i in range(cols)]]
+
+ vertices = []
+ for i in range(2*lines + 1):
+ if i % 2 == 0 or (pattern_first_line == 'magic_ball' and i == 1) or (pattern_last_line == 'magic_ball' and i == 2*lines - 1):
+ type = 0
+ elif(int(i/2 + phase_shift)) % 2 == 0:
+ type = 1
+ else:
+ type = 2
+ vertices = vertices + Path.list_add(vertex_line_types[type], (0, 0.5*i*length))
+
+ # create a list for the horizontal creases and another for the vertical creases
+ # alternate strokes to minimize laser cutter path
+ corr_fist_line = length/2 if pattern_first_line == 'magic_ball' else 0
+ corr_last_line = length/2 if pattern_last_line == 'magic_ball' else 0
+ grid = [Path.generate_hgrid([0, length*cols], [0, length*lines], lines, 'm'),
+ Path.generate_vgrid([0, length*cols], [corr_fist_line, length*lines-corr_last_line], 2*cols, 'm')]
+
+ vgrid_a = Path.generate_vgrid([0, length * cols], [0, length / 2], 2 * cols, 'v')
+ vgrid_b = Path.list_add(vgrid_a, (0, (lines - 0.5) * length))
+ if pattern_first_line == 'magic_ball' and pattern_last_line == 'magic_ball':
+ grid[1] = [[vgrid_a[i], grid[1][i], vgrid_b[i]] if i % 2 == 0 else
+ [vgrid_b[i], grid[1][i], vgrid_a[i]] for i in range(len(grid[1]))]
+ elif pattern_first_line == 'magic_ball':
+ grid[1] = [[vgrid_a[i], grid[1][i]] if i % 2 == 0 else
+ [grid[1][i], vgrid_a[i]] for i in range(len(grid[1]))]
+ elif pattern_last_line == 'magic_ball':
+ grid[1] = [[grid[1][i], vgrid_b[i]] if i % 2 == 0 else
+ [vgrid_b[i], grid[1][i]] for i in range(len(grid[1]))]
+
+ # create generic valley Path lines, one pointing up and other pointing down
+ valley_types = [Path([(i * length / 2, (1 - i % 2) * length / 2) for i in range(2 * cols + 1)], 'v'),
+ Path([( i*length/2, (i % 2)*length/2) for i in range(2 * cols + 1)], 'v')]
+
+ # define which lines must be of which type, according to parity and options
+ senses = np.array([bool((i % 2+i)/2 % 2) for i in range(2*lines)])
+ senses = 1*senses # converts bool array to 0's and 1's
+ if phase_shift:
+ senses = np.invert(senses)
+ if pattern_first_line == "magic_ball":
+ senses[0] = ~senses[0]
+ if pattern_last_line == "magic_ball":
+ senses[-1] = ~senses[-1]
+ valleys = [valley_types[senses[i]] + (0, i * length / 2) for i in range(2*lines)]
+
+ # convert first and last lines to mountains if magic_ball
+ if pattern_first_line == "magic_ball":
+ valleys[0].style = 'm'
+ if pattern_last_line == "magic_ball":
+ valleys[-1].style = 'm'
+
+ # invert every two lines to minimize laser cutter movements
+ for i in range(1, 2*lines, 2):
+ valleys[i].invert()
+
+ self.edge_points = [(0*length*cols, 0*length*lines), # top left
+ (1*length*cols, 0*length*lines), # top right
+ (1*length*cols, 1*length*lines), # bottom right
+ (0*length*cols, 1*length*lines)] # bottom left
+
+ self.path_tree = [grid, valleys, vertices]
+
+
+if __name__ == '__main__':
+
+ e = Waterbomb()
+ e.draw()
diff --git a/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/__init__.py b/extensions/fablabchemnitz/origami_patterns/OrigamiPatterns/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/Belt.scad b/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/Belt.scad
new file mode 100755
index 0000000..a103439
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/Belt.scad
@@ -0,0 +1,31 @@
+module draw_cone(R,n,h,thickness){
+ difference(){
+ cylinder(h=h, r1=R+thickness, r2=R+thickness, center=true, $fn=n);
+ cylinder(h=h, r1=R, r2=R, center=true, $fn=n);
+ cylinder(h=h, r1=R, r2=R, center=true, $fn=n);
+ }
+}
+
+module draw_cut_boxes(R, n, thickness, slot_height, slot_width){
+ union(){
+ for (i=[0: n/2]){
+ rotate(a=i*360/n, v=[0,0,1])
+ cube([2*(R+thickness),slot_height,slot_width], center=true);
+ }
+ }
+}
+
+module draw_belt(R, n, h, thickness, slot_height, slot_width){
+ difference(){
+ rotate(a=180/n, v=[0,0,1]) draw_cone(R, n, h, thickness);
+ draw_cut_boxes(R, n, thickness, slot_height, slot_width);
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/Belt_main.scad b/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/Belt_main.scad
new file mode 100755
index 0000000..55eda7b
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/Belt_main.scad
@@ -0,0 +1,13 @@
+include
+
+// Input parameters
+R = 35;
+n = 8;
+height = 7; // height of support
+thickness = 1; // thickness of belt
+
+// square cuts to be pierced by support ring
+slot_height=3; // must be smaller than height
+slot_width=3;
+
+draw_belt(R, n, height, thickness, slot_height, slot_width);
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/README.md b/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/README.md
new file mode 100755
index 0000000..04b6568
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/Support_Ring_Belt/README.md
@@ -0,0 +1,5 @@
+# Origami_Patterns_Support_Belt
+
+Designed to be used with the [Origami Patterns Inkscape extension](https://github.com/evbernardes/Origami_Patterns).
+
+3D Print this with TPU (or any other flexible material) and use together with the support rings.
diff --git a/extensions/fablabchemnitz/origami_patterns/logo.svg b/extensions/fablabchemnitz/origami_patterns/logo.svg
new file mode 100755
index 0000000..c55ee6a
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/logo.svg
@@ -0,0 +1,384 @@
+
+
diff --git a/extensions/fablabchemnitz/origami_patterns/meta.json b/extensions/fablabchemnitz/origami_patterns/meta.json
new file mode 100644
index 0000000..0d68585
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/meta.json
@@ -0,0 +1,21 @@
+[
+ {
+ "name": "Origami Pattern - ",
+ "id": "fablabchemnitz.de.origami_patterns.",
+ "path": "origami_patterns",
+ "dependent_extensions": null,
+ "original_name": "",
+ "original_id": "org.inkscape.Origami_patterns.",
+ "license": "MIT License",
+ "license_url": "https://github.com/evbernardes/Origami_Patterns/blob/master/LICENSE",
+ "comment": "",
+ "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/origami_patterns",
+ "fork_url": "https://github.com/evbernardes/Origami_Patterns",
+ "documentation_url": "https://stadtfabrikanten.org/display/IFM/Origami+Patterns",
+ "inkscape_gallery_url": null,
+ "main_authors": [
+ "github.com/evbernardes",
+ "github.com/vmario89"
+ ]
+ }
+]
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_boxes_masu.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_boxes_masu.inx
new file mode 100644
index 0000000..664e149
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_boxes_masu.inx
@@ -0,0 +1,69 @@
+
+
+ Masu box (width and height)
+ org.inkscape.Origami_patterns.boxes_masu
+
+
+ 10.0
+ 10.0
+
+ 0.0
+ false
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_boxes_masu_traditional.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_boxes_masu_traditional.inx
new file mode 100644
index 0000000..28db195
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_boxes_masu_traditional.inx
@@ -0,0 +1,65 @@
+
+
+ Masu box (traditional)
+ org.inkscape.Origami_patterns.boxes_masu_traditional
+
+
+ 10.0
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_bendy.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_bendy.inx
new file mode 100755
index 0000000..73b7231
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_bendy.inx
@@ -0,0 +1,111 @@
+
+
+ Bendy Straw
+ org.inkscape.Origami_patterns.cylindrical_bendy
+
+
+
+
+
+
+
+
+
+ 6
+ 3
+
+ 25.0
+ 0.75
+
+
+
+
+
+ 45
+ 35
+ 1
+ 2
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+ 5.0
+ false
+ false
+ 3.0
+ 3.0
+
+ 3.0
+ false
+ 3.0
+ 3.0
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 16711935
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_kresling.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_kresling.inx
new file mode 100644
index 0000000..26a983f
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_kresling.inx
@@ -0,0 +1,122 @@
+
+
+ Kresling tower
+ org.inkscape.Origami_patterns.cylindrical_kresling
+
+
+
+
+
+
+
+ 3
+ 6
+
+ 10.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+ 0.5
+ 0.5
+ 60.0
+
+
+ false
+
+ false
+
+
+
+
+
+ 5.0
+ 3.0
+ 3.0
+
+ false
+
+
+
+
+
+ 3.0
+ 3.0
+ 3.0
+
+
+ true
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 16711935
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_template.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_template.inx
new file mode 100755
index 0000000..31c06e8
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_cylindrical_template.inx
@@ -0,0 +1,95 @@
+
+
+ * Cylindrical template effect
+ org.inkscape.Origami_patterns.cylindrical_template
+
+
+
+
+ 10.0
+ 6
+ 3
+
+ 10.0
+ 0
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+ false
+
+
+
+
+
+ 5.0
+ 3.0
+ 3.0
+
+ false
+
+
+
+
+
+ 3.0
+ 3.0
+ 3.0
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_misc_support_ring.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_misc_support_ring.inx
new file mode 100755
index 0000000..854d460
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_misc_support_ring.inx
@@ -0,0 +1,53 @@
+
+
+ * Support ring
+ org.inkscape.Origami_patterns.support_ring
+
+
+ 6
+ 10.0
+ false
+ true
+
+ 3.0
+ 3.0
+ 1.0
+ 1.0
+
+ true
+ 0.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ false
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_old_bendy.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_old_bendy.inx
new file mode 100755
index 0000000..d6ace9e
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_old_bendy.inx
@@ -0,0 +1,107 @@
+
+
+ Bendy Straw (old, faster cutting)
+ org.inkscape.Origami_patterns.old_bendy
+
+
+
+
+
+
+
+
+
+ 6
+ 3
+
+ 25.0
+ 0.75
+
+
+
+
+
+ 45
+ 35
+ 1
+ 2
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+
+ 5.0
+ false
+ false
+ 3.0
+ 3.0
+
+ 3.0
+ false
+ 3.0
+ 3.0
+
+
+ true
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 16711935
+
+
+ true
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ true
+ true
+ true
+ true
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_old_kresling.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_old_kresling.inx
new file mode 100755
index 0000000..9ecaa07
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_old_kresling.inx
@@ -0,0 +1,95 @@
+
+
+ Kresling tower (old)
+ org.inkscape.Origami_patterns.old_kresling
+
+
+
+
+
+
+
+ 3
+ 6
+
+ 10.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0.5
+ 0.5
+ 60.0
+ true
+
+
+ false
+ 100
+
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_pleat_circular.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_pleat_circular.inx
new file mode 100755
index 0000000..196882f
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_pleat_circular.inx
@@ -0,0 +1,78 @@
+
+
+ Circular
+ org.inkscape.Origami_patterns.pleat_circular
+
+
+ 55.0
+
+
+
+
+
+
+
+ 0.4
+ 15
+
+ true
+
+ 20
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+ true
+ false
+ false
+ 1
+ 0.25
+ 0.1
+ 4294902015
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_pleat_hypar.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_pleat_hypar.inx
new file mode 100755
index 0000000..250d605
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_pleat_hypar.inx
@@ -0,0 +1,82 @@
+
+
+ N-sided Hypar
+ org.inkscape.Origami_patterns.pleat_hypar
+
+
+
+
+
+
+
+ 100.0
+
+
+
+
+
+
+
+ 4
+ 7
+ false
+
+ true
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ false
+ 1
+ 0.25
+ 0.1
+ 4278255615
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_template.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_template.inx
new file mode 100755
index 0000000..1c2b0c2
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_template.inx
@@ -0,0 +1,72 @@
+
+
+ Template effect
+ org.inkscape.Origami_patterns.template
+
+
+
+
+
+
+ 10.0
+
+
+
+
+
+
+
+ 0
+ 180
+
+ true
+
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/extensions/fablabchemnitz/origami_patterns/origami_patterns_waterbomb.inx b/extensions/fablabchemnitz/origami_patterns/origami_patterns_waterbomb.inx
new file mode 100755
index 0000000..46f5337
--- /dev/null
+++ b/extensions/fablabchemnitz/origami_patterns/origami_patterns_waterbomb.inx
@@ -0,0 +1,77 @@
+
+
+ Waterbomb
+ org.inkscape.Origami_patterns.magic_ball
+
+
+
+
+
+
+
+
+
+
+ false
+
+ 8
+ 16
+
+ 10.0
+
+
+
+
+
+
+
+
+ true
+
+
+ true
+ false
+ true
+ 1
+ 0.5
+ 0.1
+ 4278190335
+
+
+ true
+ false
+ true
+ 1
+ 0.25
+ 0.1
+ 65535
+
+
+ true
+ false
+ true
+ false
+ 1
+ 0.25
+ 0.1
+ 255
+
+
+ true
+ false
+ 0.1
+ 0.1
+ 255
+
+
+
+ all
+
+
+
+
+
+
\ No newline at end of file