diff --git a/.vscode/settings.json b/.vscode/settings.json index 01ee7a8..2844c0e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,8 @@ { - "terminal.integrated.cwd": "${workspaceFolder}" + "terminal.integrated.cwd": "${workspaceFolder}", + "cSpell.words": [ + "pyplot", + "xlabel", + "ylabel" + ] } \ No newline at end of file diff --git a/convo.svg b/convo.svg new file mode 100644 index 0000000..8057a21 --- /dev/null +++ b/convo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/embroiderTurtle.py b/embroiderTurtle.py new file mode 100644 index 0000000..dae4d45 --- /dev/null +++ b/embroiderTurtle.py @@ -0,0 +1,91 @@ +import matplotlib.pyplot as plt +from math import pi, sin, cos, isnan + +DEGREES_TO_RADIANS = pi / 180 + + +def print_coords(coords): + for (x, y) in coords: + if isnan(x): + print("") + else: + print("({:.2f}, {:.2f})".format(x, y)) + + +def turtle_to_coords(turtle_program, turn_amount=45): + # The state variable tracks the current location and angle of the turtle. + # The turtle starts at (0, 0) facing up (90 degrees). + state = (0.0, 0.0, 90.0) + + # Throughout the turtle's journey, we "yield" its location. These coordinate + # pairs become the path that plot_coords draws. + yield (0.0, 0.0) + + # Loop over the program, one character at a time. + for command in turtle_program: + x, y, angle = state + + if command in "Ff": # Move turtle forward + state = ( + x - cos(angle * DEGREES_TO_RADIANS), + y + sin(angle * DEGREES_TO_RADIANS), + angle, + ) + + if command == "f": + # Insert a break in the path so that + # this line segment isn't drawn. + yield (float("nan"), float("nan")) + + yield (state[0], state[1]) + + elif command == "+": # Turn turtle clockwise without moving + state = (x, y, angle + turn_amount) + + elif command == "-": # Turn turtle counter-clockwise without moving + state = (x, y, angle - turn_amount) + + # Note: We silently ignore unknown commands + + +def plot_coords(coords, bare_plot=False): + if bare_plot: + # Turns off the axis markers. + plt.axis("off") + # Ensures equal aspect ratio. + plt.axes().set_aspect("equal", "datalim") + # Converts a list of coordinates into + # lists of X and Y values, respectively. + X, Y = zip(*coords) + # Draws the plot. + plt.plot(X, Y) + + +def transform_sequence(sequence, transformations): + return "".join(transformations.get(c, c) for c in sequence) + + +def transform_multiple(sequence, transformations, iterations): + for _ in range(iterations): + sequence = transform_sequence(sequence, transformations) + return sequence + + +def hilbert(): + return turtle_to_coords( + transform_multiple("L", {"L": "-RF+LFL+FR-", "R": "+LF-RFR-FL+"}, 5), 90 + ) + + +if __name__ == "__main__": + plt.style.use("bmh") # Use some nicer default colors + plt.xlabel("x") + plt.ylabel("y") + + plot_coords( + turtle_to_coords( + transform_multiple("L", {"L": "-RF+LFL+FR-", "R": "+LF-RFR-FL+"}, 5), 90 + ) + ) + + plt.show() diff --git a/embryoid.py b/embryoid.py index 97f3a0f..3259b68 100644 --- a/embryoid.py +++ b/embryoid.py @@ -22,8 +22,15 @@ class Embryoid: def add_stitch_block(self, block): self.pattern.add_block(block) + def block_from_coords(self, coords): + block = [] + for coord in coords: + block.append((coord[0], coord[1])) + self.pattern.add_block(block, "teal") + def parse_svg(self, fname): paths, attributes = svg2paths(fname) + print(paths) print(attributes) for path in paths: block = [] @@ -43,15 +50,22 @@ def solid_block(x_len=100, y_len=100, num_stitches=20): return stitches -def parse(fname): +def parse(fname, outname): e = Embryoid() e.parse_svg(INPUT_SVG + fname) - e.save_svg("linger_longer.svg") - e.save_pes("linger_longer.pes") + e.save_svg(outname + ".svg") + e.save_pes(outname + ".pes") if __name__ == "__main__": - parse("linger_longer_audioplot.svg") + from embroiderTurtle import hilbert + + e = Embryoid() + e.block_from_coords([*hilbert()]) + e.save_pes("hilbert.pes") + e.save_svg("hilbert.svg") + print(*hilbert()) + # parse("convo.svg", "convo") # e = Embryoid() # e.add_stitch_block(solid_block()) # e.save_svg("block_test.svg") diff --git a/hilbert.pes b/hilbert.pes new file mode 100644 index 0000000..170dcd2 Binary files /dev/null and b/hilbert.pes differ diff --git a/hilbert.svg b/hilbert.svg new file mode 100644 index 0000000..dde125b --- /dev/null +++ b/hilbert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/l-system.py b/l-system.py new file mode 100644 index 0000000..d1ba05e --- /dev/null +++ b/l-system.py @@ -0,0 +1,203 @@ +import pygame +import math + +# other = variables +# F,A = move n forward +# G = move n forward without drawing a line +# B = move n backwards +# - = turn left by angle +# + = turn right by angle +# [ = push position and angle +# ] = pop position and angle +# a,b,c,d = color 1,2,3,4 +# 1-4 line size (std = 1) +# + +rules = {} + +rules["F"] = "F-F++F-F" +axiom = "F++F++F" +angle = 60 + +# rules['A'] = '+F-A-F+' # Sierpinsky +# rules['F'] = '-A+F+A-' +# axiom = 'A' +# angle = 60 + +# rules['F'] = 'F+F-F-F+F' # Koch curve 1 +# axiom = 'F' +# angle = 60 + +# rules['F'] = 'F+F--F+F' # Koch curve 2 +# axiom = 'F' +# angle = 60 + +# rules["X"] = "X+YF+" # Dragon curve +# rules["Y"] = "-FX-Y" +# axiom = "FX" +# angle = 90 + +# rules['X'] = 'F-[[X]+X]+F[+FX]-X' # Wheat +# rules['F'] = 'FF' +# axiom = 'X' +# angle = 25 + +# rules['F'] = 'a2FF-[c1-F+F+F]+[c1+F-F-F]' # Tree - colored +# axiom = 'F' +# angle = 23 + +# rules['X'] = 'F-[[X]-1X]+2F-[+3FX]+1X' # Wheat +# rules['F'] = 'X' +# axiom = 'X' +# angle = 25 + +iterations = 7 # number of iterations +step = 15 # step size / line length + +color1 = (105, 46, 26) # brown 1 +color2 = (201, 146, 127) # brown 2 +color3 = (101, 250, 52) # green +color4 = (255, 255, 255) # white + +angleoffset = 90 + +size = width, height = 10000, 10000 # display with/height +pygame.init() # init display +screen = pygame.Surface(size) # open screen + +# startpos = 100, height - 225 +# startpos = 50, height / 2 - 50 +startpos = width / 2, height / 2 +# startpos = 100, height / 2 +# startpos = 10,10 + + +def applyRule(input): + output = "" + for ( + rule, + result, + ) in rules.items(): # applying the rule by checking the current char against it + if input == rule: + output = result # Rule 1 + break + else: + output = input # else ( no rule set ) output = the current char -> no rule was applied + return output + + +def processString(oldStr): + newstr = "" + for character in oldStr: + newstr = newstr + applyRule(character) # build the new string + return newstr + + +def createSystem(numIters, axiom): + startString = axiom + endString = "" + for i in range(numIters): # iterate with appling the rules + print("Iteration: {0}".format(i)) + endString = processString(startString) + startString = endString + return endString + + +def polar_to_cart(theta, r, offx, offy): + x = r * math.cos(math.radians(theta)) + y = r * math.sin(math.radians(theta)) + return tuple([x + y for x, y in zip((int(x), int(y)), (offx, offy))]) + + +def cart_to_polar(x, y): + return (math.degrees(math.atan(y / x)), math.sqrt(math.pow(x, 2) + math.pow(y, 2))) + + +def drawTree(input, oldpos): + a = 0 # angle + i = 0 # counter for processcalculation + processOld = 0 # old process + newpos = oldpos + num = [] # stack for the brackets + color = (255, 255, 255) + linesize = 1 + xmax = 0 + xmin = 0 + ymax = 0 + ymin = 0 + for ( + character + ) in input: # process for drawing the l-system by writing the string to the screen + + i += 1 # print process in percent + process = i * 100 / len(input) + if not process == processOld: + print(process, "%") + processOld = process + + if character == "A": # magic happens here + newpos = polar_to_cart(a + angleoffset, step, oldpos[0], oldpos[1]) + pygame.draw.line(screen, color, oldpos, newpos, linesize) + oldpos = newpos + elif character == "F": + newpos = polar_to_cart(a + angleoffset, step, oldpos[0], oldpos[1]) + pygame.draw.line(screen, color, oldpos, newpos, linesize) + oldpos = newpos + elif character == "B": + newpos = polar_to_cart(-a + angleoffset, -step, oldpos[0], oldpos[1]) + pygame.draw.line(screen, color, oldpos, newpos, linesize) + oldpos = newpos + elif character == "G": + newpos = polar_to_cart(a + angleoffset, step, oldpos[0], oldpos[1]) + oldpos = newpos + elif character == "a": + color = color1 + elif character == "b": + color = color2 + elif character == "c": + color = color3 + elif character == "d": + color = color4 + elif character == "1": + linesize = 1 + elif character == "2": + linesize = 2 + elif character == "3": + linesize = 3 + elif character == "4": + linesize = 4 + elif character == "+": + a += angle + elif character == "-": + a -= angle + elif character == "[": + num.append((oldpos, a)) + elif character == "]": + oldpos, a = num.pop() + if xmax < oldpos[0] - width / 2: + xmax = oldpos[0] - width / 2 + if xmin > oldpos[0] - width / 2: + xmin = oldpos[0] - width / 2 + if ymax < oldpos[1] - height / 2: + ymax = oldpos[1] - height / 2 + if ymin > oldpos[1] - height / 2: + ymin = oldpos[1] - height / 2 + crop = pygame.Surface((abs(xmax - xmin) + 100, abs(ymax - ymin) + 100)) + crop.blit( + screen, + (50, 50), + (xmin + width / 2, ymin + height / 2, xmax + width / 2, ymax + height / 2), + ) + pygame.image.save(crop, "screenshot.png") + + +if __name__ == "__main__": + # drawTree(createSystem(iterations, axiom), startpos) + tree = createSystem(iterations, axiom) + drawTree(tree, startpos) + # pygame.display.flip() + # pygame.image.save(screen, "screenshot.png") + # print "Finished" + while 1: + pass + exit() # uncommand diff --git a/linger_longer.pes b/linger_longer.pes new file mode 100644 index 0000000..18bf62b Binary files /dev/null and b/linger_longer.pes differ diff --git a/linger_longer.svg b/linger_longer.svg new file mode 100644 index 0000000..c2d133e --- /dev/null +++ b/linger_longer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests_input/convo.svg b/tests_input/convo.svg new file mode 100644 index 0000000..7efff38 --- /dev/null +++ b/tests_input/convo.svg @@ -0,0 +1,43 @@ + + + + + + diff --git a/tests_pes/convo.pes b/tests_pes/convo.pes new file mode 100644 index 0000000..da19804 Binary files /dev/null and b/tests_pes/convo.pes differ