Added Origami Patterns

This commit is contained in:
Mario Voigt 2020-07-31 16:05:02 +02:00
parent 3c4ca85a06
commit c6546af043
14 changed files with 1775 additions and 0 deletions

112
OrigamiPatterns/Hypar.py Normal file
View File

@ -0,0 +1,112 @@
#! /usr/bin/env python3
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):
Pattern.__init__(self) # Must be called in order to parse common options
self.add_argument("-p", "--pattern", default="template1", help="Origami pattern")
self.add_argument("--radius", type=float, default=10.0, help="Radius of tower (mm)")
self.add_argument("--sides", type=int, default=4, help="Number of polygon sides")
self.add_argument("--rings", type=int, default=7, help="Number of rings")
self.add_argument("--simplify_center", type=inkex.Boolean, default=0, help="Simplify center")
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__':
Hypar().run()

154
OrigamiPatterns/Kresling.py Normal file
View File

@ -0,0 +1,154 @@
#! /usr/bin/env python3
from math import pi, sin, cos, tan, acos, sqrt
import inkex
from Path import Path
from Pattern import Pattern
class Kresling(Pattern):
def __init__(self):
Pattern.__init__(self) # Must be called in order to parse common options
self.add_argument("-p", "--pattern", default="kresling", help="Origami pattern")
self.add_argument("--lines", type=int, default=1, help="Number of lines")
self.add_argument("--sides", type=int, default=3, help="Number of polygon sides")
self.add_argument("--add_attachment", type=inkex.Boolean, default=False, help="Add attachment?")
self.add_argument("--attachment_percentage", type=float, default=100., help="Length percentage of extra facet")
self.add_argument("--mirror_cells", type=inkex.Boolean, default=False, help="Mirror odd cells?")
@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__':
Kresling().run()

View File

@ -0,0 +1,89 @@
#! /usr/bin/env python3
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=float, default=10.0, help="Length")
self.add_argument('--measure_type', default=60, help="Type of length")
self.add_argument('--parameter_type', default=60, help="Type of parameter")
self.add_argument('--radial_ratio', type=float, default=0.5, help="Radial ratio")
self.add_argument('--angle_ratio', type=float, default=0.5, help="Anle ratio")
self.add_argument('--lambdatheta', type=float, default=45, help="lambdatheta")
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__':
Kresling_Full().run()

466
OrigamiPatterns/Path.py Normal file
View File

@ -0,0 +1,466 @@
#! /usr/bin/env python3
import inkex
from lxml import etree
from math import sin, cos, pi
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
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
"""
def __init__(self, points, style, closed=False, invert=False, radius=0.1, separated=False):
""" 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.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 [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 = etree.SubElement(group, 'g')
Path.draw_paths_recursively(subpath, subgroup, styles_dict)
else:
if 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': str(inkex.Style(styles_dict[subpath.style])), 'd': path}
etree.SubElement(group, inkex.addNS('path', 'svg'), attribs)
else:
attribs = {'style': str(inkex.Style(styles_dict[subpath.style])),
'cx': str(subpath.points[0][0]), 'cy': str(subpath.points[0][1]),
'r': str(subpath.radius)}
etree.SubElement(group, inkex.addNS('circle', 'svg'), attribs)
@classmethod
def 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.
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.
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))
return hgrid
@classmethod
def 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.
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))
return vgrid
@classmethod
def generate_polygon(cls, sides, radius, style, center=(0, 0)):
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)
@classmethod
def 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.
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]))
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)
@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):
paths_new.append(path+offset)
return paths_new
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)
@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:
paths_new.append(path*(1, theta, translation[0], translation[1]))
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)
# 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

263
OrigamiPatterns/Pattern.py Normal file
View File

@ -0,0 +1,263 @@
#! /usr/bin/env python3
import os
from abc import abstractmethod
from lxml import etree
from Path import Path, inkex
class Pattern(inkex.Effect):
@abstractmethod
def generate_path_tree(self):
""" Generate nested list of Path instances
Abstract method, must be defined in all child classes
"""
pass
def __init__(self):
inkex.Effect.__init__(self) # initialize the super class
self.add_argument = self.arg_parser.add_argument
self.add_argument("-u", "--units", default='mm', help="Units this dialog is using")
# self.add_argument("-a", "--add_attachment", type=inkex.Boolean, default=False, help="command line help")
# self.add_argument("", "--accuracy", type=int, default=0, help="command line help")
# --------------------------------------------------------------------------------------------------------------
# mountain options
self.add_argument('-m', '--mountain_stroke_color', default=4278190335, help='The mountain creases color.')
self.add_argument('--mountain_stroke_width', type=float, default=0.1, help='Width of mountain strokes.')
self.add_argument('--mountain_dashes_len', type=float, default=1.0, help='Mountain dash + gap length.')
self.add_argument('--mountain_dashes_duty', type=float, default=0.5, help='Mountain dash duty cycle.')
self.add_argument('--mountain_dashes_bool', type=inkex.Boolean, default=True, help='Dashed strokes?')
self.add_argument('--mountain_bool', type=inkex.Boolean, default=True, help='Draw mountains?')
# --------------------------------------------------------------------------------------------------------------
# valley options
self.add_argument('-v', '--valley_stroke_color', default=65535, help='The valley creases color.')
self.add_argument('--valley_stroke_width', type=float, default=0.1, help='Width of valley strokes.')
self.add_argument('--valley_dashes_len', type=float, default=1.0, help='Valley dash + gap length.')
self.add_argument('--valley_dashes_duty', type=float, default=0.25, help='Valley dash duty cycle.')
self.add_argument('--valley_dashes_bool', type=inkex.Boolean, default=True, help='Dashed strokes?')
self.add_argument('--valley_bool', type=inkex.Boolean, default=True, help='Draw valleys?')
# --------------------------------------------------------------------------------------------------------------
# edge options
self.add_argument('-e', '--edge_stroke_color', default=255, help='The mountain creases color.')
self.add_argument('--edge_stroke_width', type=float, default=0.1, help='Width of edge strokes.')
self.add_argument('--edge_dashes_len', type=float, default=1.0, help='Edge dash + gap length.')
self.add_argument('--edge_dashes_duty', type=float, default=0.25, help='Edge dash duty cycle.')
self.add_argument('--edge_dashes_bool', type=inkex.Boolean, default=False, help='Dashed strokes?')
self.add_argument('--edge_bool', type=inkex.Boolean, default=True, help='Draw edges?')
self.add_argument('--edge_single_path', type=inkex.Boolean, default=True, help='Edges as single path?')
# --------------------------------------------------------------------------------------------------------------
# universal crease options
self.add_argument('--universal_stroke_color', default=4278255615, help='The universal creases color.')
self.add_argument('--universal_stroke_width', type=float, default=0.1, help='Width of universal strokes.')
self.add_argument('--universal_dashes_len', type=float, default=1.0, help='Universal dash + gap length.')
self.add_argument('--universal_dashes_duty', type=float, default=0.25, help='Universal dash duty cycle.')
self.add_argument('--universal_dashes_bool', type=inkex.Boolean, default=False, help='Dashed strokes?')
self.add_argument('--universal_bool', type=inkex.Boolean, default=True, help='Draw universal creases?')
# --------------------------------------------------------------------------------------------------------------
# semicrease options
self.add_argument('--semicrease_stroke_color', default=4294902015, help='The semicrease creases color.')
self.add_argument('--semicrease_stroke_width', type=float, default=0.1, help='Width of semicrease strokes.')
self.add_argument('--semicrease_dashes_len', type=float, default=1.0, help='Semicrease dash + gap length.')
self.add_argument('--semicrease_dashes_duty', type=float,default=0.25, help='Semicrease dash duty cycle.')
self.add_argument('--semicrease_dashes_bool', type=inkex.Boolean, default=False, help='Dashed strokes?')
self.add_argument('--semicrease_bool', type=inkex.Boolean, default=True, help='Draw semicreases?')
# --------------------------------------------------------------------------------------------------------------
# cut options
self.add_argument('--cut_stroke_color', default=16711935, help='The cut creases color.')
self.add_argument('--cut_stroke_width', type=float, default=0.1, help='Width of cut strokes.')
self.add_argument('--cut_dashes_len', type=float, default=1.0, help='Cut dash + gap length.')
self.add_argument('--cut_dashes_duty', type=float, default=0.25, help='Cut dash duty cycle.')
self.add_argument('--cut_dashes_bool', type=inkex.Boolean, default=False, help='Dashed strokes?')
self.add_argument('--cut_bool', type=inkex.Boolean, default=True, help='Draw cuts?')
# --------------------------------------------------------------------------------------------------------------
# vertex options
self.add_argument('--vertex_stroke_color', default=255, help='Vertices\' color.')
self.add_argument('--vertex_stroke_width', type=float, default=0.1, help='Width of vertex strokes.')
self.add_argument('--vertex_radius', type=float, default=0.1, help='Radius of vertices.')
self.add_argument('--vertex_bool', type=bool, default=True, help='Draw vertices?')
# here so we can have tabs - but we do not use it directly - else error
self.add_argument('--active-tab', default='title', help="Active tab.")
self.path_tree = []
self.edge_points = []
self.translate = (0, 0)
def effect(self):
""" Main function, called when the extension is run.
"""
# 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
# 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 = 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
def get_layer(self):
try:
return self.svg.get_current_layer() # new
except:
return self.current_layer # old
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()
# define colour and stroke width
mountain_style = {'draw': self.options.mountain_bool,
'stroke': self.get_color_string(self.options.mountain_stroke_color),
'fill': 'none',
'stroke-width': self.options.mountain_stroke_width*unit_factor}
valley_style = {'draw': self.options.valley_bool,
'stroke': self.get_color_string(self.options.valley_stroke_color),
'fill': 'none',
'stroke-width': self.options.valley_stroke_width*unit_factor}
universal_style = {'draw': self.options.universal_bool,
'stroke': self.get_color_string(self.options.universal_stroke_color),
'fill': 'none',
'stroke-width': self.options.universal_stroke_width*unit_factor}
semicrease_style = {'draw': self.options.semicrease_bool,
'stroke': self.get_color_string(self.options.semicrease_stroke_color),
'fill': 'none',
'stroke-width': self.options.semicrease_stroke_width*unit_factor}
cut_style = {'draw': self.options.cut_bool,
'stroke': self.get_color_string(self.options.cut_stroke_color),
'fill': 'none',
'stroke-width': self.options.cut_stroke_width*unit_factor}
edge_style = {'draw': self.options.edge_bool,
'stroke': self.get_color_string(self.options.edge_stroke_color),
'fill': 'none',
'stroke-width': self.options.edge_stroke_width*unit_factor}
vertex_style = {'draw': self.options.vertex_bool,
'stroke': self.get_color_string(self.options.vertex_stroke_color),
'fill': 'none',
'stroke-width': self.options.vertex_stroke_width*unit_factor}
# check if dashed option selected
if self.options.mountain_dashes_bool:
dash = self.options.mountain_dashes_len*self.options.mountain_dashes_duty*unit_factor
gap = abs(dash - self.options.mountain_dashes_len*unit_factor)
mountain_style['stroke-dasharray'] = "{},{}".format(dash, gap)
if self.options.valley_dashes_bool:
dash = self.options.valley_dashes_len * self.options.valley_dashes_duty*unit_factor
gap = abs(dash - self.options.valley_dashes_len*unit_factor)
valley_style['stroke-dasharray'] = "{},{}".format(dash, gap)
if self.options.edge_dashes_bool:
dash = self.options.edge_dashes_len * self.options.edge_dashes_duty*unit_factor
gap = abs(dash - self.options.edge_dashes_len*unit_factor)
edge_style['stroke-dasharray'] = "{},{}".format(dash, gap)
if self.options.universal_dashes_bool:
dash = self.options.universal_dashes_len * self.options.universal_dashes_duty*unit_factor
gap = abs(dash - self.options.universal_dashes_len*unit_factor)
universal_style['stroke-dasharray'] = "{},{}".format(dash, gap)
if self.options.semicrease_dashes_bool:
dash = self.options.semicrease_dashes_len * self.options.semicrease_dashes_duty*unit_factor
gap = abs(dash - self.options.semicrease_dashes_len*unit_factor)
semicrease_style['stroke-dasharray'] = "{},{}".format(dash, gap)
if self.options.cut_dashes_bool:
dash = self.options.cut_dashes_len * self.options.cut_dashes_duty*unit_factor
gap = abs(dash - self.options.cut_dashes_len*unit_factor)
cut_style['stroke-dasharray'] = "{},{}".format(dash, gap)
self.styles_dict = {'m': mountain_style,
'v': valley_style,
'u': universal_style,
's': semicrease_style,
'c': cut_style,
'e': edge_style,
'p': vertex_style}
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': str(Inkex.style(line_style)),
'x': str(position[0]),
'y': str((position[1] + text_height) * 1.2)
}
line = 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)

View File

@ -0,0 +1,91 @@
#! /usr/bin/env python3
import inkex
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):
Pattern.__init__(self)
self.add_argument("-p", "--pattern", default="pleat_circular", help="Origami pattern")
self.add_argument("--radius", type=float, default=55.0, help="Radius of circle")
self.add_argument("--ratio", type=float, default=0.4, help="Opening ratio")
self.add_argument("--rings", type=int, default=15, help="Number of rings")
self.add_argument("--simulation_mode", type=inkex.Boolean, default=True, help="Approximate circle and draw semicreases for simulation?")
self.add_argument("--sides", type=int, default=20, help="Number of sides for polygon approximating half circle")
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__':
PleatCircular().run()

View File

@ -0,0 +1,97 @@
#! /usr/bin/env python3
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):
Pattern.__init__(self)
self.add_argument('-p', '--pattern', default="template1", help="Origami pattern")
self.add_argument('--length', type=float, default=10.0, help="Length of grid square")
self.add_argument('--theta', type=int, default=0, help="Rotation angle (degree)")
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
theta = self.options.theta * pi / 180
# create all Path instances defining strokes
# first define its points as a list of tuples...
mountain_h_stroke_points = [(length / 2, 0),
(length / 2, length)]
mountain_v_stroke_points = [(0, length / 2),
(length, length / 2)]
# ... and then create the Path instances, defining its type ('m' for mountain, etc...)
mountains = [Path(mountain_h_stroke_points, 'm' if pattern == 'template1' else 'v'),
Path(mountain_v_stroke_points, 'm' if pattern == 'template1' else 'v')]
# doing the same for valleys
valley_1st_stroke_points = [(0, 0),
(length, length)]
valley_2nd_stroke_points = [(0, length),
(length, 0)]
valleys = [Path(valley_1st_stroke_points, 'v' if pattern == 'template1' else 'm'),
Path(valley_2nd_stroke_points, 'v' if pattern == 'template1' else 'm')]
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, theta, (1 * length, 1 * length))
mountains = Path.list_rotate(mountains, theta, (1 * length, 1 * length))
valleys = Path.list_rotate(valleys, theta, (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
edge_points = [(0 * length, 0 * length), # top left
(1 * length, 0 * length), # top right
(1 * length, 1 * length), # bottom right
(0 * length, 1 * length)] # bottom left
# create path from points to be able to use the already built rotate method
edges = Path(edge_points, 'e', closed=True)
edges = Path.list_rotate(edges, theta, (1 * length, 1 * length))
# 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)
# 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
# IMPORTANT: the attribute "path_tree" must be created at the end, saving all strokes
self.path_tree = [mountains, valleys, vertices]
# 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]
# 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__':
Template().run()

View File

@ -0,0 +1,103 @@
#! /usr/bin/env python3
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):
Pattern.__init__(self)
self.add_argument("-p", "--pattern", default="waterbomb", help="Origami pattern")
self.add_argument("--pattern_first_line", default="waterbomb", help="Origami pattern")
self.add_argument("--pattern_last_line", default="waterbomb", help="Origami pattern")
self.add_argument("--lines", type=int, default=8, help="Number of lines")
self.add_argument("--columns", type=int, default=16, help="Number of columns")
self.add_argument("--length", type=float, default=10.0, help="Length of grid square")
self.add_argument('--phase_shift', type=inkex.Boolean, default=True, help='Shift phase of tesselation.')
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(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__':
Waterbomb().run()

View File

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Origami Pattern - Kresling tower</_name>
<id>fablabchemnitz.de.origami_patterns.kresling_full</id>
<param name='active-tab' type="notebook">
<page name="title" _gui-text="Options">
<param name="pattern" type="optiongroup" _gui-text="Type of Kresling tower" >
<_option value="regular">Regular</_option>
<_option value="mirrowed">Mirror odd cells</_option>
</param>
<_param name="help" type="description" xml:space="preserve">------------------------------------------------------------</_param>
<param name="lines" type="int" min="1" max="100" _gui-text="Number of cells">3</param>
<param name="sides" type="int" min="3" max="100" _gui-text="Number of polygon sides">6</param>
<_param name="help" type="description" xml:space="preserve">------------------------------------------------------------</_param>
<param name="measure_value" type="float" max="10000" precision="3" _gui-text="Measure value:">10.0</param>
<param name="measure_type" type="optiongroup" appearance="minimal" _gui-text="Measure type:">
<option value="a">Polygon side (a)</option>
<option value="b">Vertical mountain crease (b)</option>
<option value="l">Diagonal valley crease (l)</option>
<option value="radius_external">External radius</option>
<option value="radius_internal">Internal radius (totally closed)</option>
<option value="diameter_external">External diameter</option>
<option value="diameter_internal">Internal diameter (totally closed)</option>
<!--
<option value="a_l_angle">Angle between valley crease and horizontal polygon side</option>
-->
</param>
<param name="units" type="optiongroup" appearance="minimal" _gui-text="">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="in">in</option>
<option value="pt">pt</option>
<option value="px">px</option></param>
<_param name="help" type="description" xml:space="preserve">------------------------------------------------------------</_param>
<param name="parameter_type" type="optiongroup" appearance="full" _gui-text="Parameter type:">
<option value="angle_ratio">Angle ratio (lambda)</option>
<option value="radial_ratio">Radial ratio</option>
<option value="lambdatheta">Angle between a and l (lambda * theta)</option></param>
<param name="radial_ratio" type="float" min="0" max="0.7" precision="3" _gui-text="Radial ratio:">0.5</param>
<param name="angle_ratio" type="float" min="0.5" max="1" precision="3" _gui-text="Angle ratio:">0.5</param>
<param name="lambdatheta" type="float" min="15" max="90" precision="2" _gui-text="Angle between a and l">60.0</param>
<!-- <option value="angle">Angle between l and a</option>-->
</page>
<page name="extra_options" _gui-text="Extra Options">
<param name="add_attachment" type="boolean" _gui-text="Add one more facet to close tower?">false</param>
<param name="attachment_percentage" type="float" min="0" max="100" precision="1" appearance="full" _gui-text="Length percentage of extra facet">100</param>
<_param name="help" type="description" xml:space="preserve">------------------------------------------------------------</_param>
</page>
<page name="mountains" _gui-text="Mountain creases">
<param name="mountain_bool" type="boolean" _gui-text="Draw mountains?">true</param>
<param name="mountain_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="mountain_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Mountain dash + gap length">1</param>
<param name="mountain_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Mountain dash duty cycle">0.5</param>
<param name="mountain_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of mountain strokes">0.1</param>
<param name="mountain_stroke_color" type="color" gui-text="Mountain creases color: ">4278190335</param>
</page>
<page name="valleys" _gui-text="Valley creases">
<param name="valley_bool" type="boolean" _gui-text="Draw valley?">true</param>
<param name="valley_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="valley_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Valley dash + gap length">1</param>
<param name="valley_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Valley dash duty cycle">0.25</param>
<param name="valley_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of valley strokes">0.1</param>
<param name="valley_stroke_color" type="color" gui-text="Valley creases color: ">65535</param>
</page>
<page name="edge" _gui-text="Edge">
<param name="edge_bool" type="boolean" _gui-text="Draw edges?">true</param>
<param name="edge_single_path" type="boolean" _gui-text="Edges as single path?">true</param>
<param name="edge_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>
<param name="edge_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Edge dash + gap length">1</param>
<param name="edge_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Edge dash duty cycle">0.25</param>
<param name="edge_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of edge strokes">0.1</param>
<param name="edge_stroke_color" type="color" gui-text="Edge color: ">255</param>
</page>
<page name="vertices" _gui-text="Vertices">
<param name="vertex_bool" type="boolean" _gui-text="Draw vertices?">false</param>
<param name="vertex_radius" type="float" min="0.01" max="50" appearance="full" _gui-text="Radius of vertices">0.1</param>
<param name="vertex_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of vertex strokes">0.1</param>
<param name="vertex_stroke_color" type="color" gui-text="Vertices\' color: ">255</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Paper/Cardboard Boxes"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">OrigamiPatterns/Kresling_full.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Origami Pattern - Circular</_name>
<id>fablabchemnitz.de.origami_patterns.pleat_circular</id>
<param name='active-tab' type="notebook">
<page name="title" _gui-text="Options">
<param name="radius" type="float" max="10000" precision="3" _gui-text="Radius of circle">55.0</param>
<param name="units" type="optiongroup" appearance="minimal" _gui-text="">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="in">in</option>
<option value="pt">pt</option>
<option value="px">px</option></param>
<param name="ratio" type="float" min="0" max="1" precision="3" _gui-text="Opening ratio">0.4</param>
<param name="rings" type="int" min="3" max="100" _gui-text="Number of rings">15</param>
<_param name="help" type="description" xml:space="preserve">------------------------------</_param>
<param name="simulation_mode" type="boolean" _gui-text="Simulation mode">true</param>
<_param name="help" type="description" xml:space="preserve">To simulate with OrigamiSimulator, semicreases (or facet creases) must be added to properly simulate paper, and the circles must be approximated as polygons.</_param>
<param name="sides" type="int" min="10" max="100" _gui-text="Number of sides for polygon approximating half circle">20</param>
</page>
<page name="mountains" _gui-text="Mountain creases">
<param name="mountain_bool" type="boolean" _gui-text="Draw mountains?">true</param>
<param name="mountain_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="mountain_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Mountain dash + gap length">1</param>
<param name="mountain_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Mountain dash duty cycle">0.5</param>
<param name="mountain_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of mountain strokes">0.1</param>
<param name="mountain_stroke_color" type="color" gui-text="Mountain creases color: ">4278190335</param>
</page>
<page name="valleys" _gui-text="Valley creases">
<param name="valley_bool" type="boolean" _gui-text="Draw valley?">true</param>
<param name="valley_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="valley_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Valley dash + gap length">1</param>
<param name="valley_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Valley dash duty cycle">0.25</param>
<param name="valley_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of valley strokes">0.1</param>
<param name="valley_stroke_color" type="color" gui-text="Valley creases color: ">65535</param>
</page>
<page name="edge" _gui-text="Edge">
<param name="edge_bool" type="boolean" _gui-text="Draw edges?">true</param>
<param name="edge_single_path" type="boolean" _gui-text="Edges as single path?">true</param>
<param name="edge_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>
<param name="edge_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Edge dash + gap length">1</param>
<param name="edge_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Edge dash duty cycle">0.25</param>
<param name="edge_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of edge strokes">0.1</param>
<param name="edge_stroke_color" type="color" gui-text="Edge color: ">255</param>
</page>
<page name="semicrease" _gui-text="Semicreases">
<param name="semicrease_bool" type="boolean" _gui-text="Draw semicreases?">true</param>
<param name="semicrease_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>
<param name="semicrease_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Semicrease dash + gap length">1</param>
<param name="semicrease_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Semicrease dash duty cycle">0.25</param>
<param name="semicrease_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of semicrease strokes">0.1</param>
<param name="semicrease_stroke_color" type="color" gui-text="Semicreases color: ">4294902015</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Paper/Cardboard Boxes"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">OrigamiPatterns/Pleat_Circular.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Origami Pattern - N-sided Hypar</_name>
<id>fablabchemnitz.de.origami_patterns.pleat_hypar</id>
<param name='active-tab' type="notebook">
<page name="title" _gui-text="Options">
<param name="pattern" type="optiongroup" _gui-text="Type of Hypar" >
<_option value="classic">Classic Hypar</_option>
<_option value="asymmetric">Asymmetric triangulation</_option>
<_option value="alternate_asymmetric">Alternating asymmetric triangulation</_option>
</param>
<param name="radius" type="float" max="10000" precision="3" _gui-text="Radius of polygon">100.0</param>
<param name="units" type="optiongroup" appearance="minimal" _gui-text="">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="in">in</option>
<option value="pt">pt</option>
<option value="px">px</option></param>
<param name="sides" type="int" min="3" max="100" _gui-text="Number of polygon sides">4</param>
<param name="rings" type="int" min="1" max="100" _gui-text="Number of rings">7</param>
<param name="simplify_center" type="boolean" _gui-text="Simplify center (probably not suited for odd number of sides)">false</param>
<_param name="help" type="description" xml:space="preserve">Implements Hypar (classical hyperbolic paraboloid approximate).&#xA;&#xA;Classic Hypar is the easiest one to fold. However, it's not rigid foldable. More information in:&#xA;Demaine, E. D., Demaine, M. L., Hart, V., Price, G. N., & Tachi, T. (2011). (Non)Existence of Pleated Folds: How Paper Folds Between Creases. Graphs and Combinatorics, 27(3), 377397. https://doi.org/10.1007/s00373-011-1025-2</_param>
</page>
<page name="mountains" _gui-text="Mountain creases">
<param name="mountain_bool" type="boolean" _gui-text="Draw mountains?">true</param>
<param name="mountain_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="mountain_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Mountain dash + gap length">1</param>
<param name="mountain_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Mountain dash duty cycle">0.5</param>
<param name="mountain_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of mountain strokes">0.1</param>
<param name="mountain_stroke_color" type="color" gui-text="Mountain creases color: ">4278190335</param>
</page>
<page name="valleys" _gui-text="Valley creases">
<param name="valley_bool" type="boolean" _gui-text="Draw valley?">true</param>
<param name="valley_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="valley_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Valley dash + gap length">1</param>
<param name="valley_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Valley dash duty cycle">0.25</param>
<param name="valley_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of valley strokes">0.1</param>
<param name="valley_stroke_color" type="color" gui-text="Valley creases color: ">65535</param>
</page>
<page name="universal" _gui-text="Universal creases">
<param name="universal_bool" type="boolean" _gui-text="Draw universal creases?">true</param>
<param name="universal_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>
<param name="universal_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Universal dash + gap length">1</param>
<param name="universal_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Universal dash duty cycle">0.25</param>
<param name="universal_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of universal strokes">0.1</param>
<param name="universal_stroke_color" type="color" gui-text="Universal creases color: ">4278255615</param>
</page>
<page name="edge" _gui-text="Edge">
<param name="edge_bool" type="boolean" _gui-text="Draw edges?">true</param>
<param name="edge_single_path" type="boolean" _gui-text="Edges as single path?">true</param>
<param name="edge_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>
<param name="edge_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Edge dash + gap length">1</param>
<param name="edge_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Edge dash duty cycle">0.25</param>
<param name="edge_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of edge strokes">0.1</param>
<param name="edge_stroke_color" type="color" gui-text="Edge color: ">255</param>
</page>
<page name="vertices" _gui-text="Vertices">
<param name="vertex_bool" type="boolean" _gui-text="Draw vertices?">false</param>
<param name="vertex_radius" type="float" min="0.01" max="50" appearance="full" _gui-text="Radius of vertices">0.1</param>
<param name="vertex_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of vertex strokes">0.1</param>
<param name="vertex_stroke_color" type="color" gui-text="Vertices\' color: ">255</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Paper/Cardboard Boxes"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">OrigamiPatterns/Hypar.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Origami Pattern - Template effect</_name>
<id>fablabchemnitz.de.origami_patterns.template</id>
<param name='active-tab' type="notebook">
<page name="title" _gui-text="Options">
<param name="pattern" type="optiongroup" _gui-text="Type of template" >
<_option value="template1">Template pattern 1</_option>
<_option value="template2">Template pattern 2</_option>
</param>
<param name="length" type="float" max="10000" precision="3" _gui-text="Length of grid square">10.0</param>
<param name="units" type="optiongroup" appearance="minimal" _gui-text="">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="in">in</option>
<option value="pt">pt</option>
<option value="px">px</option></param>
<param name="theta" type="int" min="0" max="360" _gui-text="Rotation angle (degree)">0</param>
<_param name="help" type="description" xml:space="preserve">The .inx file defines the bridge between Inkscape's interface and the python script.</_param>
</page>
<page name="mountains" _gui-text="Mountain creases">
<param name="mountain_bool" type="boolean" _gui-text="Draw mountains?">true</param>
<param name="mountain_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="mountain_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Mountain dash + gap length">1</param>
<param name="mountain_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Mountain dash duty cycle">0.5</param>
<param name="mountain_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of mountain strokes">0.1</param>
<param name="mountain_stroke_color" type="color" gui-text="Mountain creases color: ">4278190335</param>
</page>
<page name="valleys" _gui-text="Valley creases">
<param name="valley_bool" type="boolean" _gui-text="Draw valley?">true</param>
<param name="valley_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="valley_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Valley dash + gap length">1</param>
<param name="valley_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Valley dash duty cycle">0.25</param>
<param name="valley_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of valley strokes">0.1</param>
<param name="valley_stroke_color" type="color" gui-text="Valley creases color: ">65535</param>
</page>
<page name="edge" _gui-text="Edge">
<param name="edge_bool" type="boolean" _gui-text="Draw edges?">true</param>
<param name="edge_single_path" type="boolean" _gui-text="Edges as single path?">true</param>
<param name="edge_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>
<param name="edge_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Edge dash + gap length">1</param>
<param name="edge_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Edge dash duty cycle">0.25</param>
<param name="edge_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of edge strokes">0.1</param>
<param name="edge_stroke_color" type="color" gui-text="Edge color: ">255</param>
</page>
<page name="vertices" _gui-text="Vertices">
<param name="vertex_bool" type="boolean" _gui-text="Draw vertices?">true</param>
<param name="vertex_radius" type="float" min="0.01" max="50" appearance="full" _gui-text="Radius of vertices">0.1</param>
<param name="vertex_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of vertex strokes">0.1</param>
<param name="vertex_stroke_color" type="color" gui-text="Vertices\' color: ">255</param>
</page>
<!-- UNCOMMENT UNIVERSAL CREASES, CUTS AND/OR SEMICREASES IF NEEDED-->
<!-- <page name="universal" _gui-text="Universal creases">-->
<!-- <param name="universal_bool" type="boolean" _gui-text="Draw universal creases?">true</param>-->
<!-- <param name="universal_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>-->
<!-- <param name="universal_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Universal dash + gap length">1</param>-->
<!-- <param name="universal_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Universal dash duty cycle">0.25</param>-->
<!-- <param name="universal_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of universal strokes">0.1</param> -->
<!-- <param name="universal_stroke_color" type="color" gui-text="Universal creases color: ">4278255615</param>-->
<!-- </page>-->
<!-- <page name="semicrease" _gui-text="Semicreases">-->
<!-- <param name="semicrease_bool" type="boolean" _gui-text="Draw semicreases?">true</param>-->
<!-- <param name="semicrease_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>-->
<!-- <param name="semicrease_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Semicrease dash + gap length">1</param>-->
<!-- <param name="semicrease_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Semicrease dash duty cycle">0.25</param>-->
<!-- <param name="semicrease_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of semicrease strokes">0.1</param>-->
<!-- <param name="semicrease_stroke_color" type="color" gui-text="Semicreases color: ">4294902015</param>-->
<!-- </page>-->
<!-- <page name="cuts" _gui-text="Cuts">-->
<!-- <param name="cut_bool" type="boolean" _gui-text="Draw cuts?">true</param>-->
<!-- <param name="cut_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>-->
<!-- <param name="cut_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Cut dash + gap length">1</param>-->
<!-- <param name="cut_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Cut dash duty cycle">0.25</param>-->
<!-- <param name="cut_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of cut strokes">0.1</param>-->
<!-- <param name="cut_stroke_color" type="color" gui-text="Cut creases color: ">16711935</param>-->
<!-- </page>-->
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Paper/Cardboard Boxes"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">OrigamiPatterns/Template.py</command>
</script>
</inkscape-extension>

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<inkscape-extension xmlns="http://www.inkscape.org/namespace/inkscape/extension">
<_name>Origami Pattern - Waterbomb</_name>
<id>fablabchemnitz.de.origami_patterns.magic_ball</id>
<param name='active-tab' type="notebook">
<page name="title" _gui-text="Options">
<param name="pattern_first_line" type="optiongroup" _gui-text="First line:" >
<_option value="waterbomb">Regular waterbomb</_option>
<_option value="magic_ball">Magic ball</_option>
</param>
<param name="pattern_last_line" type="optiongroup" _gui-text="Last line:" >
<_option value="waterbomb">Regular waterbomb</_option>
<_option value="magic_ball">Magic ball</_option>
</param>
<param name="phase_shift" type="boolean" _gui-text="Shift phase?">false</param>
<_param name="help" type="description" xml:space="preserve">------------------------------</_param>
<param name="lines" type="int" min="1" max="100" _gui-text="Number of lines">8</param>
<param name="columns" type="int" min="1" max="100" _gui-text="Number of columns">16</param>
<_param name="help" type="description" xml:space="preserve">------------------------------</_param>
<param name="length" type="float" max="10000" precision="3" _gui-text="Length of grid square">10.0</param>
<param name="units" type="optiongroup" appearance="minimal" _gui-text="">
<option value="mm">mm</option>
<option value="cm">cm</option>
<option value="in">in</option>
<option value="pt">pt</option>
<option value="px">px</option></param>
<_param name="help" type="description" xml:space="preserve">"Waterbomb tessellation" creates a simple tessellation pattern repeating the Waterbomb base, with a half-step phase shift between each line.&#xA;The Magic ball is a different design that inverts both the upper half of the first line and the bottom half of the last line.</_param>
</page>
<page name="mountains" _gui-text="Mountain creases">
<param name="mountain_bool" type="boolean" _gui-text="Draw mountains?">true</param>
<param name="mountain_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="mountain_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Mountain dash + gap length">1</param>
<param name="mountain_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Mountain dash duty cycle">0.5</param>
<param name="mountain_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of mountain strokes">0.1</param>
<param name="mountain_stroke_color" type="color" gui-text="Mountain creases color: ">4278190335</param>
</page>
<page name="valleys" _gui-text="Valley creases">
<param name="valley_bool" type="boolean" _gui-text="Draw valley?">true</param>
<param name="valley_dashes_bool" type="boolean" _gui-text="Dashed strokes?">true</param>
<param name="valley_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Valley dash + gap length">1</param>
<param name="valley_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Valley dash duty cycle">0.25</param>
<param name="valley_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of valley strokes">0.1</param>
<param name="valley_stroke_color" type="color" gui-text="Valley creases color: ">65535</param>
</page>
<page name="edge" _gui-text="Edge">
<param name="edge_bool" type="boolean" _gui-text="Draw edges?">true</param>
<param name="edge_single_path" type="boolean" _gui-text="Edges as single path?">true</param>
<param name="edge_dashes_bool" type="boolean" _gui-text="Dashed strokes?">false</param>
<param name="edge_dashes_len" type="float" min="0.1" max="10" appearance="full" precision="2" _gui-text="Edge dash + gap length">1</param>
<param name="edge_dashes_duty" type="float" min="0.1" max="1" appearance="full" precision="2" _gui-text="Edge dash duty cycle">0.25</param>
<param name="edge_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of edge strokes">0.1</param>
<param name="edge_stroke_color" type="color" gui-text="Edge color: ">255</param>
</page>
<page name="vertices" _gui-text="Vertices">
<param name="vertex_bool" type="boolean" _gui-text="Draw vertices?">false</param>
<param name="vertex_radius" type="float" min="0.01" max="50" appearance="full" _gui-text="Radius of vertices">0.1</param>
<param name="vertex_stroke_width" type="float" min="0.01" max="3" appearance="full" _gui-text="Width of vertex strokes">0.1</param>
<param name="vertex_stroke_color" type="color" gui-text="Vertices\' color: ">255</param>
</page>
</param>
<effect>
<object-type>all</object-type>
<effects-menu>
<submenu _name="FabLab Chemnitz">
<submenu _name="Paper/Cardboard Boxes"/>
</submenu>
</effects-menu>
</effect>
<script>
<command reldir="extensions" interpreter="python">OrigamiPatterns/Waterbomb.py</command>
</script>
</inkscape-extension>