├── .gitignore ├── makefile ├── plot_segments.py ├── LICENSE ├── run_benchmark.py ├── analyze_benchmark.py ├── lsystems_v0.py ├── lsystems_v1.py ├── lsystems_v2.c ├── lsystems_v3.c ├── lsystems_v4.c ├── lsystems_v5.c ├── lsystems_v6.c ├── uncrustify.config └── lsystems_v7.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | lsystems_v? 3 | lsystems_debug_v? 4 | segment_plot.png 5 | segments.txt 6 | benchmark_results.* 7 | __pycache__ 8 | blog_images 9 | *.dSYM 10 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC = gcc-mp-8 2 | CFLAGS = -Wall -O3 -lm -std=c99 -fopenmp 3 | 4 | #CC = cc 5 | #CFLAGS = -Wall -O3 -lm -std=c99 -lpthread 6 | 7 | PROGS = lsystems_v2 lsystems_v3 lsystems_v4 lsystems_v5 lsystems_v6 lsystems_v7 8 | DEBUG_PROGS = $(PROGS:=_debug) 9 | 10 | all: $(PROGS) 11 | 12 | %: %.c makefile 13 | $(CC) $(CFLAGS) -o $@ $< 14 | 15 | %_debug: %.c makefile 16 | $(CC) -g $(CFLAGS) -o $@ $< 17 | 18 | .PHONY: clean 19 | 20 | debug: $(DEBUG_PROGS) 21 | 22 | clean: 23 | rm -f $(PROGS) $(DEBUG_PROGS) 24 | -------------------------------------------------------------------------------- /plot_segments.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import numpy as np 4 | import matplotlib.pyplot as plt 5 | from matplotlib.collections import LineCollection 6 | 7 | def plot_segments(segments, image_filename='segment_plot.png'): 8 | 9 | assert len(segments.shape) == 3 and segments.shape[1:] == (2, 2) 10 | 11 | lc = LineCollection(segments, color='b') 12 | 13 | ax = plt.axes() 14 | 15 | ax.add_collection(lc) 16 | ax.autoscale() 17 | 18 | plt.axis('equal') 19 | plt.axis('off') 20 | 21 | plt.savefig('segment_plot.png') 22 | print('wrote segment_plot.png') 23 | 24 | 25 | if __name__ == '__main__': 26 | 27 | segments = np.genfromtxt('segments.txt').reshape(-1, 2, 2) 28 | 29 | plot_segments(segments) 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Matt Zucker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /run_benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import subprocess 4 | import re 5 | 6 | def main(): 7 | 8 | test_cases = [ 9 | ('sierpinski_arrowhead', 17, 12), 10 | ('sierpinski_triangle', 16, 11), 11 | ('dragon_curve', 26, 18), 12 | ('barnsley_fern', 13, 9), 13 | ('sticks', 16, 11), 14 | ('hilbert', 13, 9), 15 | ('pentaplexity', 9, 6) 16 | ] 17 | 18 | test_programs = [ 19 | 'lsystems_v0.py', 20 | 'lsystems_v1.py', 21 | 'lsystems_v2', 22 | 'lsystems_v3', 23 | 'lsystems_v4', 24 | 'lsystems_v5', 25 | 'lsystems_v6', 26 | 'lsystems_v7' 27 | ] 28 | 29 | expected_python_slowdown = 100. 30 | 31 | expr = re.compile(r'generated (.*) segments in (.*) s \(') 32 | 33 | ostr = open('benchmark_results.txt', 'w') 34 | 35 | for prog in test_programs: 36 | 37 | first = True 38 | 39 | for name, max_depth_c, max_depth_py in test_cases: 40 | 41 | print(name, max_depth_c, max_depth_py) 42 | 43 | if prog.endswith('.py'): 44 | max_depth = max_depth_py 45 | args = [ 'python', prog ] 46 | else: 47 | max_depth = max_depth_c 48 | args = [ './' + prog ] 49 | 50 | args += [ '-x', '0', name, str(max_depth) ] 51 | 52 | if first: 53 | print('throwing away first run...') 54 | subprocess.run(args, capture_output=True, text=True) 55 | first = False 56 | 57 | print(args) 58 | 59 | result = subprocess.run(args, capture_output=True, text=True) 60 | 61 | output = result.stdout 62 | 63 | print(output) 64 | 65 | match = re.search(expr, output) 66 | 67 | assert match 68 | 69 | nseg = int(match.group(1)) 70 | time = float(match.group(2)) 71 | 72 | period = time / nseg 73 | ostr.write('{} {} {}\n'.format(prog, name, repr(period))) 74 | ostr.flush() 75 | 76 | if __name__ == '__main__': 77 | main() 78 | 79 | -------------------------------------------------------------------------------- /analyze_benchmark.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import matplotlib.pyplot as plt 4 | import numpy as np 5 | import os 6 | 7 | def main(): 8 | 9 | istr = open('benchmark_results.txt', 'r') 10 | 11 | results = dict() 12 | 13 | for line in istr: 14 | 15 | line = line.rstrip() 16 | 17 | if not line: 18 | continue 19 | 20 | prog, test_name, period = line.split() 21 | period = float(period) 22 | 23 | if prog not in results: 24 | results[prog] = dict() 25 | 26 | assert test_name not in results[prog] 27 | 28 | results[prog][test_name] = period 29 | 30 | test_names = set( next(iter(results.values())).keys() ) 31 | 32 | progs = list(sorted(results.keys())) 33 | 34 | nprogs = len(progs) 35 | 36 | versions = np.arange(nprogs) 37 | mean_times = np.zeros(nprogs) 38 | errs = np.zeros((2, nprogs)) 39 | 40 | plt.figure(num=None, figsize=(8, 4.03)) 41 | 42 | plt.grid(color=[.9, .9, .9], zorder=-1, linewidth=.5) 43 | 44 | for pidx, prog in enumerate(progs): 45 | presults = results[prog] 46 | assert set(presults.keys()) == test_names 47 | result_data = np.array(list(presults.values())) * 1e6 48 | mtime = np.power(result_data.prod(), 1.0/len(result_data)) 49 | mean_times[pidx] = mtime 50 | errs[0, pidx] = mtime - result_data.min() 51 | errs[1, pidx] = result_data.max() - mtime 52 | 53 | unit = 'μs' 54 | if mtime < 1: 55 | mtime *= 1e3 56 | unit = 'ns' 57 | print('{} {:.4g} {}'.format(prog, mtime, unit)) 58 | 59 | 60 | 61 | assert np.all(errs >= 0) 62 | 63 | is_c = np.array([not p.endswith('.py') for p in progs]) 64 | 65 | plt.errorbar(versions, 66 | mean_times, errs, fmt='-', capsize=2, 67 | color=[.6, .6, .6], ecolor='k', 68 | linewidth=0.5, elinewidth=0.5, zorder=1, 69 | barsabove=True) 70 | 71 | 72 | for i in range(2): 73 | mask = (is_c == bool(i)) 74 | label = 'Python versions' if i == 0 else 'C versions' 75 | plt.plot(versions[mask], mean_times[mask], '.', zorder=2, label=label) 76 | 77 | plt.legend(loc='upper right') 78 | plt.xlabel('program version') 79 | plt.ylabel('μs / segment') 80 | plt.title('L-System benchmark results') 81 | 82 | plt.gca().set_axisbelow(True) 83 | 84 | 85 | plt.yscale('log') 86 | #plt.show() 87 | 88 | plt.savefig('benchmark_results.png', dpi=200, facecolor=(1., 1., 1., 0.)) 89 | os.system('mogrify -trim -bordercolor transparent -border 10x10 benchmark_results.png') 90 | 91 | #plt.savefig('benchmark_results.png', dpi=200) 92 | #os.system('mogrify -trim -bordercolor White -border 20x20 benchmark_results.png') 93 | 94 | 95 | 96 | 97 | 98 | if __name__ == '__main__': 99 | main() 100 | -------------------------------------------------------------------------------- /lsystems_v0.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ###################################################################### 3 | # 4 | # lsystems_v0.py 5 | # Matt Zucker 6 | # 7 | ###################################################################### 8 | # 9 | # Based on documentation in https://en.wikipedia.org/wiki/L-system and 10 | # http://paulbourke.net/fractals/lsys/ 11 | 12 | import sys 13 | import argparse 14 | from datetime import datetime 15 | from collections import namedtuple 16 | import numpy as np 17 | 18 | from plot_segments import plot_segments 19 | 20 | LSystem = namedtuple('LSystem', 'start, rules, turn_angle_deg, draw_chars') 21 | 22 | # A few L-Systems found on pages linked above 23 | 24 | KNOWN_LSYSTEMS = { 25 | 26 | 'sierpinski_triangle': LSystem( 27 | start = 'F-G-G', 28 | rules = dict(F='F-G+F+G-F', G='GG'), 29 | turn_angle_deg = 120, 30 | draw_chars = None 31 | ), 32 | 33 | 'sierpinski_arrowhead': LSystem( 34 | start = 'A', 35 | rules = dict(A='B-A-B', B='A+B+A'), 36 | turn_angle_deg = 60, 37 | draw_chars = None 38 | ), 39 | 40 | 'dragon_curve': LSystem( 41 | start = 'FX', 42 | rules = dict(X='X+YF+', Y='-FX-Y'), 43 | turn_angle_deg = 90, 44 | draw_chars = None 45 | ), 46 | 47 | 'barnsley_fern': LSystem( 48 | start = 'X', 49 | rules = dict(X='F+[[X]-X]-F[-FX]+X', F='FF'), 50 | turn_angle_deg = 25, 51 | draw_chars = None 52 | ), 53 | 54 | 'sticks': LSystem( 55 | start = 'X', 56 | rules = dict(X='F[+X]F[-X]+X', F='FF'), 57 | turn_angle_deg = 20, 58 | draw_chars = 'F' 59 | ), 60 | 61 | 'hilbert': LSystem( 62 | start = 'L', 63 | rules = dict(L='+RF-LFL-FR+', R='-LF+RFR+FL-'), 64 | turn_angle_deg = 90, 65 | draw_chars = 'F' 66 | ), 67 | 68 | 'pentaplexity': LSystem( 69 | start = 'F++F++F++F++F', 70 | rules = dict(F='F++F++F+++++F-F++F'), 71 | turn_angle_deg = 36, 72 | draw_chars = None 73 | ) 74 | } 75 | 76 | ###################################################################### 77 | # make a big ol' string from an L-System starting from its start state 78 | # using repeated string replacement. 79 | 80 | def lsys_build_string(lsys, total_iterations): 81 | 82 | lstring = lsys.start 83 | 84 | rules = lsys.rules 85 | 86 | for i in range(total_iterations): 87 | 88 | output = '' 89 | 90 | for symbol in lstring: 91 | if symbol in rules: 92 | output += rules[symbol] 93 | else: 94 | output += symbol 95 | 96 | lstring = output 97 | 98 | return lstring 99 | 100 | ###################################################################### 101 | # take a string and turn it into a set of line segments, returned as 102 | # an n-by-2-by-2 array where each segment is represented as 103 | # 104 | # [(x0, y0), (x1, y1)] 105 | 106 | def lsys_segments_from_string(lsys, lstring): 107 | 108 | cur_pos = np.array([0., 0.]) 109 | cur_angle_deg = 0 110 | 111 | cur_state = ( cur_pos, cur_angle_deg ) 112 | 113 | # stack of pos, angle pairs 114 | stack = [] 115 | 116 | segments = [] 117 | 118 | for symbol in lstring: 119 | 120 | if symbol.isalpha(): 121 | 122 | if lsys.draw_chars is None or symbol in lsys.draw_chars: 123 | 124 | cur_theta = cur_angle_deg * np.pi / 180 125 | offset = np.array([np.cos(cur_theta), np.sin(cur_theta)]) 126 | new_pos = cur_pos + offset 127 | segments.append([cur_pos, new_pos]) 128 | cur_pos = new_pos 129 | 130 | elif symbol == '+': 131 | 132 | cur_angle_deg += lsys.turn_angle_deg 133 | 134 | elif symbol == '-': 135 | 136 | cur_angle_deg -= lsys.turn_angle_deg 137 | 138 | elif symbol == '[': 139 | 140 | stack.append( ( cur_pos, cur_angle_deg ) ) 141 | 142 | elif symbol == ']': 143 | 144 | cur_pos, cur_angle_deg = stack.pop() 145 | 146 | else: 147 | 148 | raise RuntimeError('invalid symbol:' + symbol) 149 | 150 | return np.array(segments) 151 | 152 | ###################################################################### 153 | # parse command-line options for this program 154 | 155 | def parse_options(): 156 | 157 | parser = argparse.ArgumentParser( 158 | description='simple Python L-system renderer') 159 | 160 | parser.add_argument('lname', metavar='LSYSTEM', nargs=1, 161 | help='name of desired L-system', 162 | type=str, 163 | choices=KNOWN_LSYSTEMS) 164 | 165 | parser.add_argument('total_iterations', metavar='ITERATIONS', nargs=1, 166 | help='number of iterations', type=int) 167 | 168 | parser.add_argument('-x', dest='max_segments', metavar='MAXSEGMENTS', 169 | type=int, default=100000, 170 | help='maximum number of segments to plot') 171 | 172 | parser.add_argument('-t', dest='text_only', action='store_true', 173 | help='use text output instead of PNG') 174 | 175 | opts = parser.parse_args() 176 | 177 | opts.lname = opts.lname[0] 178 | opts.total_iterations = opts.total_iterations[0] 179 | 180 | opts.lsys = KNOWN_LSYSTEMS[opts.lname] 181 | 182 | return opts 183 | 184 | ###################################################################### 185 | # main function 186 | 187 | def main(): 188 | 189 | opts = parse_options() 190 | 191 | # time segment generation 192 | start = datetime.now() 193 | 194 | lstring = lsys_build_string(opts.lsys, opts.total_iterations) 195 | 196 | segments = lsys_segments_from_string(opts.lsys, lstring) 197 | 198 | # print elapsed time 199 | elapsed = (datetime.now() - start).total_seconds() 200 | 201 | print('generated {} segments in {:.6f} s ({:.3f} us/segment)'.format( 202 | len(segments), elapsed, 1e6 * elapsed/len(segments))) 203 | 204 | if opts.max_segments >= 0 and len(segments) > opts.max_segments: 205 | print('...maximum of {} segments exceeded, skipping output!'.format( 206 | opts.max_segments)) 207 | return 208 | 209 | if opts.text_only: 210 | np.savetxt('segments.txt', segments.reshape(-1, 4)) 211 | else: 212 | plot_segments(segments) 213 | 214 | if __name__ == '__main__': 215 | main() 216 | 217 | 218 | 219 | -------------------------------------------------------------------------------- /lsystems_v1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ###################################################################### 3 | # 4 | # lsystems_v1.py 5 | # Matt Zucker 6 | # 7 | ###################################################################### 8 | # 9 | # Based on documentation in https://en.wikipedia.org/wiki/L-system and 10 | # http://paulbourke.net/fractals/lsys/ 11 | 12 | import sys 13 | import argparse 14 | from datetime import datetime 15 | from collections import namedtuple 16 | import numpy as np 17 | 18 | from plot_segments import plot_segments 19 | 20 | LSystem = namedtuple('LSystem', 'start, rules, turn_angle_deg, draw_chars') 21 | 22 | # A few L-Systems found on pages linked above 23 | 24 | KNOWN_LSYSTEMS = { 25 | 26 | 'sierpinski_triangle': LSystem( 27 | start = 'F-G-G', 28 | rules = dict(F='F-G+F+G-F', G='GG'), 29 | turn_angle_deg = 120, 30 | draw_chars = None 31 | ), 32 | 33 | 'sierpinski_arrowhead': LSystem( 34 | start = 'A', 35 | rules = dict(A='B-A-B', B='A+B+A'), 36 | turn_angle_deg = 60, 37 | draw_chars = None 38 | ), 39 | 40 | 'dragon_curve': LSystem( 41 | start = 'FX', 42 | rules = dict(X='X+YF+', Y='-FX-Y'), 43 | turn_angle_deg = 90, 44 | draw_chars = None 45 | ), 46 | 47 | 'barnsley_fern': LSystem( 48 | start = 'X', 49 | rules = dict(X='F+[[X]-X]-F[-FX]+X', F='FF'), 50 | turn_angle_deg = 25, 51 | draw_chars = None 52 | ), 53 | 54 | 'sticks': LSystem( 55 | start = 'X', 56 | rules = dict(X='F[+X]F[-X]+X', F='FF'), 57 | turn_angle_deg = 20, 58 | draw_chars = 'F' 59 | ), 60 | 61 | 'hilbert': LSystem( 62 | start = 'L', 63 | rules = dict(L='+RF-LFL-FR+', R='-LF+RFR+FL-'), 64 | turn_angle_deg = 90, 65 | draw_chars = 'F' 66 | ), 67 | 68 | 'pentaplexity': LSystem( 69 | start = 'F++F++F++F++F', 70 | rules = dict(F='F++F++F+++++F-F++F'), 71 | turn_angle_deg = 36, 72 | draw_chars = None 73 | ) 74 | } 75 | 76 | ###################################################################### 77 | # make a big ol' string from an L-System starting from its start state 78 | # using repeated string replacement. 79 | 80 | def lsys_build_string(lsys, total_iterations): 81 | 82 | lstring = lsys.start 83 | 84 | rules = lsys.rules 85 | 86 | for i in range(total_iterations): 87 | 88 | output = '' 89 | 90 | for symbol in lstring: 91 | if symbol in rules: 92 | output += rules[symbol] 93 | else: 94 | output += symbol 95 | 96 | lstring = output 97 | 98 | return lstring 99 | 100 | ###################################################################### 101 | # "draw" a single symbol from an L-System string 102 | # 103 | # stack is read-write 104 | # segments is for appending 105 | # cur_state is read and returned 106 | 107 | def _lsys_execute_symbol(lsys, symbol, cur_state, stack, segments): 108 | 109 | cur_pos, cur_angle_deg = cur_state 110 | 111 | if symbol.isalpha(): 112 | 113 | if lsys.draw_chars is None or symbol in lsys.draw_chars: 114 | 115 | cur_theta = cur_angle_deg * np.pi / 180 116 | offset = np.array([np.cos(cur_theta), np.sin(cur_theta)]) 117 | new_pos = cur_pos + offset 118 | segments.append([cur_pos, new_pos]) 119 | cur_pos = new_pos 120 | 121 | elif symbol == '+': 122 | 123 | cur_angle_deg += lsys.turn_angle_deg 124 | 125 | elif symbol == '-': 126 | 127 | cur_angle_deg -= lsys.turn_angle_deg 128 | 129 | elif symbol == '[': 130 | 131 | stack.append( ( cur_pos, cur_angle_deg ) ) 132 | 133 | elif symbol == ']': 134 | 135 | return stack.pop() 136 | 137 | else: 138 | 139 | raise RuntimeError('invalid symbol:' + symbol) 140 | 141 | return cur_pos, cur_angle_deg 142 | 143 | 144 | ###################################################################### 145 | # take a string and turn it into a set of line segments, returned as 146 | # an n-by-2-by-2 array where each segment is represented as 147 | # 148 | # [(x0, y0), (x1, y1)] 149 | 150 | def lsys_segments_from_string(lsys, lstring): 151 | 152 | cur_pos = np.array([0., 0.]) 153 | cur_angle_deg = 0 154 | 155 | cur_state = ( cur_pos, cur_angle_deg ) 156 | 157 | # stack of pos, angle pairs 158 | stack = [] 159 | 160 | segments = [] 161 | 162 | for symbol in lstring: 163 | cur_state = _lsys_execute_symbol(lsys, symbol, cur_state, 164 | stack, segments) 165 | 166 | return np.array(segments) 167 | 168 | ###################################################################### 169 | # build up segment list recursively - don't call this function 170 | # directly, use lsys_segments_recursive instead 171 | 172 | def _lsys_segments_r(lsys, s, 173 | remaining_iterations, 174 | cur_state, 175 | state_stack, 176 | segments): 177 | 178 | # for each symbol in input 179 | for symbol in s: 180 | 181 | # see if we can run a replacement rule for this symbol 182 | if remaining_iterations > 0 and symbol in lsys.rules: 183 | 184 | # get the replacement 185 | replacement = lsys.rules[symbol] 186 | 187 | # recursively call this function with fewer remaining steps 188 | cur_state = _lsys_segments_r(lsys, replacement, 189 | remaining_iterations-1, 190 | cur_state, 191 | state_stack, segments) 192 | 193 | else: # execute symbol directly 194 | 195 | cur_state = _lsys_execute_symbol(lsys, symbol, cur_state, 196 | state_stack, segments) 197 | 198 | return cur_state 199 | 200 | ###################################################################### 201 | # build up segment list recursively by calling helper function above 202 | 203 | def lsys_segments_recursive(lsys, total_iterations): 204 | 205 | cur_pos = np.array([0., 0.]) 206 | cur_angle_deg = 0 207 | 208 | cur_state = (cur_pos, cur_angle_deg) 209 | state_stack = [] 210 | 211 | segments = [] 212 | 213 | s = lsys.start 214 | 215 | _lsys_segments_r(lsys, s, 216 | total_iterations, 217 | cur_state, 218 | state_stack, 219 | segments) 220 | 221 | return np.array(segments) 222 | 223 | ###################################################################### 224 | # parse command-line options for this program 225 | 226 | def parse_options(): 227 | 228 | parser = argparse.ArgumentParser( 229 | description='simple Python L-system renderer') 230 | 231 | parser.add_argument('lname', metavar='LSYSTEM', nargs=1, 232 | help='name of desired L-system', 233 | type=str, 234 | choices=KNOWN_LSYSTEMS) 235 | 236 | parser.add_argument('total_iterations', metavar='ITERATIONS', nargs=1, 237 | help='number of iterations', type=int) 238 | 239 | parser.add_argument('-x', dest='max_segments', metavar='MAXSEGMENTS', 240 | type=int, default=100000, 241 | help='maximum number of segments to plot') 242 | 243 | parser.add_argument('-t', dest='text_only', action='store_true', 244 | help='use text output instead of PNG') 245 | 246 | parser.add_argument('-s', dest='use_recursion', action='store_false', 247 | default=True, 248 | help='use string building method (default)') 249 | 250 | parser.add_argument('-r', dest='use_recursion', action='store_true', 251 | default=True, 252 | help='use recursive method') 253 | 254 | opts = parser.parse_args() 255 | 256 | opts.lname = opts.lname[0] 257 | opts.total_iterations = opts.total_iterations[0] 258 | 259 | opts.lsys = KNOWN_LSYSTEMS[opts.lname] 260 | 261 | if opts.use_recursion: 262 | print('using recursive method') 263 | else: 264 | print('using string building method') 265 | 266 | return opts 267 | 268 | ###################################################################### 269 | # main function 270 | 271 | def main(): 272 | 273 | opts = parse_options() 274 | 275 | # time segment generation 276 | start = datetime.now() 277 | 278 | if opts.use_recursion: 279 | 280 | segments = lsys_segments_recursive(opts.lsys, opts.total_iterations) 281 | 282 | else: 283 | 284 | lstring = lsys_build_string(opts.lsys, opts.total_iterations) 285 | 286 | segments = lsys_segments_from_string(opts.lsys, lstring) 287 | 288 | # print elapsed time 289 | elapsed = (datetime.now() - start).total_seconds() 290 | 291 | print('generated {} segments in {:.6f} s ({:.3f} us/segment)'.format( 292 | len(segments), elapsed, 1e6 * elapsed/len(segments))) 293 | 294 | if opts.max_segments >= 0 and len(segments) > opts.max_segments: 295 | print('...maximum of {} segments exceeded, skipping output!'.format( 296 | opts.max_segments)) 297 | return 298 | 299 | if opts.text_only: 300 | np.savetxt('segments.txt', segments.reshape(-1, 4)) 301 | else: 302 | plot_segments(segments) 303 | 304 | if __name__ == '__main__': 305 | main() 306 | 307 | 308 | 309 | -------------------------------------------------------------------------------- /lsystems_v2.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // lsystems_v2.c 4 | // Matt Zucker 5 | // 6 | ////////////////////////////////////////////////////////////////////// 7 | // 8 | // Based on documentation in https://en.wikipedia.org/wiki/L-system and 9 | // http://paulbourke.net/fractals/lsys/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | ////////////////////////////////////////////////////////////////////// 19 | // for benchmarking 20 | 21 | double get_time_as_double(void); 22 | 23 | ////////////////////////////////////////////////////////////////////// 24 | // dynamic array 25 | 26 | // dynamic array data type 27 | typedef struct darray { 28 | 29 | size_t elem_size; 30 | size_t capacity; 31 | size_t count; 32 | 33 | unsigned char* data; 34 | 35 | } darray_t; 36 | 37 | // dynamic array functions 38 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity); 39 | void darray_resize(darray_t* darray, size_t new_count); 40 | void darray_extend(darray_t* darray, const void* elements, size_t count); 41 | void darray_push_back(darray_t* darray, const void* elem); 42 | void darray_pop_back(darray_t* darray, void* elem); 43 | void* darray_elem_ptr(darray_t* darray, size_t idx); 44 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx); 45 | void darray_get(const darray_t* darray, size_t idx, void* dst); 46 | void darray_set(darray_t* darray, size_t idx, const void* src); 47 | void darray_clear(darray_t* darray); 48 | void darray_destroy(darray_t* darray); 49 | 50 | ////////////////////////////////////////////////////////////////////// 51 | // geometry utils 52 | 53 | // 2D point 54 | typedef struct point2d { 55 | float x, y; 56 | } point2d_t; 57 | 58 | ////////////////////////////////////////////////////////////////////// 59 | // L-System types/functions 60 | 61 | // misc enums 62 | enum { 63 | LSYS_MAX_RULES = 128, 64 | LSYS_MAX_CYCLE_LENGTH = 256, 65 | LSYS_INIT_STRING_CAPACITY = 4096, 66 | LSYS_INIT_STATES_CAPACITY = 64, 67 | LSYS_INIT_SEGMENTS_CAPACITY = 1024 68 | }; 69 | 70 | // line segment is 2 points 71 | typedef struct lsys_segment { 72 | point2d_t p0, p1; 73 | } lsys_segment_t; 74 | 75 | // rule tagged with string length for string replacement 76 | typedef struct lsys_sized_string { 77 | const char* replacement; 78 | size_t length; 79 | } lsys_sized_string_t; 80 | 81 | // L-System datatype 82 | typedef struct lsystem { 83 | 84 | const char* name; 85 | const char* start; 86 | lsys_sized_string_t rules[LSYS_MAX_RULES]; 87 | unsigned char draw_chars[LSYS_MAX_RULES]; 88 | double turn_angle_rad; 89 | 90 | } lsys_t; 91 | 92 | // lsystem character + replacement pair, for defining L-Systems 93 | typedef struct lsys_rule_def { 94 | char symbol; 95 | const char* replacement; 96 | } lsys_rule_def_t; 97 | 98 | typedef struct lsys_state { 99 | point2d_t pos; 100 | float angle; 101 | } lsys_state_t; 102 | 103 | static const lsys_state_t LSYS_START_STATE = { { 0.f, 0.f, }, 0.f }; 104 | 105 | void lsys_create(lsys_t* lsys, 106 | const char* name, 107 | const char* start, 108 | lsys_rule_def_t const rules[], 109 | double turn_angle_deg, 110 | const char* draw_chars); 111 | 112 | void lsys_print(const lsys_t* lsys); 113 | 114 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations); 115 | 116 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 117 | const char* lstring); 118 | 119 | ////////////////////////////////////////////////////////////////////// 120 | // set up some known L-Systems 121 | 122 | enum { 123 | LSYS_SIERPINSKI_ARROWHEAD = 0, // depth 17 takes ~5 sec 124 | LSYS_SIERPINSKI_TRIANGLE, // depth 16 takes ~3 sec 125 | LSYS_DRAGON_CURVE, // depth 26 takes ~6 sec 126 | LSYS_BARNSLEY_FERN, // depth 13 takes ~7 sec 127 | LSYS_STICKS, // depth 16 takes ~4 sec 128 | LSYS_HILBERT, // depth 13 takes ~3 sec 129 | LSYS_PENTAPLEXITY, // depth 9 takes ~2 sec 130 | NUM_KNOWN_LSYSTEMS 131 | }; 132 | 133 | lsys_t KNOWN_LSYSTEMS[NUM_KNOWN_LSYSTEMS]; 134 | 135 | void initialize_known_lsystems(void); 136 | 137 | ////////////////////////////////////////////////////////////////////// 138 | // options for running this program 139 | 140 | typedef struct options { 141 | lsys_t* lsys; 142 | size_t total_iterations; 143 | size_t max_segments; 144 | } options_t; 145 | 146 | void parse_options(int argc, char** argv, options_t* opts); 147 | 148 | ////////////////////////////////////////////////////////////////////// 149 | 150 | double get_time_as_double(void) { 151 | 152 | struct timespec tp; 153 | 154 | clock_gettime(CLOCK_REALTIME, &tp); 155 | 156 | return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9; 157 | 158 | } 159 | 160 | ////////////////////////////////////////////////////////////////////// 161 | 162 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity) { 163 | 164 | size_t alloc_size = elem_size * capacity; 165 | 166 | darray->elem_size = elem_size; 167 | darray->count = 0; 168 | darray->capacity = capacity; 169 | darray->data = malloc(alloc_size); 170 | 171 | } 172 | 173 | void darray_resize(darray_t* darray, size_t new_count) { 174 | 175 | if (new_count > darray->capacity) { 176 | 177 | size_t new_capacity = darray->capacity; 178 | 179 | while (new_capacity <= new_count) { 180 | new_capacity *= 2; 181 | } 182 | 183 | size_t alloc_size = darray->elem_size * new_capacity; 184 | 185 | darray->data = realloc(darray->data, alloc_size); 186 | darray->capacity = new_capacity; 187 | 188 | } 189 | 190 | darray->count = new_count; 191 | 192 | } 193 | 194 | void darray_extend(darray_t* darray, const void* elements, size_t count) { 195 | 196 | size_t offset = darray->elem_size * darray->count; 197 | 198 | darray_resize(darray, darray->count + count); 199 | 200 | memcpy(darray->data + offset, elements, count*darray->elem_size); 201 | 202 | } 203 | 204 | void darray_push_back(darray_t* darray, const void* elem) { 205 | darray_extend(darray, elem, 1); 206 | } 207 | 208 | void* darray_elem_ptr(darray_t* darray, size_t idx) { 209 | return darray->data + idx*darray->elem_size; 210 | } 211 | 212 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx) { 213 | return darray->data + idx*darray->elem_size; 214 | } 215 | 216 | void darray_get(const darray_t* darray, size_t idx, void* dst) { 217 | memcpy(dst, darray_const_elem_ptr(darray, idx), darray->elem_size); 218 | } 219 | 220 | void darray_set(darray_t* darray, size_t idx, const void* src) { 221 | memcpy(darray_elem_ptr(darray, idx), src, darray->elem_size); 222 | } 223 | 224 | void darray_pop_back(darray_t* darray, void* dst) { 225 | darray->count -= 1; 226 | size_t offset = darray->count * darray->elem_size; 227 | memcpy(dst, (const void*)darray->data + offset, darray->elem_size); 228 | } 229 | 230 | void darray_clear(darray_t* darray) { 231 | darray->count = 0; 232 | } 233 | 234 | void darray_destroy(darray_t* darray) { 235 | free(darray->data); 236 | memset(darray, 0, sizeof(darray_t)); 237 | } 238 | 239 | ////////////////////////////////////////////////////////////////////// 240 | 241 | void lsys_create(lsys_t* lsys, 242 | const char* name, 243 | const char* start, 244 | lsys_rule_def_t const rules[], 245 | double turn_angle_deg, 246 | const char* draw_chars) { 247 | 248 | lsys->name = name; 249 | lsys->start = start; 250 | 251 | memset(lsys->rules, 0, sizeof(lsys->rules)); 252 | memset(lsys->draw_chars, 0, sizeof(lsys->draw_chars)); 253 | 254 | for (const lsys_rule_def_t* src_rule=rules; src_rule->symbol; ++src_rule) { 255 | lsys_sized_string_t* dst_rule = lsys->rules + (int)src_rule->symbol; 256 | dst_rule->replacement = src_rule->replacement; 257 | dst_rule->length = strlen(src_rule->replacement); 258 | } 259 | 260 | lsys->turn_angle_rad = turn_angle_deg * M_PI / 180.f; 261 | 262 | if (draw_chars) { 263 | lsys->draw_chars[0] = 1; 264 | for (const char* c=draw_chars; *c; ++c) { 265 | lsys->draw_chars[(int)*c] = 1; 266 | } 267 | } 268 | 269 | } 270 | 271 | void lsys_print(const lsys_t* lsys) { 272 | 273 | printf("%s:\n", lsys->name); 274 | printf(" start: %s\n", lsys->start); 275 | printf(" rules:\n"); 276 | for (int i=0; irules[i].replacement) { 278 | printf(" %c -> %s\n", i, lsys->rules[i].replacement); 279 | } 280 | } 281 | printf(" turn_angle_deg: %g\n", lsys->turn_angle_rad * 180.f / M_PI); 282 | printf("\n"); 283 | 284 | } 285 | 286 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations) { 287 | 288 | darray_t string_darrays[2]; 289 | 290 | for (int i=0; i<2; ++i) { 291 | darray_create(string_darrays + i, sizeof(char), 292 | LSYS_INIT_STRING_CAPACITY); 293 | } 294 | 295 | int cur_idx = 0; 296 | 297 | darray_extend(string_darrays + cur_idx, 298 | lsys->start, 299 | strlen(lsys->start)); 300 | 301 | for (int i=0; idata; 311 | const char* end = start + src_darray->count; 312 | 313 | for (const char* c=start; c!=end; ++c) { 314 | 315 | const lsys_sized_string_t* rule = lsys->rules + (int)*c; 316 | 317 | if (rule->replacement) { 318 | 319 | darray_extend(dst_darray, 320 | rule->replacement, 321 | rule->length); 322 | 323 | } else { 324 | 325 | darray_push_back(dst_darray, c); 326 | 327 | } 328 | 329 | } 330 | 331 | cur_idx = next_idx; 332 | 333 | } 334 | 335 | const char nul = '\0'; 336 | darray_push_back(string_darrays + cur_idx, &nul); 337 | 338 | darray_destroy(string_darrays + (1 - cur_idx)); 339 | 340 | return (char*)string_darrays[cur_idx].data; 341 | 342 | } 343 | 344 | void _lsys_execute_symbol(const lsys_t* lsys, 345 | const char symbol, 346 | darray_t* segments, 347 | lsys_state_t* state, 348 | darray_t* state_stack) { 349 | 350 | if (isalpha(symbol)) { 351 | 352 | if (lsys->draw_chars[0] && !lsys->draw_chars[(int)symbol]) { 353 | return; 354 | } 355 | 356 | float c = cosf(state->angle); 357 | float s = sinf(state->angle); 358 | 359 | float xnew = state->pos.x + c; 360 | float ynew = state->pos.y + s; 361 | 362 | lsys_segment_t seg = { { state->pos.x, state->pos.y}, 363 | { xnew, ynew } }; 364 | 365 | darray_push_back(segments, &seg); 366 | 367 | state->pos.x = xnew; 368 | state->pos.y = ynew; 369 | 370 | } else if (symbol == '+' || symbol == '-') { 371 | 372 | float delta = ( (symbol == '+') ? 373 | lsys->turn_angle_rad : -lsys->turn_angle_rad ); 374 | 375 | state->angle += delta; 376 | 377 | } else if (symbol == '[') { 378 | 379 | darray_push_back(state_stack, state); 380 | 381 | } else if (symbol == ']') { 382 | 383 | darray_pop_back(state_stack, state); 384 | 385 | } else { 386 | 387 | fprintf(stderr, "invalid character in string: %c\n", symbol); 388 | exit(1); 389 | 390 | } 391 | 392 | } 393 | 394 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 395 | const char* lstring) { 396 | 397 | darray_t* segments = malloc(sizeof(darray_t)); 398 | darray_create(segments, sizeof(lsys_segment_t), 399 | LSYS_INIT_SEGMENTS_CAPACITY); 400 | 401 | darray_t state_stack; 402 | darray_create(&state_stack, sizeof(lsys_state_t), 403 | LSYS_INIT_STATES_CAPACITY); 404 | 405 | lsys_state_t cur_state = LSYS_START_STATE; 406 | 407 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 408 | 409 | _lsys_execute_symbol(lsys, *psymbol, segments, 410 | &cur_state, &state_stack); 411 | 412 | } 413 | 414 | darray_destroy(&state_stack); 415 | 416 | return segments; 417 | 418 | } 419 | 420 | ////////////////////////////////////////////////////////////////////// 421 | // definitions for L-Systems from websites listed at top of file 422 | 423 | void initialize_known_lsystems(void) { 424 | 425 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_TRIANGLE, 426 | "sierpinski_triangle", "F-G-G", 427 | (lsys_rule_def_t[]){ 428 | { 'F', "F-G+F+G-F" }, 429 | { 'G', "GG" }, 430 | { 0, 0 } 431 | }, 120, NULL); 432 | 433 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_ARROWHEAD, 434 | "sierpinski_arrowhead", "A", 435 | (lsys_rule_def_t[]){ 436 | { 'A', "B-A-B" }, 437 | { 'B', "A+B+A" }, 438 | { 0, 0 } 439 | }, 60, NULL); 440 | 441 | lsys_create(KNOWN_LSYSTEMS + LSYS_DRAGON_CURVE, 442 | "dragon_curve", "FX", 443 | (lsys_rule_def_t[]){ 444 | { 'X', "X+YF+" }, 445 | { 'Y', "-FX-Y" }, 446 | { 0, 0 } 447 | }, 90, NULL); 448 | 449 | lsys_create(KNOWN_LSYSTEMS + LSYS_BARNSLEY_FERN, 450 | "barnsley_fern", "X", 451 | (lsys_rule_def_t[]){ 452 | { 'X', "F+[[X]-X]-F[-FX]+X" }, 453 | { 'F', "FF" }, 454 | { 0, 0 } 455 | }, 25, NULL); 456 | 457 | lsys_create(KNOWN_LSYSTEMS + LSYS_STICKS, 458 | "sticks", "X", 459 | (lsys_rule_def_t[]){ 460 | { 'X', "F[+X]F[-X]+X" }, 461 | { 'F', "FF" }, 462 | { 0, 0 } 463 | }, 20, "F"); 464 | 465 | lsys_create(KNOWN_LSYSTEMS + LSYS_HILBERT, 466 | "hilbert", "L", 467 | (lsys_rule_def_t[]){ 468 | { 'L', "+RF-LFL-FR+" }, 469 | { 'R', "-LF+RFR+FL-" }, 470 | { 0, 0 } 471 | }, 90, "F"); 472 | 473 | lsys_create(KNOWN_LSYSTEMS + LSYS_PENTAPLEXITY, 474 | "pentaplexity", "F++F++F++F++F", 475 | (lsys_rule_def_t[]){ 476 | { 'F', "F++F++F+++++F-F++F" }, 477 | { 0, 0 } 478 | }, 36, NULL); 479 | 480 | } 481 | 482 | ////////////////////////////////////////////////////////////////////// 483 | 484 | void parse_options(int argc, char** argv, options_t* opts) { 485 | 486 | int ok = 1; 487 | 488 | memset(opts, 0, sizeof(options_t)); 489 | opts->max_segments = 100000; 490 | 491 | int i=1; 492 | int required_count = 0; 493 | 494 | for (; ilsys = KNOWN_LSYSTEMS + j; 508 | break; 509 | } 510 | } 511 | 512 | if (!ok) { break; } 513 | 514 | } else if (required_count == 1) { 515 | 516 | int d; 517 | 518 | if (sscanf(arg, "%d", &d) != 1 || d <= 0) { 519 | ok = 0; 520 | break; 521 | } 522 | 523 | opts->total_iterations = d; 524 | 525 | } else { 526 | 527 | ok = 0; 528 | break; 529 | 530 | } 531 | 532 | ++required_count; 533 | 534 | } else if (!strcmp(arg, "-x")) { 535 | 536 | if (++i == argc) { ok = 0; break; } 537 | 538 | int d; 539 | 540 | if (sscanf(argv[i], "%d", &d) != 1) { 541 | ok = 0; break; 542 | } 543 | 544 | if (d >= -1) { 545 | opts->max_segments = (size_t)d; 546 | } else { 547 | ok = 0; 548 | break; 549 | } 550 | 551 | } else { 552 | 553 | fprintf(stderr, "error: unrecognized option %s\n\n", arg); 554 | 555 | ok = 0; 556 | break; 557 | 558 | } 559 | 560 | } 561 | 562 | if (!ok || !opts->lsys || !opts->total_iterations) { 563 | printf("usage: %s [options] LSYSTEM ITERATIONS\n" 564 | "\n" 565 | "where LSYSTEM is one of:\n", argv[0]); 566 | for (int j=0; jlsys); 579 | 580 | } 581 | 582 | ////////////////////////////////////////////////////////////////////// 583 | // main program 584 | 585 | int main(int argc, char** argv) { 586 | 587 | // initialize lsystems 588 | initialize_known_lsystems(); 589 | 590 | // parse command-line options 591 | options_t opts; 592 | parse_options(argc, argv, &opts); 593 | 594 | //////////////////////////////////////////////////////////// 595 | // now get the segments 596 | 597 | double start = get_time_as_double(); 598 | 599 | darray_t* segments; 600 | 601 | char* lstring = lsys_build_string(opts.lsys, 602 | opts.total_iterations); 603 | 604 | segments = lsys_segments_from_string(opts.lsys, lstring); 605 | 606 | free(lstring); 607 | 608 | double elapsed = get_time_as_double() - start; 609 | printf("generated %d segments in %.6f s (%.3f ns/segment).\n", 610 | (int)segments->count, elapsed, 1e9 * elapsed/segments->count); 611 | 612 | //////////////////////////////////////////////////////////// 613 | // either output or not 614 | 615 | if (segments->count > opts.max_segments) { 616 | 617 | printf("...maximum of %d segments exceeded, skipping output!\n", 618 | (int)opts.max_segments); 619 | 620 | } else { 621 | 622 | const lsys_segment_t* segment = (const lsys_segment_t*)segments->data; 623 | const lsys_segment_t* end = segment + segments->count; 624 | 625 | FILE* outfile = fopen("segments.txt", "w"); 626 | 627 | for ( ; segment != end; ++segment) { 628 | fprintf(outfile, "%g %g %g %g\n", 629 | segment->p0.x, segment->p0.y, 630 | segment->p1.x, segment->p1.y); 631 | } 632 | 633 | fclose(outfile); 634 | 635 | printf("wrote segments.txt\n"); 636 | 637 | } 638 | 639 | //////////////////////////////////////////////////////////// 640 | // clean up 641 | 642 | darray_destroy(segments); 643 | free(segments); 644 | 645 | return 0; 646 | 647 | } 648 | -------------------------------------------------------------------------------- /lsystems_v3.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // lsystems_v3.c 4 | // Matt Zucker 5 | // 6 | ////////////////////////////////////////////////////////////////////// 7 | // 8 | // Based on documentation in https://en.wikipedia.org/wiki/L-system and 9 | // http://paulbourke.net/fractals/lsys/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | ////////////////////////////////////////////////////////////////////// 19 | // for benchmarking 20 | 21 | double get_time_as_double(void); 22 | 23 | ////////////////////////////////////////////////////////////////////// 24 | // dynamic array 25 | 26 | // dynamic array data type 27 | typedef struct darray { 28 | 29 | size_t elem_size; 30 | size_t capacity; 31 | size_t count; 32 | 33 | unsigned char* data; 34 | 35 | } darray_t; 36 | 37 | // dynamic array functions 38 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity); 39 | void darray_resize(darray_t* darray, size_t new_count); 40 | void darray_extend(darray_t* darray, const void* elements, size_t count); 41 | void darray_push_back(darray_t* darray, const void* elem); 42 | void darray_pop_back(darray_t* darray, void* elem); 43 | void* darray_elem_ptr(darray_t* darray, size_t idx); 44 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx); 45 | void darray_get(const darray_t* darray, size_t idx, void* dst); 46 | void darray_set(darray_t* darray, size_t idx, const void* src); 47 | void darray_clear(darray_t* darray); 48 | void darray_destroy(darray_t* darray); 49 | 50 | ////////////////////////////////////////////////////////////////////// 51 | // geometry utils 52 | 53 | // 2D point 54 | typedef struct point2d { 55 | float x, y; 56 | } point2d_t; 57 | 58 | ////////////////////////////////////////////////////////////////////// 59 | // L-System types/functions 60 | 61 | // misc enums 62 | enum { 63 | LSYS_MAX_RULES = 128, 64 | LSYS_MAX_CYCLE_LENGTH = 256, 65 | LSYS_INIT_STRING_CAPACITY = 4096, 66 | LSYS_INIT_STATES_CAPACITY = 64, 67 | LSYS_INIT_SEGMENTS_CAPACITY = 1024 68 | }; 69 | 70 | // line segment is 2 points 71 | typedef struct lsys_segment { 72 | point2d_t p0, p1; 73 | } lsys_segment_t; 74 | 75 | // rule tagged with string length for string replacement 76 | typedef struct lsys_sized_string { 77 | const char* replacement; 78 | size_t length; 79 | } lsys_sized_string_t; 80 | 81 | // L-System datatype 82 | typedef struct lsystem { 83 | 84 | const char* name; 85 | const char* start; 86 | lsys_sized_string_t rules[LSYS_MAX_RULES]; 87 | unsigned char draw_chars[LSYS_MAX_RULES]; 88 | double turn_angle_rad; 89 | 90 | } lsys_t; 91 | 92 | // lsystem character + replacement pair, for defining L-Systems 93 | typedef struct lsys_rule_def { 94 | char symbol; 95 | const char* replacement; 96 | } lsys_rule_def_t; 97 | 98 | typedef struct lsys_state { 99 | point2d_t pos; 100 | float angle; 101 | } lsys_state_t; 102 | 103 | static const lsys_state_t LSYS_START_STATE = { { 0.f, 0.f, }, 0.f }; 104 | 105 | void lsys_create(lsys_t* lsys, 106 | const char* name, 107 | const char* start, 108 | lsys_rule_def_t const rules[], 109 | double turn_angle_deg, 110 | const char* draw_chars); 111 | 112 | void lsys_print(const lsys_t* lsys); 113 | 114 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations); 115 | 116 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 117 | const char* lstring); 118 | 119 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 120 | size_t total_iterations); 121 | 122 | ////////////////////////////////////////////////////////////////////// 123 | // set up some known L-Systems 124 | 125 | enum { 126 | LSYS_SIERPINSKI_ARROWHEAD = 0, // depth 17 takes ~5 sec 127 | LSYS_SIERPINSKI_TRIANGLE, // depth 16 takes ~3 sec 128 | LSYS_DRAGON_CURVE, // depth 26 takes ~6 sec 129 | LSYS_BARNSLEY_FERN, // depth 13 takes ~7 sec 130 | LSYS_STICKS, // depth 16 takes ~4 sec 131 | LSYS_HILBERT, // depth 13 takes ~3 sec 132 | LSYS_PENTAPLEXITY, // depth 9 takes ~2 sec 133 | NUM_KNOWN_LSYSTEMS 134 | }; 135 | 136 | lsys_t KNOWN_LSYSTEMS[NUM_KNOWN_LSYSTEMS]; 137 | 138 | void initialize_known_lsystems(void); 139 | 140 | ////////////////////////////////////////////////////////////////////// 141 | // options for running this program 142 | 143 | typedef enum lsys_method { 144 | LSYS_METHOD_RECURSION, 145 | LSYS_METHOD_STRING 146 | } lsys_method_t; 147 | 148 | typedef struct options { 149 | lsys_t* lsys; 150 | size_t total_iterations; 151 | size_t max_segments; 152 | lsys_method_t method; 153 | } options_t; 154 | 155 | void parse_options(int argc, char** argv, options_t* opts); 156 | 157 | ////////////////////////////////////////////////////////////////////// 158 | 159 | double get_time_as_double(void) { 160 | 161 | struct timespec tp; 162 | 163 | clock_gettime(CLOCK_REALTIME, &tp); 164 | 165 | return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9; 166 | 167 | } 168 | 169 | ////////////////////////////////////////////////////////////////////// 170 | 171 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity) { 172 | 173 | size_t alloc_size = elem_size * capacity; 174 | 175 | darray->elem_size = elem_size; 176 | darray->count = 0; 177 | darray->capacity = capacity; 178 | darray->data = malloc(alloc_size); 179 | 180 | } 181 | 182 | void darray_resize(darray_t* darray, size_t new_count) { 183 | 184 | if (new_count > darray->capacity) { 185 | 186 | size_t new_capacity = darray->capacity; 187 | 188 | while (new_capacity <= new_count) { 189 | new_capacity *= 2; 190 | } 191 | 192 | size_t alloc_size = darray->elem_size * new_capacity; 193 | 194 | darray->data = realloc(darray->data, alloc_size); 195 | darray->capacity = new_capacity; 196 | 197 | } 198 | 199 | darray->count = new_count; 200 | 201 | } 202 | 203 | void darray_extend(darray_t* darray, const void* elements, size_t count) { 204 | 205 | size_t offset = darray->elem_size * darray->count; 206 | 207 | darray_resize(darray, darray->count + count); 208 | 209 | memcpy(darray->data + offset, elements, count*darray->elem_size); 210 | 211 | } 212 | 213 | void darray_push_back(darray_t* darray, const void* elem) { 214 | darray_extend(darray, elem, 1); 215 | } 216 | 217 | void* darray_elem_ptr(darray_t* darray, size_t idx) { 218 | return darray->data + idx*darray->elem_size; 219 | } 220 | 221 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx) { 222 | return darray->data + idx*darray->elem_size; 223 | } 224 | 225 | void darray_get(const darray_t* darray, size_t idx, void* dst) { 226 | memcpy(dst, darray_const_elem_ptr(darray, idx), darray->elem_size); 227 | } 228 | 229 | void darray_set(darray_t* darray, size_t idx, const void* src) { 230 | memcpy(darray_elem_ptr(darray, idx), src, darray->elem_size); 231 | } 232 | 233 | void darray_pop_back(darray_t* darray, void* dst) { 234 | darray->count -= 1; 235 | size_t offset = darray->count * darray->elem_size; 236 | memcpy(dst, (const void*)darray->data + offset, darray->elem_size); 237 | } 238 | 239 | void darray_clear(darray_t* darray) { 240 | darray->count = 0; 241 | } 242 | 243 | void darray_destroy(darray_t* darray) { 244 | free(darray->data); 245 | memset(darray, 0, sizeof(darray_t)); 246 | } 247 | 248 | ////////////////////////////////////////////////////////////////////// 249 | 250 | void lsys_create(lsys_t* lsys, 251 | const char* name, 252 | const char* start, 253 | lsys_rule_def_t const rules[], 254 | double turn_angle_deg, 255 | const char* draw_chars) { 256 | 257 | lsys->name = name; 258 | lsys->start = start; 259 | 260 | memset(lsys->rules, 0, sizeof(lsys->rules)); 261 | memset(lsys->draw_chars, 0, sizeof(lsys->draw_chars)); 262 | 263 | for (const lsys_rule_def_t* src_rule=rules; src_rule->symbol; ++src_rule) { 264 | lsys_sized_string_t* dst_rule = lsys->rules + (int)src_rule->symbol; 265 | dst_rule->replacement = src_rule->replacement; 266 | dst_rule->length = strlen(src_rule->replacement); 267 | } 268 | 269 | lsys->turn_angle_rad = turn_angle_deg * M_PI / 180.f; 270 | 271 | if (draw_chars) { 272 | lsys->draw_chars[0] = 1; 273 | for (const char* c=draw_chars; *c; ++c) { 274 | lsys->draw_chars[(int)*c] = 1; 275 | } 276 | } 277 | 278 | } 279 | 280 | void lsys_print(const lsys_t* lsys) { 281 | 282 | printf("%s:\n", lsys->name); 283 | printf(" start: %s\n", lsys->start); 284 | printf(" rules:\n"); 285 | for (int i=0; irules[i].replacement) { 287 | printf(" %c -> %s\n", i, lsys->rules[i].replacement); 288 | } 289 | } 290 | printf(" turn_angle_deg: %g\n", lsys->turn_angle_rad * 180.f / M_PI); 291 | printf("\n"); 292 | 293 | } 294 | 295 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations) { 296 | 297 | darray_t string_darrays[2]; 298 | 299 | for (int i=0; i<2; ++i) { 300 | darray_create(string_darrays + i, sizeof(char), 301 | LSYS_INIT_STRING_CAPACITY); 302 | } 303 | 304 | int cur_idx = 0; 305 | 306 | darray_extend(string_darrays + cur_idx, 307 | lsys->start, 308 | strlen(lsys->start)); 309 | 310 | for (int i=0; idata; 320 | const char* end = start + src_darray->count; 321 | 322 | for (const char* c=start; c!=end; ++c) { 323 | 324 | const lsys_sized_string_t* rule = lsys->rules + (int)*c; 325 | 326 | if (rule->replacement) { 327 | 328 | darray_extend(dst_darray, 329 | rule->replacement, 330 | rule->length); 331 | 332 | } else { 333 | 334 | darray_push_back(dst_darray, c); 335 | 336 | } 337 | 338 | } 339 | 340 | cur_idx = next_idx; 341 | 342 | } 343 | 344 | const char nul = '\0'; 345 | darray_push_back(string_darrays + cur_idx, &nul); 346 | 347 | darray_destroy(string_darrays + (1 - cur_idx)); 348 | 349 | return (char*)string_darrays[cur_idx].data; 350 | 351 | } 352 | 353 | void _lsys_execute_symbol(const lsys_t* lsys, 354 | const char symbol, 355 | darray_t* segments, 356 | lsys_state_t* state, 357 | darray_t* state_stack) { 358 | 359 | if (isalpha(symbol)) { 360 | 361 | if (lsys->draw_chars[0] && !lsys->draw_chars[(int)symbol]) { 362 | return; 363 | } 364 | 365 | float c = cosf(state->angle); 366 | float s = sinf(state->angle); 367 | 368 | float xnew = state->pos.x + c; 369 | float ynew = state->pos.y + s; 370 | 371 | lsys_segment_t seg = { { state->pos.x, state->pos.y}, 372 | { xnew, ynew } }; 373 | 374 | darray_push_back(segments, &seg); 375 | 376 | state->pos.x = xnew; 377 | state->pos.y = ynew; 378 | 379 | } else if (symbol == '+' || symbol == '-') { 380 | 381 | float delta = ( (symbol == '+') ? 382 | lsys->turn_angle_rad : -lsys->turn_angle_rad ); 383 | 384 | state->angle += delta; 385 | 386 | } else if (symbol == '[') { 387 | 388 | darray_push_back(state_stack, state); 389 | 390 | } else if (symbol == ']') { 391 | 392 | darray_pop_back(state_stack, state); 393 | 394 | } else { 395 | 396 | fprintf(stderr, "invalid character in string: %c\n", symbol); 397 | exit(1); 398 | 399 | } 400 | 401 | } 402 | 403 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 404 | const char* lstring) { 405 | 406 | darray_t* segments = malloc(sizeof(darray_t)); 407 | darray_create(segments, sizeof(lsys_segment_t), 408 | LSYS_INIT_SEGMENTS_CAPACITY); 409 | 410 | darray_t state_stack; 411 | darray_create(&state_stack, sizeof(lsys_state_t), 412 | LSYS_INIT_STATES_CAPACITY); 413 | 414 | lsys_state_t cur_state = LSYS_START_STATE; 415 | 416 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 417 | 418 | _lsys_execute_symbol(lsys, *psymbol, segments, 419 | &cur_state, &state_stack); 420 | 421 | } 422 | 423 | darray_destroy(&state_stack); 424 | 425 | return segments; 426 | 427 | } 428 | 429 | void _lsys_segments_r(const lsys_t* lsys, 430 | const char* lstring, 431 | size_t remaining_iterations, 432 | darray_t* segments, 433 | lsys_state_t* cur_state, 434 | darray_t* state_stack) { 435 | 436 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 437 | 438 | int symbol = *psymbol; 439 | 440 | const lsys_sized_string_t* rule = lsys->rules + symbol; 441 | 442 | if (remaining_iterations && rule->replacement) { 443 | 444 | _lsys_segments_r(lsys, rule->replacement, 445 | remaining_iterations-1, 446 | segments, cur_state, state_stack); 447 | 448 | } else { 449 | 450 | _lsys_execute_symbol(lsys, *psymbol, segments, 451 | cur_state, state_stack); 452 | 453 | } 454 | 455 | } 456 | 457 | } 458 | 459 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 460 | size_t total_iterations) { 461 | 462 | darray_t* segments = malloc(sizeof(darray_t)); 463 | darray_create(segments, sizeof(lsys_segment_t), 464 | LSYS_INIT_SEGMENTS_CAPACITY); 465 | 466 | darray_t state_stack; 467 | darray_create(&state_stack, sizeof(lsys_state_t), 468 | LSYS_INIT_STATES_CAPACITY); 469 | 470 | lsys_state_t cur_state = LSYS_START_STATE; 471 | 472 | _lsys_segments_r(lsys, lsys->start, 473 | total_iterations, segments, 474 | &cur_state, &state_stack); 475 | 476 | darray_destroy(&state_stack); 477 | 478 | return segments; 479 | 480 | } 481 | 482 | ////////////////////////////////////////////////////////////////////// 483 | // definitions for L-Systems from websites listed at top of file 484 | 485 | void initialize_known_lsystems(void) { 486 | 487 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_TRIANGLE, 488 | "sierpinski_triangle", "F-G-G", 489 | (lsys_rule_def_t[]){ 490 | { 'F', "F-G+F+G-F" }, 491 | { 'G', "GG" }, 492 | { 0, 0 } 493 | }, 120, NULL); 494 | 495 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_ARROWHEAD, 496 | "sierpinski_arrowhead", "A", 497 | (lsys_rule_def_t[]){ 498 | { 'A', "B-A-B" }, 499 | { 'B', "A+B+A" }, 500 | { 0, 0 } 501 | }, 60, NULL); 502 | 503 | lsys_create(KNOWN_LSYSTEMS + LSYS_DRAGON_CURVE, 504 | "dragon_curve", "FX", 505 | (lsys_rule_def_t[]){ 506 | { 'X', "X+YF+" }, 507 | { 'Y', "-FX-Y" }, 508 | { 0, 0 } 509 | }, 90, NULL); 510 | 511 | lsys_create(KNOWN_LSYSTEMS + LSYS_BARNSLEY_FERN, 512 | "barnsley_fern", "X", 513 | (lsys_rule_def_t[]){ 514 | { 'X', "F+[[X]-X]-F[-FX]+X" }, 515 | { 'F', "FF" }, 516 | { 0, 0 } 517 | }, 25, NULL); 518 | 519 | lsys_create(KNOWN_LSYSTEMS + LSYS_STICKS, 520 | "sticks", "X", 521 | (lsys_rule_def_t[]){ 522 | { 'X', "F[+X]F[-X]+X" }, 523 | { 'F', "FF" }, 524 | { 0, 0 } 525 | }, 20, "F"); 526 | 527 | lsys_create(KNOWN_LSYSTEMS + LSYS_HILBERT, 528 | "hilbert", "L", 529 | (lsys_rule_def_t[]){ 530 | { 'L', "+RF-LFL-FR+" }, 531 | { 'R', "-LF+RFR+FL-" }, 532 | { 0, 0 } 533 | }, 90, "F"); 534 | 535 | lsys_create(KNOWN_LSYSTEMS + LSYS_PENTAPLEXITY, 536 | "pentaplexity", "F++F++F++F++F", 537 | (lsys_rule_def_t[]){ 538 | { 'F', "F++F++F+++++F-F++F" }, 539 | { 0, 0 } 540 | }, 36, NULL); 541 | 542 | } 543 | 544 | ////////////////////////////////////////////////////////////////////// 545 | 546 | void parse_options(int argc, char** argv, options_t* opts) { 547 | 548 | int ok = 1; 549 | 550 | memset(opts, 0, sizeof(options_t)); 551 | opts->max_segments = 100000; 552 | 553 | int i=1; 554 | int required_count = 0; 555 | 556 | for (; ilsys = KNOWN_LSYSTEMS + j; 570 | break; 571 | } 572 | } 573 | 574 | if (!ok) { break; } 575 | 576 | } else if (required_count == 1) { 577 | 578 | int d; 579 | 580 | if (sscanf(arg, "%d", &d) != 1 || d <= 0) { 581 | ok = 0; 582 | break; 583 | } 584 | 585 | opts->total_iterations = d; 586 | 587 | } else { 588 | 589 | ok = 0; 590 | break; 591 | 592 | } 593 | 594 | ++required_count; 595 | 596 | } else if (!strcmp(arg, "-s")) { 597 | 598 | opts->method = LSYS_METHOD_STRING; 599 | 600 | } else if (!strcmp(arg, "-r")) { 601 | 602 | opts->method = LSYS_METHOD_RECURSION; 603 | 604 | } else if (!strcmp(arg, "-x")) { 605 | 606 | if (++i == argc) { ok = 0; break; } 607 | 608 | int d; 609 | 610 | if (sscanf(argv[i], "%d", &d) != 1) { 611 | ok = 0; break; 612 | } 613 | 614 | if (d >= -1) { 615 | opts->max_segments = (size_t)d; 616 | } else { 617 | ok = 0; 618 | break; 619 | } 620 | 621 | } else { 622 | 623 | fprintf(stderr, "error: unrecognized option %s\n\n", arg); 624 | 625 | ok = 0; 626 | break; 627 | 628 | } 629 | 630 | } 631 | 632 | if (!ok || !opts->lsys || !opts->total_iterations) { 633 | printf("usage: %s [options] LSYSTEM ITERATIONS\n" 634 | "\n" 635 | "where LSYSTEM is one of:\n", argv[0]); 636 | for (int j=0; jmethod == LSYS_METHOD_STRING ? "string" : "recursion"); 650 | 651 | printf("\n"); 652 | 653 | lsys_print(opts->lsys); 654 | 655 | } 656 | 657 | ////////////////////////////////////////////////////////////////////// 658 | // main program 659 | 660 | int main(int argc, char** argv) { 661 | 662 | // initialize lsystems 663 | initialize_known_lsystems(); 664 | 665 | // parse command-line options 666 | options_t opts; 667 | parse_options(argc, argv, &opts); 668 | 669 | //////////////////////////////////////////////////////////// 670 | // now get the segments 671 | 672 | double start = get_time_as_double(); 673 | 674 | darray_t* segments; 675 | 676 | if (opts.method == LSYS_METHOD_STRING) { 677 | 678 | char* lstring = lsys_build_string(opts.lsys, 679 | opts.total_iterations); 680 | 681 | segments = lsys_segments_from_string(opts.lsys, lstring); 682 | 683 | free(lstring); 684 | 685 | } else { 686 | 687 | segments = lsys_segments_recursive(opts.lsys, opts.total_iterations); 688 | 689 | } 690 | 691 | double elapsed = get_time_as_double() - start; 692 | printf("generated %d segments in %.6f s (%.3f ns/segment).\n", 693 | (int)segments->count, elapsed, 1e9 * elapsed/segments->count); 694 | 695 | //////////////////////////////////////////////////////////// 696 | // either output or not 697 | 698 | if (segments->count > opts.max_segments) { 699 | 700 | printf("...maximum of %d segments exceeded, skipping output!\n", 701 | (int)opts.max_segments); 702 | 703 | } else { 704 | 705 | const lsys_segment_t* segment = (const lsys_segment_t*)segments->data; 706 | const lsys_segment_t* end = segment + segments->count; 707 | 708 | FILE* outfile = fopen("segments.txt", "w"); 709 | 710 | for ( ; segment != end; ++segment) { 711 | fprintf(outfile, "%g %g %g %g\n", 712 | segment->p0.x, segment->p0.y, 713 | segment->p1.x, segment->p1.y); 714 | } 715 | 716 | fclose(outfile); 717 | 718 | printf("wrote segments.txt\n"); 719 | 720 | } 721 | 722 | //////////////////////////////////////////////////////////// 723 | // clean up 724 | 725 | darray_destroy(segments); 726 | free(segments); 727 | 728 | return 0; 729 | 730 | } 731 | -------------------------------------------------------------------------------- /lsystems_v4.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // lsystems_v4.c 4 | // Matt Zucker 5 | // 6 | ////////////////////////////////////////////////////////////////////// 7 | // 8 | // Based on documentation in https://en.wikipedia.org/wiki/L-system and 9 | // http://paulbourke.net/fractals/lsys/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | ////////////////////////////////////////////////////////////////////// 19 | // for benchmarking 20 | 21 | double get_time_as_double(void); 22 | 23 | ////////////////////////////////////////////////////////////////////// 24 | // dynamic array 25 | 26 | // dynamic array data type 27 | typedef struct darray { 28 | 29 | size_t elem_size; 30 | size_t capacity; 31 | size_t count; 32 | 33 | unsigned char* data; 34 | 35 | } darray_t; 36 | 37 | // dynamic array functions 38 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity); 39 | void darray_resize(darray_t* darray, size_t new_count); 40 | void darray_extend(darray_t* darray, const void* elements, size_t count); 41 | void darray_push_back(darray_t* darray, const void* elem); 42 | void darray_pop_back(darray_t* darray, void* elem); 43 | void* darray_elem_ptr(darray_t* darray, size_t idx); 44 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx); 45 | void darray_get(const darray_t* darray, size_t idx, void* dst); 46 | void darray_set(darray_t* darray, size_t idx, const void* src); 47 | void darray_clear(darray_t* darray); 48 | void darray_destroy(darray_t* darray); 49 | 50 | ////////////////////////////////////////////////////////////////////// 51 | // geometry utils 52 | 53 | // 2D point 54 | typedef struct point2d { 55 | float x, y; 56 | } point2d_t; 57 | 58 | // 2D rotation 59 | typedef struct rot2d { 60 | float c, s; 61 | } rot2d_t; 62 | 63 | ////////////////////////////////////////////////////////////////////// 64 | // L-System types/functions 65 | 66 | // misc enums 67 | enum { 68 | LSYS_MAX_RULES = 128, 69 | LSYS_MAX_CYCLE_LENGTH = 256, 70 | LSYS_INIT_STRING_CAPACITY = 4096, 71 | LSYS_INIT_STATES_CAPACITY = 64, 72 | LSYS_INIT_SEGMENTS_CAPACITY = 1024 73 | }; 74 | 75 | // line segment is 2 points 76 | typedef struct lsys_segment { 77 | point2d_t p0, p1; 78 | } lsys_segment_t; 79 | 80 | // rule tagged with string length for string replacement 81 | typedef struct lsys_sized_string { 82 | const char* replacement; 83 | size_t length; 84 | } lsys_sized_string_t; 85 | 86 | // L-System datatype 87 | typedef struct lsystem { 88 | 89 | const char* name; 90 | const char* start; 91 | lsys_sized_string_t rules[LSYS_MAX_RULES]; 92 | unsigned char draw_chars[LSYS_MAX_RULES]; 93 | double turn_angle_rad; 94 | 95 | } lsys_t; 96 | 97 | // lsystem character + replacement pair, for defining L-Systems 98 | typedef struct lsys_rule_def { 99 | char symbol; 100 | const char* replacement; 101 | } lsys_rule_def_t; 102 | 103 | typedef struct lsys_state { 104 | point2d_t pos; 105 | rot2d_t rot; 106 | float angle; 107 | } lsys_state_t; 108 | 109 | static const lsys_state_t LSYS_START_STATE = { { 0.f, 0.f, }, { 1.f, 0.f }, 0.f }; 110 | 111 | void lsys_create(lsys_t* lsys, 112 | const char* name, 113 | const char* start, 114 | lsys_rule_def_t const rules[], 115 | double turn_angle_deg, 116 | const char* draw_chars); 117 | 118 | void lsys_print(const lsys_t* lsys); 119 | 120 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations); 121 | 122 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 123 | const char* lstring); 124 | 125 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 126 | size_t total_iterations); 127 | 128 | ////////////////////////////////////////////////////////////////////// 129 | // set up some known L-Systems 130 | 131 | enum { 132 | LSYS_SIERPINSKI_ARROWHEAD = 0, // depth 17 takes ~5 sec 133 | LSYS_SIERPINSKI_TRIANGLE, // depth 16 takes ~3 sec 134 | LSYS_DRAGON_CURVE, // depth 26 takes ~6 sec 135 | LSYS_BARNSLEY_FERN, // depth 13 takes ~7 sec 136 | LSYS_STICKS, // depth 16 takes ~4 sec 137 | LSYS_HILBERT, // depth 13 takes ~3 sec 138 | LSYS_PENTAPLEXITY, // depth 9 takes ~2 sec 139 | NUM_KNOWN_LSYSTEMS 140 | }; 141 | 142 | lsys_t KNOWN_LSYSTEMS[NUM_KNOWN_LSYSTEMS]; 143 | 144 | void initialize_known_lsystems(void); 145 | 146 | ////////////////////////////////////////////////////////////////////// 147 | // options for running this program 148 | 149 | typedef enum lsys_method { 150 | LSYS_METHOD_RECURSION, 151 | LSYS_METHOD_STRING 152 | } lsys_method_t; 153 | 154 | typedef struct options { 155 | lsys_t* lsys; 156 | size_t total_iterations; 157 | size_t max_segments; 158 | lsys_method_t method; 159 | } options_t; 160 | 161 | void parse_options(int argc, char** argv, options_t* opts); 162 | 163 | ////////////////////////////////////////////////////////////////////// 164 | 165 | double get_time_as_double(void) { 166 | 167 | struct timespec tp; 168 | 169 | clock_gettime(CLOCK_REALTIME, &tp); 170 | 171 | return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9; 172 | 173 | } 174 | 175 | ////////////////////////////////////////////////////////////////////// 176 | 177 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity) { 178 | 179 | size_t alloc_size = elem_size * capacity; 180 | 181 | darray->elem_size = elem_size; 182 | darray->count = 0; 183 | darray->capacity = capacity; 184 | darray->data = malloc(alloc_size); 185 | 186 | } 187 | 188 | void darray_resize(darray_t* darray, size_t new_count) { 189 | 190 | if (new_count > darray->capacity) { 191 | 192 | size_t new_capacity = darray->capacity; 193 | 194 | while (new_capacity <= new_count) { 195 | new_capacity *= 2; 196 | } 197 | 198 | size_t alloc_size = darray->elem_size * new_capacity; 199 | 200 | darray->data = realloc(darray->data, alloc_size); 201 | darray->capacity = new_capacity; 202 | 203 | } 204 | 205 | darray->count = new_count; 206 | 207 | } 208 | 209 | void darray_extend(darray_t* darray, const void* elements, size_t count) { 210 | 211 | size_t offset = darray->elem_size * darray->count; 212 | 213 | darray_resize(darray, darray->count + count); 214 | 215 | memcpy(darray->data + offset, elements, count*darray->elem_size); 216 | 217 | } 218 | 219 | void darray_push_back(darray_t* darray, const void* elem) { 220 | darray_extend(darray, elem, 1); 221 | } 222 | 223 | void* darray_elem_ptr(darray_t* darray, size_t idx) { 224 | return darray->data + idx*darray->elem_size; 225 | } 226 | 227 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx) { 228 | return darray->data + idx*darray->elem_size; 229 | } 230 | 231 | void darray_get(const darray_t* darray, size_t idx, void* dst) { 232 | memcpy(dst, darray_const_elem_ptr(darray, idx), darray->elem_size); 233 | } 234 | 235 | void darray_set(darray_t* darray, size_t idx, const void* src) { 236 | memcpy(darray_elem_ptr(darray, idx), src, darray->elem_size); 237 | } 238 | 239 | void darray_pop_back(darray_t* darray, void* dst) { 240 | darray->count -= 1; 241 | size_t offset = darray->count * darray->elem_size; 242 | memcpy(dst, (const void*)darray->data + offset, darray->elem_size); 243 | } 244 | 245 | void darray_clear(darray_t* darray) { 246 | darray->count = 0; 247 | } 248 | 249 | void darray_destroy(darray_t* darray) { 250 | free(darray->data); 251 | memset(darray, 0, sizeof(darray_t)); 252 | } 253 | 254 | ////////////////////////////////////////////////////////////////////// 255 | 256 | void lsys_create(lsys_t* lsys, 257 | const char* name, 258 | const char* start, 259 | lsys_rule_def_t const rules[], 260 | double turn_angle_deg, 261 | const char* draw_chars) { 262 | 263 | lsys->name = name; 264 | lsys->start = start; 265 | 266 | memset(lsys->rules, 0, sizeof(lsys->rules)); 267 | memset(lsys->draw_chars, 0, sizeof(lsys->draw_chars)); 268 | 269 | for (const lsys_rule_def_t* src_rule=rules; src_rule->symbol; ++src_rule) { 270 | lsys_sized_string_t* dst_rule = lsys->rules + (int)src_rule->symbol; 271 | dst_rule->replacement = src_rule->replacement; 272 | dst_rule->length = strlen(src_rule->replacement); 273 | } 274 | 275 | lsys->turn_angle_rad = turn_angle_deg * M_PI / 180.f; 276 | 277 | if (draw_chars) { 278 | lsys->draw_chars[0] = 1; 279 | for (const char* c=draw_chars; *c; ++c) { 280 | lsys->draw_chars[(int)*c] = 1; 281 | } 282 | } 283 | 284 | } 285 | 286 | void lsys_print(const lsys_t* lsys) { 287 | 288 | printf("%s:\n", lsys->name); 289 | printf(" start: %s\n", lsys->start); 290 | printf(" rules:\n"); 291 | for (int i=0; irules[i].replacement) { 293 | printf(" %c -> %s\n", i, lsys->rules[i].replacement); 294 | } 295 | } 296 | printf(" turn_angle_deg: %g\n", lsys->turn_angle_rad * 180.f / M_PI); 297 | printf("\n"); 298 | 299 | } 300 | 301 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations) { 302 | 303 | darray_t string_darrays[2]; 304 | 305 | for (int i=0; i<2; ++i) { 306 | darray_create(string_darrays + i, sizeof(char), 307 | LSYS_INIT_STRING_CAPACITY); 308 | } 309 | 310 | int cur_idx = 0; 311 | 312 | darray_extend(string_darrays + cur_idx, 313 | lsys->start, 314 | strlen(lsys->start)); 315 | 316 | for (int i=0; idata; 326 | const char* end = start + src_darray->count; 327 | 328 | for (const char* c=start; c!=end; ++c) { 329 | 330 | const lsys_sized_string_t* rule = lsys->rules + (int)*c; 331 | 332 | if (rule->replacement) { 333 | 334 | darray_extend(dst_darray, 335 | rule->replacement, 336 | rule->length); 337 | 338 | } else { 339 | 340 | darray_push_back(dst_darray, c); 341 | 342 | } 343 | 344 | } 345 | 346 | cur_idx = next_idx; 347 | 348 | } 349 | 350 | const char nul = '\0'; 351 | darray_push_back(string_darrays + cur_idx, &nul); 352 | 353 | darray_destroy(string_darrays + (1 - cur_idx)); 354 | 355 | return (char*)string_darrays[cur_idx].data; 356 | 357 | } 358 | 359 | void _lsys_execute_symbol(const lsys_t* lsys, 360 | const char symbol, 361 | darray_t* segments, 362 | lsys_state_t* state, 363 | darray_t* state_stack) { 364 | 365 | if (isalpha(symbol)) { 366 | 367 | if (lsys->draw_chars[0] && !lsys->draw_chars[(int)symbol]) { 368 | return; 369 | } 370 | 371 | float xnew = state->pos.x + state->rot.c; 372 | float ynew = state->pos.y + state->rot.s; 373 | 374 | lsys_segment_t seg = { { state->pos.x, state->pos.y}, 375 | { xnew, ynew } }; 376 | 377 | darray_push_back(segments, &seg); 378 | 379 | state->pos.x = xnew; 380 | state->pos.y = ynew; 381 | 382 | } else if (symbol == '+' || symbol == '-') { 383 | 384 | float delta = ( (symbol == '+') ? 385 | lsys->turn_angle_rad : -lsys->turn_angle_rad ); 386 | 387 | state->angle += delta; 388 | 389 | state->rot.c = cosf(state->angle); 390 | state->rot.s = sinf(state->angle); 391 | 392 | } else if (symbol == '[') { 393 | 394 | darray_push_back(state_stack, state); 395 | 396 | } else if (symbol == ']') { 397 | 398 | darray_pop_back(state_stack, state); 399 | 400 | } else { 401 | 402 | fprintf(stderr, "invalid character in string: %c\n", symbol); 403 | exit(1); 404 | 405 | } 406 | 407 | } 408 | 409 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 410 | const char* lstring) { 411 | 412 | darray_t* segments = malloc(sizeof(darray_t)); 413 | darray_create(segments, sizeof(lsys_segment_t), 414 | LSYS_INIT_SEGMENTS_CAPACITY); 415 | 416 | darray_t state_stack; 417 | darray_create(&state_stack, sizeof(lsys_state_t), 418 | LSYS_INIT_STATES_CAPACITY); 419 | 420 | lsys_state_t cur_state = LSYS_START_STATE; 421 | 422 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 423 | 424 | _lsys_execute_symbol(lsys, *psymbol, segments, 425 | &cur_state, &state_stack); 426 | 427 | } 428 | 429 | darray_destroy(&state_stack); 430 | 431 | return segments; 432 | 433 | } 434 | 435 | void _lsys_segments_r(const lsys_t* lsys, 436 | const char* lstring, 437 | size_t remaining_iterations, 438 | darray_t* segments, 439 | lsys_state_t* cur_state, 440 | darray_t* state_stack) { 441 | 442 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 443 | 444 | int symbol = *psymbol; 445 | 446 | const lsys_sized_string_t* rule = lsys->rules + symbol; 447 | 448 | if (remaining_iterations && rule->replacement) { 449 | 450 | _lsys_segments_r(lsys, rule->replacement, 451 | remaining_iterations-1, 452 | segments, cur_state, state_stack); 453 | 454 | } else { 455 | 456 | _lsys_execute_symbol(lsys, *psymbol, segments, 457 | cur_state, state_stack); 458 | 459 | } 460 | 461 | } 462 | 463 | } 464 | 465 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 466 | size_t total_iterations) { 467 | 468 | darray_t* segments = malloc(sizeof(darray_t)); 469 | darray_create(segments, sizeof(lsys_segment_t), 470 | LSYS_INIT_SEGMENTS_CAPACITY); 471 | 472 | darray_t state_stack; 473 | darray_create(&state_stack, sizeof(lsys_state_t), 474 | LSYS_INIT_STATES_CAPACITY); 475 | 476 | lsys_state_t cur_state = LSYS_START_STATE; 477 | 478 | _lsys_segments_r(lsys, lsys->start, 479 | total_iterations, segments, 480 | &cur_state, &state_stack); 481 | 482 | darray_destroy(&state_stack); 483 | 484 | return segments; 485 | 486 | } 487 | 488 | ////////////////////////////////////////////////////////////////////// 489 | // definitions for L-Systems from websites listed at top of file 490 | 491 | void initialize_known_lsystems(void) { 492 | 493 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_TRIANGLE, 494 | "sierpinski_triangle", "F-G-G", 495 | (lsys_rule_def_t[]){ 496 | { 'F', "F-G+F+G-F" }, 497 | { 'G', "GG" }, 498 | { 0, 0 } 499 | }, 120, NULL); 500 | 501 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_ARROWHEAD, 502 | "sierpinski_arrowhead", "A", 503 | (lsys_rule_def_t[]){ 504 | { 'A', "B-A-B" }, 505 | { 'B', "A+B+A" }, 506 | { 0, 0 } 507 | }, 60, NULL); 508 | 509 | lsys_create(KNOWN_LSYSTEMS + LSYS_DRAGON_CURVE, 510 | "dragon_curve", "FX", 511 | (lsys_rule_def_t[]){ 512 | { 'X', "X+YF+" }, 513 | { 'Y', "-FX-Y" }, 514 | { 0, 0 } 515 | }, 90, NULL); 516 | 517 | lsys_create(KNOWN_LSYSTEMS + LSYS_BARNSLEY_FERN, 518 | "barnsley_fern", "X", 519 | (lsys_rule_def_t[]){ 520 | { 'X', "F+[[X]-X]-F[-FX]+X" }, 521 | { 'F', "FF" }, 522 | { 0, 0 } 523 | }, 25, NULL); 524 | 525 | lsys_create(KNOWN_LSYSTEMS + LSYS_STICKS, 526 | "sticks", "X", 527 | (lsys_rule_def_t[]){ 528 | { 'X', "F[+X]F[-X]+X" }, 529 | { 'F', "FF" }, 530 | { 0, 0 } 531 | }, 20, "F"); 532 | 533 | lsys_create(KNOWN_LSYSTEMS + LSYS_HILBERT, 534 | "hilbert", "L", 535 | (lsys_rule_def_t[]){ 536 | { 'L', "+RF-LFL-FR+" }, 537 | { 'R', "-LF+RFR+FL-" }, 538 | { 0, 0 } 539 | }, 90, "F"); 540 | 541 | lsys_create(KNOWN_LSYSTEMS + LSYS_PENTAPLEXITY, 542 | "pentaplexity", "F++F++F++F++F", 543 | (lsys_rule_def_t[]){ 544 | { 'F', "F++F++F+++++F-F++F" }, 545 | { 0, 0 } 546 | }, 36, NULL); 547 | 548 | } 549 | 550 | ////////////////////////////////////////////////////////////////////// 551 | 552 | void parse_options(int argc, char** argv, options_t* opts) { 553 | 554 | int ok = 1; 555 | 556 | memset(opts, 0, sizeof(options_t)); 557 | opts->max_segments = 100000; 558 | 559 | int i=1; 560 | int required_count = 0; 561 | 562 | for (; ilsys = KNOWN_LSYSTEMS + j; 576 | break; 577 | } 578 | } 579 | 580 | if (!ok) { break; } 581 | 582 | } else if (required_count == 1) { 583 | 584 | int d; 585 | 586 | if (sscanf(arg, "%d", &d) != 1 || d <= 0) { 587 | ok = 0; 588 | break; 589 | } 590 | 591 | opts->total_iterations = d; 592 | 593 | } else { 594 | 595 | ok = 0; 596 | break; 597 | 598 | } 599 | 600 | ++required_count; 601 | 602 | } else if (!strcmp(arg, "-s")) { 603 | 604 | opts->method = LSYS_METHOD_STRING; 605 | 606 | } else if (!strcmp(arg, "-r")) { 607 | 608 | opts->method = LSYS_METHOD_RECURSION; 609 | 610 | } else if (!strcmp(arg, "-x")) { 611 | 612 | if (++i == argc) { ok = 0; break; } 613 | 614 | int d; 615 | 616 | if (sscanf(argv[i], "%d", &d) != 1) { 617 | ok = 0; break; 618 | } 619 | 620 | if (d >= -1) { 621 | opts->max_segments = (size_t)d; 622 | } else { 623 | ok = 0; 624 | break; 625 | } 626 | 627 | } else { 628 | 629 | fprintf(stderr, "error: unrecognized option %s\n\n", arg); 630 | 631 | ok = 0; 632 | break; 633 | 634 | } 635 | 636 | } 637 | 638 | if (!ok || !opts->lsys || !opts->total_iterations) { 639 | printf("usage: %s [options] LSYSTEM ITERATIONS\n" 640 | "\n" 641 | "where LSYSTEM is one of:\n", argv[0]); 642 | for (int j=0; jmethod == LSYS_METHOD_STRING ? "string" : "recursion"); 656 | 657 | printf("\n"); 658 | 659 | lsys_print(opts->lsys); 660 | 661 | } 662 | 663 | ////////////////////////////////////////////////////////////////////// 664 | // main program 665 | 666 | int main(int argc, char** argv) { 667 | 668 | // initialize lsystems 669 | initialize_known_lsystems(); 670 | 671 | // parse command-line options 672 | options_t opts; 673 | parse_options(argc, argv, &opts); 674 | 675 | //////////////////////////////////////////////////////////// 676 | // now get the segments 677 | 678 | double start = get_time_as_double(); 679 | 680 | darray_t* segments; 681 | 682 | if (opts.method == LSYS_METHOD_STRING) { 683 | 684 | char* lstring = lsys_build_string(opts.lsys, 685 | opts.total_iterations); 686 | 687 | segments = lsys_segments_from_string(opts.lsys, lstring); 688 | 689 | free(lstring); 690 | 691 | } else { 692 | 693 | segments = lsys_segments_recursive(opts.lsys, opts.total_iterations); 694 | 695 | } 696 | 697 | double elapsed = get_time_as_double() - start; 698 | printf("generated %d segments in %.6f s (%.3f ns/segment).\n", 699 | (int)segments->count, elapsed, 1e9 * elapsed/segments->count); 700 | 701 | //////////////////////////////////////////////////////////// 702 | // either output or not 703 | 704 | if (segments->count > opts.max_segments) { 705 | 706 | printf("...maximum of %d segments exceeded, skipping output!\n", 707 | (int)opts.max_segments); 708 | 709 | } else { 710 | 711 | const lsys_segment_t* segment = (const lsys_segment_t*)segments->data; 712 | const lsys_segment_t* end = segment + segments->count; 713 | 714 | FILE* outfile = fopen("segments.txt", "w"); 715 | 716 | for ( ; segment != end; ++segment) { 717 | fprintf(outfile, "%g %g %g %g\n", 718 | segment->p0.x, segment->p0.y, 719 | segment->p1.x, segment->p1.y); 720 | } 721 | 722 | fclose(outfile); 723 | 724 | printf("wrote segments.txt\n"); 725 | 726 | } 727 | 728 | //////////////////////////////////////////////////////////// 729 | // clean up 730 | 731 | darray_destroy(segments); 732 | free(segments); 733 | 734 | return 0; 735 | 736 | } 737 | -------------------------------------------------------------------------------- /lsystems_v5.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // lsystems_v5.c 4 | // Matt Zucker 5 | // 6 | ////////////////////////////////////////////////////////////////////// 7 | // 8 | // Based on documentation in https://en.wikipedia.org/wiki/L-system and 9 | // http://paulbourke.net/fractals/lsys/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | ////////////////////////////////////////////////////////////////////// 19 | // for benchmarking 20 | 21 | double get_time_as_double(void); 22 | 23 | ////////////////////////////////////////////////////////////////////// 24 | // dynamic array 25 | 26 | // dynamic array data type 27 | typedef struct darray { 28 | 29 | size_t elem_size; 30 | size_t capacity; 31 | size_t count; 32 | 33 | unsigned char* data; 34 | 35 | } darray_t; 36 | 37 | // dynamic array functions 38 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity); 39 | void darray_resize(darray_t* darray, size_t new_count); 40 | void darray_extend(darray_t* darray, const void* elements, size_t count); 41 | void darray_push_back(darray_t* darray, const void* elem); 42 | void darray_pop_back(darray_t* darray, void* elem); 43 | void* darray_elem_ptr(darray_t* darray, size_t idx); 44 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx); 45 | void darray_get(const darray_t* darray, size_t idx, void* dst); 46 | void darray_set(darray_t* darray, size_t idx, const void* src); 47 | void darray_clear(darray_t* darray); 48 | void darray_destroy(darray_t* darray); 49 | 50 | ////////////////////////////////////////////////////////////////////// 51 | // geometry utils 52 | 53 | // 2D point 54 | typedef struct point2d { 55 | float x, y; 56 | } point2d_t; 57 | 58 | // 2D rotation 59 | typedef struct rot2d { 60 | float c, s; 61 | } rot2d_t; 62 | 63 | int positive_mod(int ticks, int divisor); 64 | 65 | ////////////////////////////////////////////////////////////////////// 66 | // L-System types/functions 67 | 68 | // misc enums 69 | enum { 70 | LSYS_MAX_RULES = 128, 71 | LSYS_MAX_CYCLE_LENGTH = 256, 72 | LSYS_INIT_STRING_CAPACITY = 4096, 73 | LSYS_INIT_STATES_CAPACITY = 64, 74 | LSYS_INIT_SEGMENTS_CAPACITY = 1024 75 | }; 76 | 77 | // line segment is 2 points 78 | typedef struct lsys_segment { 79 | point2d_t p0, p1; 80 | } lsys_segment_t; 81 | 82 | // rule tagged with string length for string replacement 83 | typedef struct lsys_sized_string { 84 | const char* replacement; 85 | size_t length; 86 | } lsys_sized_string_t; 87 | 88 | // L-System datatype 89 | typedef struct lsystem { 90 | 91 | const char* name; 92 | const char* start; 93 | lsys_sized_string_t rules[LSYS_MAX_RULES]; 94 | unsigned char draw_chars[LSYS_MAX_RULES]; 95 | double turn_angle_rad; 96 | int rotation_cycle_length; 97 | rot2d_t rotations[LSYS_MAX_CYCLE_LENGTH]; 98 | 99 | } lsys_t; 100 | 101 | // lsystem character + replacement pair, for defining L-Systems 102 | typedef struct lsys_rule_def { 103 | char symbol; 104 | const char* replacement; 105 | } lsys_rule_def_t; 106 | 107 | typedef struct lsys_state { 108 | point2d_t pos; 109 | rot2d_t rot; 110 | float angle; 111 | } lsys_state_t; 112 | 113 | static const lsys_state_t LSYS_START_STATE = { { 0.f, 0.f, }, { 1.f, 0.f }, 0.f }; 114 | 115 | void lsys_create(lsys_t* lsys, 116 | const char* name, 117 | const char* start, 118 | lsys_rule_def_t const rules[], 119 | double turn_angle_deg, 120 | const char* draw_chars); 121 | 122 | void lsys_print(const lsys_t* lsys); 123 | 124 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations); 125 | 126 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 127 | const char* lstring); 128 | 129 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 130 | size_t total_iterations); 131 | 132 | ////////////////////////////////////////////////////////////////////// 133 | // set up some known L-Systems 134 | 135 | enum { 136 | LSYS_SIERPINSKI_ARROWHEAD = 0, // depth 17 takes ~5 sec 137 | LSYS_SIERPINSKI_TRIANGLE, // depth 16 takes ~3 sec 138 | LSYS_DRAGON_CURVE, // depth 26 takes ~6 sec 139 | LSYS_BARNSLEY_FERN, // depth 13 takes ~7 sec 140 | LSYS_STICKS, // depth 16 takes ~4 sec 141 | LSYS_HILBERT, // depth 13 takes ~3 sec 142 | LSYS_PENTAPLEXITY, // depth 9 takes ~2 sec 143 | NUM_KNOWN_LSYSTEMS 144 | }; 145 | 146 | lsys_t KNOWN_LSYSTEMS[NUM_KNOWN_LSYSTEMS]; 147 | 148 | void initialize_known_lsystems(void); 149 | 150 | ////////////////////////////////////////////////////////////////////// 151 | // options for running this program 152 | 153 | typedef enum lsys_method { 154 | LSYS_METHOD_RECURSION, 155 | LSYS_METHOD_STRING 156 | } lsys_method_t; 157 | 158 | typedef struct options { 159 | lsys_t* lsys; 160 | size_t total_iterations; 161 | size_t max_segments; 162 | lsys_method_t method; 163 | } options_t; 164 | 165 | void parse_options(int argc, char** argv, options_t* opts); 166 | 167 | ////////////////////////////////////////////////////////////////////// 168 | 169 | double get_time_as_double(void) { 170 | 171 | struct timespec tp; 172 | 173 | clock_gettime(CLOCK_REALTIME, &tp); 174 | 175 | return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9; 176 | 177 | } 178 | 179 | ////////////////////////////////////////////////////////////////////// 180 | 181 | int positive_mod(int ticks, int divisor) { 182 | 183 | int rval = ticks % divisor; 184 | if (ticks < 0) { 185 | rval += divisor; 186 | } 187 | 188 | return rval; 189 | 190 | } 191 | 192 | ////////////////////////////////////////////////////////////////////// 193 | 194 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity) { 195 | 196 | size_t alloc_size = elem_size * capacity; 197 | 198 | darray->elem_size = elem_size; 199 | darray->count = 0; 200 | darray->capacity = capacity; 201 | darray->data = malloc(alloc_size); 202 | 203 | } 204 | 205 | void darray_resize(darray_t* darray, size_t new_count) { 206 | 207 | if (new_count > darray->capacity) { 208 | 209 | size_t new_capacity = darray->capacity; 210 | 211 | while (new_capacity <= new_count) { 212 | new_capacity *= 2; 213 | } 214 | 215 | size_t alloc_size = darray->elem_size * new_capacity; 216 | 217 | darray->data = realloc(darray->data, alloc_size); 218 | darray->capacity = new_capacity; 219 | 220 | } 221 | 222 | darray->count = new_count; 223 | 224 | } 225 | 226 | void darray_extend(darray_t* darray, const void* elements, size_t count) { 227 | 228 | size_t offset = darray->elem_size * darray->count; 229 | 230 | darray_resize(darray, darray->count + count); 231 | 232 | memcpy(darray->data + offset, elements, count*darray->elem_size); 233 | 234 | } 235 | 236 | void darray_push_back(darray_t* darray, const void* elem) { 237 | darray_extend(darray, elem, 1); 238 | } 239 | 240 | void* darray_elem_ptr(darray_t* darray, size_t idx) { 241 | return darray->data + idx*darray->elem_size; 242 | } 243 | 244 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx) { 245 | return darray->data + idx*darray->elem_size; 246 | } 247 | 248 | void darray_get(const darray_t* darray, size_t idx, void* dst) { 249 | memcpy(dst, darray_const_elem_ptr(darray, idx), darray->elem_size); 250 | } 251 | 252 | void darray_set(darray_t* darray, size_t idx, const void* src) { 253 | memcpy(darray_elem_ptr(darray, idx), src, darray->elem_size); 254 | } 255 | 256 | void darray_pop_back(darray_t* darray, void* dst) { 257 | darray->count -= 1; 258 | size_t offset = darray->count * darray->elem_size; 259 | memcpy(dst, (const void*)darray->data + offset, darray->elem_size); 260 | } 261 | 262 | void darray_clear(darray_t* darray) { 263 | darray->count = 0; 264 | } 265 | 266 | void darray_destroy(darray_t* darray) { 267 | free(darray->data); 268 | memset(darray, 0, sizeof(darray_t)); 269 | } 270 | 271 | ////////////////////////////////////////////////////////////////////// 272 | 273 | void lsys_create(lsys_t* lsys, 274 | const char* name, 275 | const char* start, 276 | lsys_rule_def_t const rules[], 277 | double turn_angle_deg, 278 | const char* draw_chars) { 279 | 280 | lsys->name = name; 281 | lsys->start = start; 282 | 283 | memset(lsys->rules, 0, sizeof(lsys->rules)); 284 | memset(lsys->draw_chars, 0, sizeof(lsys->draw_chars)); 285 | 286 | for (const lsys_rule_def_t* src_rule=rules; src_rule->symbol; ++src_rule) { 287 | lsys_sized_string_t* dst_rule = lsys->rules + (int)src_rule->symbol; 288 | dst_rule->replacement = src_rule->replacement; 289 | dst_rule->length = strlen(src_rule->replacement); 290 | } 291 | 292 | lsys->turn_angle_rad = turn_angle_deg * M_PI / 180.f; 293 | 294 | for (int i=0; i<=LSYS_MAX_CYCLE_LENGTH; ++i) { 295 | 296 | if (i > 0 && fmod(turn_angle_deg*i, 360.) == 0) { 297 | lsys->rotation_cycle_length = i; 298 | break; 299 | } 300 | 301 | float theta = lsys->turn_angle_rad * i; 302 | 303 | lsys->rotations[i].c = cosf(theta); 304 | lsys->rotations[i].s = sinf(theta); 305 | 306 | } 307 | 308 | if (draw_chars) { 309 | lsys->draw_chars[0] = 1; 310 | for (const char* c=draw_chars; *c; ++c) { 311 | lsys->draw_chars[(int)*c] = 1; 312 | } 313 | } 314 | 315 | } 316 | 317 | void lsys_print(const lsys_t* lsys) { 318 | 319 | printf("%s:\n", lsys->name); 320 | printf(" start: %s\n", lsys->start); 321 | printf(" rules:\n"); 322 | for (int i=0; irules[i].replacement) { 324 | printf(" %c -> %s\n", i, lsys->rules[i].replacement); 325 | } 326 | } 327 | printf(" turn_angle_deg: %g\n", lsys->turn_angle_rad * 180.f / M_PI); 328 | if (lsys->rotation_cycle_length) { 329 | printf(" rotation_cycle_length: %d\n", lsys->rotation_cycle_length); 330 | } 331 | printf("\n"); 332 | 333 | } 334 | 335 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations) { 336 | 337 | darray_t string_darrays[2]; 338 | 339 | for (int i=0; i<2; ++i) { 340 | darray_create(string_darrays + i, sizeof(char), 341 | LSYS_INIT_STRING_CAPACITY); 342 | } 343 | 344 | int cur_idx = 0; 345 | 346 | darray_extend(string_darrays + cur_idx, 347 | lsys->start, 348 | strlen(lsys->start)); 349 | 350 | for (int i=0; idata; 360 | const char* end = start + src_darray->count; 361 | 362 | for (const char* c=start; c!=end; ++c) { 363 | 364 | const lsys_sized_string_t* rule = lsys->rules + (int)*c; 365 | 366 | if (rule->replacement) { 367 | 368 | darray_extend(dst_darray, 369 | rule->replacement, 370 | rule->length); 371 | 372 | } else { 373 | 374 | darray_push_back(dst_darray, c); 375 | 376 | } 377 | 378 | } 379 | 380 | cur_idx = next_idx; 381 | 382 | } 383 | 384 | const char nul = '\0'; 385 | darray_push_back(string_darrays + cur_idx, &nul); 386 | 387 | darray_destroy(string_darrays + (1 - cur_idx)); 388 | 389 | return (char*)string_darrays[cur_idx].data; 390 | 391 | } 392 | 393 | void _lsys_execute_symbol(const lsys_t* lsys, 394 | const char symbol, 395 | darray_t* segments, 396 | lsys_state_t* state, 397 | darray_t* state_stack) { 398 | 399 | if (isalpha(symbol)) { 400 | 401 | if (lsys->draw_chars[0] && !lsys->draw_chars[(int)symbol]) { 402 | return; 403 | } 404 | 405 | float xnew = state->pos.x + state->rot.c; 406 | float ynew = state->pos.y + state->rot.s; 407 | 408 | lsys_segment_t seg = { { state->pos.x, state->pos.y}, 409 | { xnew, ynew } }; 410 | 411 | darray_push_back(segments, &seg); 412 | 413 | state->pos.x = xnew; 414 | state->pos.y = ynew; 415 | 416 | } else if (symbol == '+' || symbol == '-') { 417 | 418 | if (lsys->rotation_cycle_length) { 419 | 420 | int delta = (symbol == '+') ? 1 : -1; 421 | 422 | int t = positive_mod((int)state->angle + delta, 423 | lsys->rotation_cycle_length); 424 | 425 | state->angle = t; 426 | state->rot = lsys->rotations[t]; 427 | 428 | } else { 429 | 430 | float delta = ( (symbol == '+') ? 431 | lsys->turn_angle_rad : -lsys->turn_angle_rad ); 432 | 433 | state->angle += delta; 434 | 435 | state->rot.c = cosf(state->angle); 436 | state->rot.s = sinf(state->angle); 437 | 438 | } 439 | 440 | } else if (symbol == '[') { 441 | 442 | darray_push_back(state_stack, state); 443 | 444 | } else if (symbol == ']') { 445 | 446 | darray_pop_back(state_stack, state); 447 | 448 | } else { 449 | 450 | fprintf(stderr, "invalid character in string: %c\n", symbol); 451 | exit(1); 452 | 453 | } 454 | 455 | } 456 | 457 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 458 | const char* lstring) { 459 | 460 | darray_t* segments = malloc(sizeof(darray_t)); 461 | darray_create(segments, sizeof(lsys_segment_t), 462 | LSYS_INIT_SEGMENTS_CAPACITY); 463 | 464 | darray_t state_stack; 465 | darray_create(&state_stack, sizeof(lsys_state_t), 466 | LSYS_INIT_STATES_CAPACITY); 467 | 468 | lsys_state_t cur_state = LSYS_START_STATE; 469 | 470 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 471 | 472 | _lsys_execute_symbol(lsys, *psymbol, segments, 473 | &cur_state, &state_stack); 474 | 475 | } 476 | 477 | darray_destroy(&state_stack); 478 | 479 | return segments; 480 | 481 | } 482 | 483 | void _lsys_segments_r(const lsys_t* lsys, 484 | const char* lstring, 485 | size_t remaining_iterations, 486 | darray_t* segments, 487 | lsys_state_t* cur_state, 488 | darray_t* state_stack) { 489 | 490 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 491 | 492 | int symbol = *psymbol; 493 | 494 | const lsys_sized_string_t* rule = lsys->rules + symbol; 495 | 496 | if (remaining_iterations && rule->replacement) { 497 | 498 | _lsys_segments_r(lsys, rule->replacement, 499 | remaining_iterations-1, 500 | segments, cur_state, state_stack); 501 | 502 | } else { 503 | 504 | _lsys_execute_symbol(lsys, *psymbol, segments, 505 | cur_state, state_stack); 506 | 507 | } 508 | 509 | } 510 | 511 | } 512 | 513 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 514 | size_t total_iterations) { 515 | 516 | darray_t* segments = malloc(sizeof(darray_t)); 517 | darray_create(segments, sizeof(lsys_segment_t), 518 | LSYS_INIT_SEGMENTS_CAPACITY); 519 | 520 | darray_t state_stack; 521 | darray_create(&state_stack, sizeof(lsys_state_t), 522 | LSYS_INIT_STATES_CAPACITY); 523 | 524 | lsys_state_t cur_state = LSYS_START_STATE; 525 | 526 | _lsys_segments_r(lsys, lsys->start, 527 | total_iterations, segments, 528 | &cur_state, &state_stack); 529 | 530 | darray_destroy(&state_stack); 531 | 532 | return segments; 533 | 534 | } 535 | 536 | ////////////////////////////////////////////////////////////////////// 537 | // definitions for L-Systems from websites listed at top of file 538 | 539 | void initialize_known_lsystems(void) { 540 | 541 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_TRIANGLE, 542 | "sierpinski_triangle", "F-G-G", 543 | (lsys_rule_def_t[]){ 544 | { 'F', "F-G+F+G-F" }, 545 | { 'G', "GG" }, 546 | { 0, 0 } 547 | }, 120, NULL); 548 | 549 | lsys_create(KNOWN_LSYSTEMS + LSYS_SIERPINSKI_ARROWHEAD, 550 | "sierpinski_arrowhead", "A", 551 | (lsys_rule_def_t[]){ 552 | { 'A', "B-A-B" }, 553 | { 'B', "A+B+A" }, 554 | { 0, 0 } 555 | }, 60, NULL); 556 | 557 | lsys_create(KNOWN_LSYSTEMS + LSYS_DRAGON_CURVE, 558 | "dragon_curve", "FX", 559 | (lsys_rule_def_t[]){ 560 | { 'X', "X+YF+" }, 561 | { 'Y', "-FX-Y" }, 562 | { 0, 0 } 563 | }, 90, NULL); 564 | 565 | lsys_create(KNOWN_LSYSTEMS + LSYS_BARNSLEY_FERN, 566 | "barnsley_fern", "X", 567 | (lsys_rule_def_t[]){ 568 | { 'X', "F+[[X]-X]-F[-FX]+X" }, 569 | { 'F', "FF" }, 570 | { 0, 0 } 571 | }, 25, NULL); 572 | 573 | lsys_create(KNOWN_LSYSTEMS + LSYS_STICKS, 574 | "sticks", "X", 575 | (lsys_rule_def_t[]){ 576 | { 'X', "F[+X]F[-X]+X" }, 577 | { 'F', "FF" }, 578 | { 0, 0 } 579 | }, 20, "F"); 580 | 581 | lsys_create(KNOWN_LSYSTEMS + LSYS_HILBERT, 582 | "hilbert", "L", 583 | (lsys_rule_def_t[]){ 584 | { 'L', "+RF-LFL-FR+" }, 585 | { 'R', "-LF+RFR+FL-" }, 586 | { 0, 0 } 587 | }, 90, "F"); 588 | 589 | lsys_create(KNOWN_LSYSTEMS + LSYS_PENTAPLEXITY, 590 | "pentaplexity", "F++F++F++F++F", 591 | (lsys_rule_def_t[]){ 592 | { 'F', "F++F++F+++++F-F++F" }, 593 | { 0, 0 } 594 | }, 36, NULL); 595 | 596 | } 597 | 598 | ////////////////////////////////////////////////////////////////////// 599 | 600 | void parse_options(int argc, char** argv, options_t* opts) { 601 | 602 | int ok = 1; 603 | 604 | int disable_precomputed_rotation = 0; 605 | 606 | memset(opts, 0, sizeof(options_t)); 607 | opts->max_segments = 100000; 608 | 609 | int i=1; 610 | int required_count = 0; 611 | 612 | for (; ilsys = KNOWN_LSYSTEMS + j; 626 | break; 627 | } 628 | } 629 | 630 | if (!ok) { break; } 631 | 632 | } else if (required_count == 1) { 633 | 634 | int d; 635 | 636 | if (sscanf(arg, "%d", &d) != 1 || d <= 0) { 637 | ok = 0; 638 | break; 639 | } 640 | 641 | opts->total_iterations = d; 642 | 643 | } else { 644 | 645 | ok = 0; 646 | break; 647 | 648 | } 649 | 650 | ++required_count; 651 | 652 | } else if (!strcmp(arg, "-s")) { 653 | 654 | opts->method = LSYS_METHOD_STRING; 655 | 656 | } else if (!strcmp(arg, "-r")) { 657 | 658 | opts->method = LSYS_METHOD_RECURSION; 659 | 660 | } else if (!strcmp(arg, "-x")) { 661 | 662 | if (++i == argc) { ok = 0; break; } 663 | 664 | int d; 665 | 666 | if (sscanf(argv[i], "%d", &d) != 1) { 667 | ok = 0; break; 668 | } 669 | 670 | if (d >= -1) { 671 | opts->max_segments = (size_t)d; 672 | } else { 673 | ok = 0; 674 | break; 675 | } 676 | 677 | } else if (!strcmp(arg, "-R")) { 678 | 679 | disable_precomputed_rotation = 1; 680 | 681 | } else { 682 | 683 | fprintf(stderr, "error: unrecognized option %s\n\n", arg); 684 | 685 | ok = 0; 686 | break; 687 | 688 | } 689 | 690 | } 691 | 692 | if (!ok || !opts->lsys || !opts->total_iterations) { 693 | printf("usage: %s [options] LSYSTEM ITERATIONS\n" 694 | "\n" 695 | "where LSYSTEM is one of:\n", argv[0]); 696 | for (int j=0; jmethod == LSYS_METHOD_STRING ? "string" : "recursion"); 711 | 712 | if (disable_precomputed_rotation) { 713 | printf("disabling precomputed rotation!\n"); 714 | opts->lsys->rotation_cycle_length = 0; 715 | } 716 | 717 | printf("\n"); 718 | 719 | lsys_print(opts->lsys); 720 | 721 | } 722 | 723 | ////////////////////////////////////////////////////////////////////// 724 | // main program 725 | 726 | int main(int argc, char** argv) { 727 | 728 | // initialize lsystems 729 | initialize_known_lsystems(); 730 | 731 | // parse command-line options 732 | options_t opts; 733 | parse_options(argc, argv, &opts); 734 | 735 | //////////////////////////////////////////////////////////// 736 | // now get the segments 737 | 738 | double start = get_time_as_double(); 739 | 740 | darray_t* segments; 741 | 742 | if (opts.method == LSYS_METHOD_STRING) { 743 | 744 | char* lstring = lsys_build_string(opts.lsys, 745 | opts.total_iterations); 746 | 747 | segments = lsys_segments_from_string(opts.lsys, lstring); 748 | 749 | free(lstring); 750 | 751 | } else { 752 | 753 | segments = lsys_segments_recursive(opts.lsys, opts.total_iterations); 754 | 755 | } 756 | 757 | double elapsed = get_time_as_double() - start; 758 | printf("generated %d segments in %.6f s (%.3f ns/segment).\n", 759 | (int)segments->count, elapsed, 1e9 * elapsed/segments->count); 760 | 761 | //////////////////////////////////////////////////////////// 762 | // either output or not 763 | 764 | if (segments->count > opts.max_segments) { 765 | 766 | printf("...maximum of %d segments exceeded, skipping output!\n", 767 | (int)opts.max_segments); 768 | 769 | } else { 770 | 771 | const lsys_segment_t* segment = (const lsys_segment_t*)segments->data; 772 | const lsys_segment_t* end = segment + segments->count; 773 | 774 | FILE* outfile = fopen("segments.txt", "w"); 775 | 776 | for ( ; segment != end; ++segment) { 777 | fprintf(outfile, "%g %g %g %g\n", 778 | segment->p0.x, segment->p0.y, 779 | segment->p1.x, segment->p1.y); 780 | } 781 | 782 | fclose(outfile); 783 | 784 | printf("wrote segments.txt\n"); 785 | 786 | } 787 | 788 | //////////////////////////////////////////////////////////// 789 | // clean up 790 | 791 | darray_destroy(segments); 792 | free(segments); 793 | 794 | return 0; 795 | 796 | } 797 | -------------------------------------------------------------------------------- /lsystems_v6.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // lsystems_v6.c 4 | // Matt Zucker 5 | // 6 | ////////////////////////////////////////////////////////////////////// 7 | // 8 | // Based on documentation in https://en.wikipedia.org/wiki/L-system and 9 | // http://paulbourke.net/fractals/lsys/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | ////////////////////////////////////////////////////////////////////// 19 | // for benchmarking 20 | 21 | double get_time_as_double(void); 22 | 23 | ////////////////////////////////////////////////////////////////////// 24 | // dynamic array 25 | 26 | // dynamic array data type 27 | typedef struct darray { 28 | 29 | size_t elem_size; 30 | size_t capacity; 31 | size_t count; 32 | 33 | unsigned char* data; 34 | 35 | } darray_t; 36 | 37 | // dynamic array functions 38 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity); 39 | void darray_resize(darray_t* darray, size_t new_count); 40 | void darray_extend(darray_t* darray, const void* elements, size_t count); 41 | void darray_push_back(darray_t* darray, const void* elem); 42 | void darray_pop_back(darray_t* darray, void* elem); 43 | void* darray_elem_ptr(darray_t* darray, size_t idx); 44 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx); 45 | void darray_get(const darray_t* darray, size_t idx, void* dst); 46 | void darray_set(darray_t* darray, size_t idx, const void* src); 47 | void darray_clear(darray_t* darray); 48 | void darray_destroy(darray_t* darray); 49 | 50 | ////////////////////////////////////////////////////////////////////// 51 | // geometry utils 52 | 53 | // 2D point 54 | typedef struct point2d { 55 | float x, y; 56 | } point2d_t; 57 | 58 | // 2D rotation 59 | typedef struct rot2d { 60 | float c, s; 61 | } rot2d_t; 62 | 63 | // 2D transformation 64 | typedef struct xform { 65 | point2d_t pos; 66 | rot2d_t rot; 67 | float angle; 68 | } xform_t; 69 | 70 | // identity transform 71 | static const xform_t IDENTITY_XFORM = { 72 | { 0.f, 0.f, }, { 1.f, 0.f }, 0.f 73 | }; 74 | 75 | // 2D geometry functions 76 | int positive_mod(int ticks, int divisor); 77 | point2d_t rotate_point(const rot2d_t R, const point2d_t p); 78 | rot2d_t rotate_compose(const rot2d_t R2, const rot2d_t R1); 79 | point2d_t translate_point(const point2d_t p, const point2d_t q); 80 | xform_t xform_inverse(xform_t xform); 81 | xform_t xform_compose(xform_t xform2, xform_t xform1); 82 | point2d_t xform_transform_point(xform_t xform, point2d_t p); 83 | 84 | ////////////////////////////////////////////////////////////////////// 85 | // L-System types/functions 86 | 87 | // misc enums 88 | enum { 89 | LSYS_MAX_RULES = 128, 90 | LSYS_MAX_CYCLE_LENGTH = 256, 91 | LSYS_INIT_STRING_CAPACITY = 4096, 92 | LSYS_INIT_STATES_CAPACITY = 64, 93 | LSYS_INIT_SEGMENTS_CAPACITY = 1024 94 | }; 95 | 96 | // line segment is 2 points 97 | typedef struct lsys_segment { 98 | point2d_t p0, p1; 99 | } lsys_segment_t; 100 | 101 | // rule tagged with string length for string replacement 102 | typedef struct lsys_sized_string { 103 | const char* replacement; 104 | size_t length; 105 | } lsys_sized_string_t; 106 | 107 | // L-System datatype 108 | typedef struct lsystem { 109 | 110 | const char* name; 111 | const char* start; 112 | lsys_sized_string_t rules[LSYS_MAX_RULES]; 113 | unsigned char draw_chars[LSYS_MAX_RULES]; 114 | double turn_angle_rad; 115 | int rotation_cycle_length; 116 | rot2d_t rotations[LSYS_MAX_CYCLE_LENGTH]; 117 | 118 | } lsys_t; 119 | 120 | // lsystem character + replacement pair, for defining L-Systems 121 | typedef struct lsys_rule_def { 122 | char symbol; 123 | const char* replacement; 124 | } lsys_rule_def_t; 125 | 126 | // datatype for memoizing a single L-System rule 127 | typedef struct lsys_memo { 128 | 129 | size_t memo_iterations; 130 | 131 | size_t segment_start; 132 | size_t segment_count; 133 | 134 | xform_t init_inverse; 135 | xform_t delta_xform; 136 | 137 | } lsys_memo_t; 138 | 139 | // datatype for memoizing an entire L-system 140 | typedef struct lsys_memo_set { 141 | 142 | size_t total_iterations; 143 | size_t min_memo_segments; 144 | 145 | lsys_memo_t* memos[LSYS_MAX_RULES]; 146 | 147 | } lsys_memo_set_t; 148 | 149 | void lsys_create(lsys_t* lsys, 150 | const char* name, 151 | const char* start, 152 | lsys_rule_def_t const rules[], 153 | double turn_angle_deg, 154 | const char* draw_chars); 155 | 156 | void lsys_print(const lsys_t* lsys); 157 | 158 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations); 159 | 160 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 161 | const char* lstring); 162 | 163 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 164 | size_t total_iterations, 165 | size_t min_memo_segments); 166 | 167 | ////////////////////////////////////////////////////////////////////// 168 | // set up some known L-Systems 169 | 170 | enum { 171 | LSYS_SIERPINSKI_ARROWHEAD = 0, // depth 17 takes ~5 sec 172 | LSYS_SIERPINSKI_TRIANGLE, // depth 16 takes ~3 sec 173 | LSYS_DRAGON_CURVE, // depth 26 takes ~6 sec 174 | LSYS_BARNSLEY_FERN, // depth 13 takes ~7 sec 175 | LSYS_STICKS, // depth 16 takes ~4 sec 176 | LSYS_HILBERT, // depth 13 takes ~3 sec 177 | LSYS_PENTAPLEXITY, // depth 9 takes ~2 sec 178 | NUM_KNOWN_LSYSTEMS 179 | }; 180 | 181 | lsys_t KNOWN_LSYSTEMS[NUM_KNOWN_LSYSTEMS]; 182 | 183 | void initialize_known_lsystems(void); 184 | 185 | ////////////////////////////////////////////////////////////////////// 186 | // options for running this program 187 | 188 | typedef enum lsys_method { 189 | LSYS_METHOD_RECURSION, 190 | LSYS_METHOD_STRING 191 | } lsys_method_t; 192 | 193 | typedef struct options { 194 | lsys_t* lsys; 195 | size_t total_iterations; 196 | size_t max_segments; 197 | lsys_method_t method; 198 | size_t min_memo_segments; 199 | } options_t; 200 | 201 | void parse_options(int argc, char** argv, options_t* opts); 202 | 203 | ////////////////////////////////////////////////////////////////////// 204 | 205 | double get_time_as_double(void) { 206 | 207 | struct timespec tp; 208 | 209 | clock_gettime(CLOCK_REALTIME, &tp); 210 | 211 | return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9; 212 | 213 | } 214 | 215 | ////////////////////////////////////////////////////////////////////// 216 | 217 | int positive_mod(int ticks, int divisor) { 218 | 219 | int rval = ticks % divisor; 220 | if (ticks < 0) { 221 | rval += divisor; 222 | } 223 | 224 | return rval; 225 | 226 | } 227 | 228 | point2d_t rotate_point(const rot2d_t R, const point2d_t p) { 229 | 230 | return (point2d_t){ 231 | R.c * p.x - R.s * p.y, 232 | R.s * p.x + R.c * p.y 233 | }; 234 | 235 | } 236 | 237 | rot2d_t rotate_compose(const rot2d_t R2, const rot2d_t R1) { 238 | 239 | return (rot2d_t) { 240 | R2.c * R1.c - R2.s * R1.s, 241 | R2.s * R1.c + R2.c * R1.s, 242 | }; 243 | 244 | } 245 | 246 | point2d_t translate_point(const point2d_t p, const point2d_t q) { 247 | 248 | return (point2d_t) { p.x + q.x, p.y + q.y }; 249 | 250 | } 251 | 252 | xform_t xform_inverse(xform_t xform) { 253 | 254 | rot2d_t rinv = { xform.rot.c, -xform.rot.s }; 255 | 256 | return (xform_t) { 257 | .pos = rotate_point(rinv, (point2d_t){ -xform.pos.x, -xform.pos.y }), 258 | .rot = rinv, 259 | .angle = -xform.angle 260 | }; 261 | 262 | } 263 | 264 | xform_t xform_compose(xform_t xform2, 265 | xform_t xform1) { 266 | 267 | return (xform_t) { 268 | 269 | .pos = translate_point(xform2.pos, 270 | rotate_point(xform2.rot, xform1.pos)), 271 | 272 | .rot = rotate_compose(xform2.rot, xform1.rot), 273 | 274 | .angle = xform2.angle + xform1.angle 275 | 276 | }; 277 | 278 | } 279 | 280 | point2d_t xform_transform_point(xform_t xform, 281 | point2d_t p) { 282 | 283 | return translate_point(rotate_point(xform.rot, p), xform.pos); 284 | 285 | } 286 | 287 | ////////////////////////////////////////////////////////////////////// 288 | 289 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity) { 290 | 291 | size_t alloc_size = elem_size * capacity; 292 | 293 | darray->elem_size = elem_size; 294 | darray->count = 0; 295 | darray->capacity = capacity; 296 | darray->data = malloc(alloc_size); 297 | 298 | } 299 | 300 | void darray_resize(darray_t* darray, size_t new_count) { 301 | 302 | if (new_count > darray->capacity) { 303 | 304 | size_t new_capacity = darray->capacity; 305 | 306 | while (new_capacity <= new_count) { 307 | new_capacity *= 2; 308 | } 309 | 310 | size_t alloc_size = darray->elem_size * new_capacity; 311 | 312 | darray->data = realloc(darray->data, alloc_size); 313 | darray->capacity = new_capacity; 314 | 315 | } 316 | 317 | darray->count = new_count; 318 | 319 | } 320 | 321 | void darray_extend(darray_t* darray, const void* elements, size_t count) { 322 | 323 | size_t offset = darray->elem_size * darray->count; 324 | 325 | darray_resize(darray, darray->count + count); 326 | 327 | memcpy(darray->data + offset, elements, count*darray->elem_size); 328 | 329 | } 330 | 331 | void darray_push_back(darray_t* darray, const void* elem) { 332 | darray_extend(darray, elem, 1); 333 | } 334 | 335 | void* darray_elem_ptr(darray_t* darray, size_t idx) { 336 | return darray->data + idx*darray->elem_size; 337 | } 338 | 339 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx) { 340 | return darray->data + idx*darray->elem_size; 341 | } 342 | 343 | void darray_get(const darray_t* darray, size_t idx, void* dst) { 344 | memcpy(dst, darray_const_elem_ptr(darray, idx), darray->elem_size); 345 | } 346 | 347 | void darray_set(darray_t* darray, size_t idx, const void* src) { 348 | memcpy(darray_elem_ptr(darray, idx), src, darray->elem_size); 349 | } 350 | 351 | void darray_pop_back(darray_t* darray, void* dst) { 352 | darray->count -= 1; 353 | size_t offset = darray->count * darray->elem_size; 354 | memcpy(dst, (const void*)darray->data + offset, darray->elem_size); 355 | } 356 | 357 | void darray_clear(darray_t* darray) { 358 | darray->count = 0; 359 | } 360 | 361 | void darray_destroy(darray_t* darray) { 362 | free(darray->data); 363 | memset(darray, 0, sizeof(darray_t)); 364 | } 365 | 366 | ////////////////////////////////////////////////////////////////////// 367 | 368 | void lsys_create(lsys_t* lsys, 369 | const char* name, 370 | const char* start, 371 | lsys_rule_def_t const rules[], 372 | double turn_angle_deg, 373 | const char* draw_chars) { 374 | 375 | lsys->name = name; 376 | lsys->start = start; 377 | 378 | memset(lsys->rules, 0, sizeof(lsys->rules)); 379 | memset(lsys->draw_chars, 0, sizeof(lsys->draw_chars)); 380 | 381 | for (const lsys_rule_def_t* src_rule=rules; src_rule->symbol; ++src_rule) { 382 | lsys_sized_string_t* dst_rule = lsys->rules + (int)src_rule->symbol; 383 | dst_rule->replacement = src_rule->replacement; 384 | dst_rule->length = strlen(src_rule->replacement); 385 | } 386 | 387 | lsys->turn_angle_rad = turn_angle_deg * M_PI / 180.f; 388 | 389 | for (int i=0; i<=LSYS_MAX_CYCLE_LENGTH; ++i) { 390 | 391 | if (i > 0 && fmod(turn_angle_deg*i, 360.) == 0) { 392 | lsys->rotation_cycle_length = i; 393 | break; 394 | } 395 | 396 | float theta = lsys->turn_angle_rad * i; 397 | 398 | lsys->rotations[i].c = cosf(theta); 399 | lsys->rotations[i].s = sinf(theta); 400 | 401 | } 402 | 403 | if (draw_chars) { 404 | lsys->draw_chars[0] = 1; 405 | for (const char* c=draw_chars; *c; ++c) { 406 | lsys->draw_chars[(int)*c] = 1; 407 | } 408 | } 409 | 410 | } 411 | 412 | void lsys_print(const lsys_t* lsys) { 413 | 414 | printf("%s:\n", lsys->name); 415 | printf(" start: %s\n", lsys->start); 416 | printf(" rules:\n"); 417 | for (int i=0; irules[i].replacement) { 419 | printf(" %c -> %s\n", i, lsys->rules[i].replacement); 420 | } 421 | } 422 | printf(" turn_angle_deg: %g\n", lsys->turn_angle_rad * 180.f / M_PI); 423 | if (lsys->rotation_cycle_length) { 424 | printf(" rotation_cycle_length: %d\n", lsys->rotation_cycle_length); 425 | } 426 | printf("\n"); 427 | 428 | } 429 | 430 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations) { 431 | 432 | darray_t string_darrays[2]; 433 | 434 | for (int i=0; i<2; ++i) { 435 | darray_create(string_darrays + i, sizeof(char), 436 | LSYS_INIT_STRING_CAPACITY); 437 | } 438 | 439 | int cur_idx = 0; 440 | 441 | darray_extend(string_darrays + cur_idx, 442 | lsys->start, 443 | strlen(lsys->start)); 444 | 445 | for (int i=0; idata; 455 | const char* end = start + src_darray->count; 456 | 457 | for (const char* c=start; c!=end; ++c) { 458 | 459 | const lsys_sized_string_t* rule = lsys->rules + (int)*c; 460 | 461 | if (rule->replacement) { 462 | 463 | darray_extend(dst_darray, 464 | rule->replacement, 465 | rule->length); 466 | 467 | } else { 468 | 469 | darray_push_back(dst_darray, c); 470 | 471 | } 472 | 473 | } 474 | 475 | cur_idx = next_idx; 476 | 477 | } 478 | 479 | const char nul = '\0'; 480 | darray_push_back(string_darrays + cur_idx, &nul); 481 | 482 | darray_destroy(string_darrays + (1 - cur_idx)); 483 | 484 | return (char*)string_darrays[cur_idx].data; 485 | 486 | } 487 | 488 | void _lsys_execute_symbol(const lsys_t* lsys, 489 | const char symbol, 490 | darray_t* segments, 491 | xform_t* state, 492 | darray_t* xform_stack) { 493 | 494 | if (isalpha(symbol)) { 495 | 496 | if (lsys->draw_chars[0] && !lsys->draw_chars[(int)symbol]) { 497 | return; 498 | } 499 | 500 | float xnew = state->pos.x + state->rot.c; 501 | float ynew = state->pos.y + state->rot.s; 502 | 503 | lsys_segment_t seg = { { state->pos.x, state->pos.y}, 504 | { xnew, ynew } }; 505 | 506 | darray_push_back(segments, &seg); 507 | 508 | state->pos.x = xnew; 509 | state->pos.y = ynew; 510 | 511 | } else if (symbol == '+' || symbol == '-') { 512 | 513 | if (lsys->rotation_cycle_length) { 514 | 515 | int delta = (symbol == '+') ? 1 : -1; 516 | 517 | int t = positive_mod((int)state->angle + delta, 518 | lsys->rotation_cycle_length); 519 | 520 | state->angle = t; 521 | state->rot = lsys->rotations[t]; 522 | 523 | } else { 524 | 525 | float delta = ( (symbol == '+') ? 526 | lsys->turn_angle_rad : -lsys->turn_angle_rad ); 527 | 528 | state->angle += delta; 529 | 530 | state->rot.c = cosf(state->angle); 531 | state->rot.s = sinf(state->angle); 532 | 533 | } 534 | 535 | } else if (symbol == '[') { 536 | 537 | darray_push_back(xform_stack, state); 538 | 539 | } else if (symbol == ']') { 540 | 541 | darray_pop_back(xform_stack, state); 542 | 543 | } else { 544 | 545 | fprintf(stderr, "invalid character in string: %c\n", symbol); 546 | exit(1); 547 | 548 | } 549 | 550 | } 551 | 552 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 553 | const char* lstring) { 554 | 555 | darray_t* segments = malloc(sizeof(darray_t)); 556 | darray_create(segments, sizeof(lsys_segment_t), 557 | LSYS_INIT_SEGMENTS_CAPACITY); 558 | 559 | darray_t xform_stack; 560 | darray_create(&xform_stack, sizeof(xform_t), 561 | LSYS_INIT_STATES_CAPACITY); 562 | 563 | xform_t cur_state = IDENTITY_XFORM; 564 | 565 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 566 | 567 | _lsys_execute_symbol(lsys, *psymbol, segments, 568 | &cur_state, &xform_stack); 569 | 570 | } 571 | 572 | darray_destroy(&xform_stack); 573 | 574 | return segments; 575 | 576 | } 577 | 578 | void _lsys_segments_r(const lsys_t* lsys, 579 | const char* lstring, 580 | size_t remaining_iterations, 581 | darray_t* segments, 582 | xform_t* cur_state, 583 | darray_t* xform_stack, 584 | lsys_memo_set_t* mset) { 585 | 586 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 587 | 588 | int symbol = *psymbol; 589 | 590 | const lsys_sized_string_t* rule = lsys->rules + symbol; 591 | 592 | if (remaining_iterations && rule->replacement) { 593 | 594 | size_t segment_start = segments->count; 595 | xform_t xform_start = *cur_state; 596 | 597 | if (mset) { 598 | 599 | lsys_memo_t* memo = mset->memos[symbol]; 600 | 601 | if (memo && memo->memo_iterations == remaining_iterations) { 602 | 603 | xform_t update_xform = 604 | xform_compose(*cur_state, memo->init_inverse); 605 | 606 | darray_resize(segments, segment_start + memo->segment_count); 607 | 608 | const lsys_segment_t* src = 609 | darray_const_elem_ptr(segments, memo->segment_start); 610 | 611 | lsys_segment_t* dst = 612 | darray_elem_ptr(segments, segment_start); 613 | 614 | for (size_t i=0; isegment_count; ++i) { 615 | lsys_segment_t newsrc = { 616 | xform_transform_point(update_xform, src[i].p0), 617 | xform_transform_point(update_xform, src[i].p1) 618 | }; 619 | dst[i] = newsrc; 620 | } 621 | 622 | *cur_state = xform_compose(*cur_state, memo->delta_xform); 623 | 624 | continue; 625 | 626 | } 627 | 628 | } 629 | 630 | _lsys_segments_r(lsys, rule->replacement, 631 | remaining_iterations-1, 632 | segments, cur_state, xform_stack, 633 | mset); 634 | 635 | if (mset && !mset->memos[symbol]) { 636 | 637 | size_t segment_count = segments->count - segment_start; 638 | 639 | if (segment_count > mset->min_memo_segments || 640 | remaining_iterations == mset->total_iterations - 2) { 641 | 642 | lsys_memo_t* new_memo = malloc(sizeof(lsys_memo_t)); 643 | 644 | new_memo->memo_iterations = remaining_iterations; 645 | new_memo->segment_start = segment_start; 646 | new_memo->segment_count = segment_count; 647 | new_memo->init_inverse = xform_inverse(xform_start); 648 | 649 | new_memo->delta_xform = xform_compose(new_memo->init_inverse, 650 | *cur_state); 651 | 652 | mset->memos[symbol] = new_memo; 653 | 654 | } 655 | 656 | } 657 | 658 | } else { 659 | 660 | _lsys_execute_symbol(lsys, *psymbol, segments, 661 | cur_state, xform_stack); 662 | 663 | } 664 | 665 | } 666 | 667 | } 668 | 669 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 670 | size_t total_iterations, 671 | size_t min_memo_segments) { 672 | 673 | darray_t* segments = malloc(sizeof(darray_t)); 674 | darray_create(segments, sizeof(lsys_segment_t), 675 | LSYS_INIT_SEGMENTS_CAPACITY); 676 | 677 | darray_t xform_stack; 678 | darray_create(&xform_stack, sizeof(xform_t), 679 | LSYS_INIT_STATES_CAPACITY); 680 | 681 | xform_t cur_state = IDENTITY_XFORM; 682 | 683 | lsys_memo_set_t mset; 684 | memset(&mset, 0, sizeof(mset)); 685 | 686 | mset.total_iterations = total_iterations; 687 | mset.min_memo_segments = min_memo_segments; 688 | 689 | _lsys_segments_r(lsys, lsys->start, 690 | total_iterations, segments, 691 | &cur_state, &xform_stack, 692 | &mset); 693 | 694 | for (int i=0; imax_segments = 100000; 779 | 780 | int i=1; 781 | int required_count = 0; 782 | 783 | for (; ilsys = KNOWN_LSYSTEMS + j; 797 | break; 798 | } 799 | } 800 | 801 | if (!ok) { break; } 802 | 803 | } else if (required_count == 1) { 804 | 805 | int d; 806 | 807 | if (sscanf(arg, "%d", &d) != 1 || d <= 0) { 808 | ok = 0; 809 | break; 810 | } 811 | 812 | opts->total_iterations = d; 813 | 814 | } else { 815 | 816 | ok = 0; 817 | break; 818 | 819 | } 820 | 821 | ++required_count; 822 | 823 | } else if (!strcmp(arg, "-s")) { 824 | 825 | opts->method = LSYS_METHOD_STRING; 826 | 827 | } else if (!strcmp(arg, "-r")) { 828 | 829 | opts->method = LSYS_METHOD_RECURSION; 830 | 831 | } else if (!strcmp(arg, "-x")) { 832 | 833 | if (++i == argc) { ok = 0; break; } 834 | 835 | int d; 836 | 837 | if (sscanf(argv[i], "%d", &d) != 1) { 838 | ok = 0; break; 839 | } 840 | 841 | if (d >= -1) { 842 | opts->max_segments = (size_t)d; 843 | } else { 844 | ok = 0; 845 | break; 846 | } 847 | 848 | } else if (!strcmp(arg, "-M")) { 849 | 850 | disable_memoization = 1; 851 | 852 | } else if (!strcmp(arg, "-R")) { 853 | 854 | disable_precomputed_rotation = 1; 855 | 856 | } else { 857 | 858 | fprintf(stderr, "error: unrecognized option %s\n\n", arg); 859 | 860 | ok = 0; 861 | break; 862 | 863 | } 864 | 865 | } 866 | 867 | if (!ok || !opts->lsys || !opts->total_iterations) { 868 | printf("usage: %s [options] LSYSTEM ITERATIONS\n" 869 | "\n" 870 | "where LSYSTEM is one of:\n", argv[0]); 871 | for (int j=0; jmethod == LSYS_METHOD_STRING ? "string" : "recursion"); 887 | 888 | if (opts->method == LSYS_METHOD_STRING) { 889 | if (disable_memoization) { 890 | printf("warning: disabling memoization has no effect for string method!\n"); 891 | } 892 | } 893 | 894 | int have_memo = (opts->method == LSYS_METHOD_RECURSION) && !disable_memoization; 895 | 896 | if (!have_memo) { 897 | opts->min_memo_segments = (size_t)-1; 898 | if (opts->method == LSYS_METHOD_RECURSION) { 899 | printf("memoization is disabled\n"); 900 | } 901 | } else { 902 | opts->min_memo_segments = 1000; 903 | printf("memoizing runs with > %d segments\n", 904 | (int)opts->min_memo_segments); 905 | } 906 | 907 | if (disable_precomputed_rotation) { 908 | printf("disabling precomputed rotation!\n"); 909 | opts->lsys->rotation_cycle_length = 0; 910 | } 911 | 912 | printf("\n"); 913 | 914 | lsys_print(opts->lsys); 915 | 916 | } 917 | 918 | ////////////////////////////////////////////////////////////////////// 919 | // main program 920 | 921 | int main(int argc, char** argv) { 922 | 923 | // initialize lsystems 924 | initialize_known_lsystems(); 925 | 926 | // parse command-line options 927 | options_t opts; 928 | parse_options(argc, argv, &opts); 929 | 930 | //////////////////////////////////////////////////////////// 931 | // now get the segments 932 | 933 | double start = get_time_as_double(); 934 | 935 | darray_t* segments; 936 | 937 | if (opts.method == LSYS_METHOD_STRING) { 938 | 939 | char* lstring = lsys_build_string(opts.lsys, 940 | opts.total_iterations); 941 | 942 | segments = lsys_segments_from_string(opts.lsys, lstring); 943 | 944 | free(lstring); 945 | 946 | } else { 947 | 948 | segments = lsys_segments_recursive(opts.lsys, opts.total_iterations, 949 | opts.min_memo_segments); 950 | 951 | } 952 | 953 | double elapsed = get_time_as_double() - start; 954 | printf("generated %d segments in %.6f s (%.3f ns/segment).\n", 955 | (int)segments->count, elapsed, 1e9 * elapsed/segments->count); 956 | 957 | //////////////////////////////////////////////////////////// 958 | // either output or not 959 | 960 | if (segments->count > opts.max_segments) { 961 | 962 | printf("...maximum of %d segments exceeded, skipping output!\n", 963 | (int)opts.max_segments); 964 | 965 | } else { 966 | 967 | const lsys_segment_t* segment = (const lsys_segment_t*)segments->data; 968 | const lsys_segment_t* end = segment + segments->count; 969 | 970 | FILE* outfile = fopen("segments.txt", "w"); 971 | 972 | for ( ; segment != end; ++segment) { 973 | fprintf(outfile, "%g %g %g %g\n", 974 | segment->p0.x, segment->p0.y, 975 | segment->p1.x, segment->p1.y); 976 | } 977 | 978 | fclose(outfile); 979 | 980 | printf("wrote segments.txt\n"); 981 | 982 | } 983 | 984 | //////////////////////////////////////////////////////////// 985 | // clean up 986 | 987 | darray_destroy(segments); 988 | free(segments); 989 | 990 | return 0; 991 | 992 | } 993 | -------------------------------------------------------------------------------- /uncrustify.config: -------------------------------------------------------------------------------- 1 | # Uncrustify-0.70.1_f 2 | newlines = auto 3 | input_tab_size = 4 4 | output_tab_size = 4 5 | string_escape_char = 92 6 | string_escape_char2 = 0 7 | string_replace_tab_chars = false 8 | tok_split_gte = false 9 | disable_processing_cmt = " *INDENT-OFF*" 10 | enable_processing_cmt = " *INDENT-ON*" 11 | enable_digraphs = false 12 | utf8_bom = ignore 13 | utf8_byte = false 14 | utf8_force = false 15 | sp_do_brace_open = ignore 16 | sp_brace_close_while = ignore 17 | sp_while_paren_open = ignore 18 | sp_arith = ignore 19 | sp_arith_additive = ignore 20 | sp_assign = ignore 21 | sp_cpp_lambda_assign = ignore 22 | sp_cpp_lambda_square_paren = ignore 23 | sp_cpp_lambda_square_brace = ignore 24 | sp_cpp_lambda_paren_brace = ignore 25 | sp_cpp_lambda_fparen = ignore 26 | sp_assign_default = ignore 27 | sp_before_assign = ignore 28 | sp_after_assign = ignore 29 | sp_enum_paren = ignore 30 | sp_enum_assign = ignore 31 | sp_enum_before_assign = ignore 32 | sp_enum_after_assign = ignore 33 | sp_enum_colon = ignore 34 | sp_pp_concat = add 35 | sp_pp_stringify = ignore 36 | sp_before_pp_stringify = ignore 37 | sp_bool = ignore 38 | sp_compare = ignore 39 | sp_inside_paren = ignore 40 | sp_paren_paren = ignore 41 | sp_cparen_oparen = ignore 42 | sp_balance_nested_parens = false 43 | sp_paren_brace = ignore 44 | sp_brace_brace = ignore 45 | sp_before_ptr_star = ignore 46 | sp_before_unnamed_ptr_star = ignore 47 | sp_between_ptr_star = ignore 48 | sp_after_ptr_star = ignore 49 | sp_after_ptr_block_caret = ignore 50 | sp_after_ptr_star_qualifier = ignore 51 | sp_after_ptr_star_func = ignore 52 | sp_ptr_star_paren = ignore 53 | sp_before_ptr_star_func = ignore 54 | sp_before_byref = ignore 55 | sp_before_unnamed_byref = ignore 56 | sp_after_byref = ignore 57 | sp_after_byref_func = ignore 58 | sp_before_byref_func = ignore 59 | sp_after_type = force 60 | sp_after_decltype = ignore 61 | sp_before_template_paren = ignore 62 | sp_template_angle = ignore 63 | sp_before_angle = ignore 64 | sp_inside_angle = ignore 65 | sp_inside_angle_empty = ignore 66 | sp_angle_colon = ignore 67 | sp_after_angle = ignore 68 | sp_angle_paren = ignore 69 | sp_angle_paren_empty = ignore 70 | sp_angle_word = ignore 71 | sp_angle_shift = add 72 | sp_permit_cpp11_shift = false 73 | sp_before_sparen = ignore 74 | sp_inside_sparen = ignore 75 | sp_inside_sparen_open = ignore 76 | sp_inside_sparen_close = ignore 77 | sp_after_sparen = ignore 78 | sp_sparen_brace = ignore 79 | sp_invariant_paren = ignore 80 | sp_after_invariant_paren = ignore 81 | sp_special_semi = ignore 82 | sp_before_semi = remove 83 | sp_before_semi_for = ignore 84 | sp_before_semi_for_empty = ignore 85 | sp_after_semi = add 86 | sp_after_semi_for = force 87 | sp_after_semi_for_empty = ignore 88 | sp_before_square = ignore 89 | sp_before_vardef_square = remove 90 | sp_before_square_asm_block = ignore 91 | sp_before_squares = ignore 92 | sp_cpp_before_struct_binding = ignore 93 | sp_inside_square = ignore 94 | sp_inside_square_oc_array = ignore 95 | sp_after_comma = ignore 96 | sp_before_comma = remove 97 | sp_after_mdatype_commas = ignore 98 | sp_before_mdatype_commas = ignore 99 | sp_between_mdatype_commas = ignore 100 | sp_paren_comma = force 101 | sp_before_ellipsis = ignore 102 | sp_type_ellipsis = ignore 103 | sp_type_question = ignore 104 | sp_paren_ellipsis = ignore 105 | sp_paren_qualifier = ignore 106 | sp_paren_noexcept = ignore 107 | sp_after_class_colon = ignore 108 | sp_before_class_colon = ignore 109 | sp_after_constr_colon = ignore 110 | sp_before_constr_colon = ignore 111 | sp_before_case_colon = remove 112 | sp_after_operator = ignore 113 | sp_after_operator_sym = ignore 114 | sp_after_operator_sym_empty = ignore 115 | sp_after_cast = ignore 116 | sp_inside_paren_cast = ignore 117 | sp_cpp_cast_paren = ignore 118 | sp_sizeof_paren = ignore 119 | sp_sizeof_ellipsis = ignore 120 | sp_sizeof_ellipsis_paren = ignore 121 | sp_decltype_paren = ignore 122 | sp_after_tag = ignore 123 | sp_inside_braces_enum = ignore 124 | sp_inside_braces_struct = ignore 125 | sp_inside_braces_oc_dict = ignore 126 | sp_after_type_brace_init_lst_open = ignore 127 | sp_before_type_brace_init_lst_close = ignore 128 | sp_inside_type_brace_init_lst = ignore 129 | sp_inside_braces = ignore 130 | sp_inside_braces_empty = ignore 131 | sp_trailing_return = ignore 132 | sp_type_func = ignore 133 | sp_type_brace_init_lst = ignore 134 | sp_func_proto_paren = ignore 135 | sp_func_proto_paren_empty = ignore 136 | sp_func_type_paren = ignore 137 | sp_func_def_paren = ignore 138 | sp_func_def_paren_empty = ignore 139 | sp_inside_fparens = ignore 140 | sp_inside_fparen = ignore 141 | sp_inside_tparen = ignore 142 | sp_after_tparen_close = ignore 143 | sp_square_fparen = ignore 144 | sp_fparen_brace = ignore 145 | sp_fparen_brace_initializer = ignore 146 | sp_fparen_dbrace = ignore 147 | sp_func_call_paren = ignore 148 | sp_func_call_paren_empty = ignore 149 | sp_func_call_user_paren = ignore 150 | sp_func_call_user_inside_fparen = ignore 151 | sp_func_call_user_paren_paren = ignore 152 | sp_func_class_paren = ignore 153 | sp_func_class_paren_empty = ignore 154 | sp_return_paren = ignore 155 | sp_return_brace = ignore 156 | sp_attribute_paren = ignore 157 | sp_defined_paren = ignore 158 | sp_throw_paren = ignore 159 | sp_after_throw = ignore 160 | sp_catch_paren = ignore 161 | sp_oc_catch_paren = ignore 162 | sp_oc_classname_paren = ignore 163 | sp_version_paren = ignore 164 | sp_scope_paren = ignore 165 | sp_super_paren = remove 166 | sp_this_paren = remove 167 | sp_macro = ignore 168 | sp_macro_func = ignore 169 | sp_else_brace = ignore 170 | sp_brace_else = ignore 171 | sp_brace_typedef = ignore 172 | sp_catch_brace = ignore 173 | sp_oc_catch_brace = ignore 174 | sp_brace_catch = ignore 175 | sp_oc_brace_catch = ignore 176 | sp_finally_brace = ignore 177 | sp_brace_finally = ignore 178 | sp_try_brace = ignore 179 | sp_getset_brace = ignore 180 | sp_word_brace = add 181 | sp_word_brace_ns = add 182 | sp_before_dc = ignore 183 | sp_after_dc = ignore 184 | sp_d_array_colon = ignore 185 | sp_not = remove 186 | sp_inv = remove 187 | sp_addr = remove 188 | sp_member = remove 189 | sp_deref = remove 190 | sp_sign = remove 191 | sp_incdec = remove 192 | sp_before_nl_cont = add 193 | sp_after_oc_scope = ignore 194 | sp_after_oc_colon = ignore 195 | sp_before_oc_colon = ignore 196 | sp_after_oc_dict_colon = ignore 197 | sp_before_oc_dict_colon = ignore 198 | sp_after_send_oc_colon = ignore 199 | sp_before_send_oc_colon = ignore 200 | sp_after_oc_type = ignore 201 | sp_after_oc_return_type = ignore 202 | sp_after_oc_at_sel = ignore 203 | sp_after_oc_at_sel_parens = ignore 204 | sp_inside_oc_at_sel_parens = ignore 205 | sp_before_oc_block_caret = ignore 206 | sp_after_oc_block_caret = ignore 207 | sp_after_oc_msg_receiver = ignore 208 | sp_after_oc_property = ignore 209 | sp_after_oc_synchronized = ignore 210 | sp_cond_colon = ignore 211 | sp_cond_colon_before = ignore 212 | sp_cond_colon_after = ignore 213 | sp_cond_question = ignore 214 | sp_cond_question_before = ignore 215 | sp_cond_question_after = ignore 216 | sp_cond_ternary_short = ignore 217 | sp_case_label = ignore 218 | sp_range = ignore 219 | sp_after_for_colon = ignore 220 | sp_before_for_colon = ignore 221 | sp_extern_paren = ignore 222 | sp_cmt_cpp_start = ignore 223 | sp_cmt_cpp_doxygen = false 224 | sp_cmt_cpp_qttr = false 225 | sp_endif_cmt = ignore 226 | sp_after_new = ignore 227 | sp_between_new_paren = ignore 228 | sp_after_newop_paren = ignore 229 | sp_inside_newop_paren = ignore 230 | sp_inside_newop_paren_open = ignore 231 | sp_inside_newop_paren_close = ignore 232 | sp_before_tr_emb_cmt = ignore 233 | sp_num_before_tr_emb_cmt = 0 234 | sp_annotation_paren = ignore 235 | sp_skip_vbrace_tokens = false 236 | sp_after_noexcept = ignore 237 | sp_vala_after_translation = ignore 238 | force_tab_after_define = false 239 | indent_columns = 4 240 | indent_continue = 0 241 | indent_continue_class_head = 0 242 | indent_single_newlines = false 243 | indent_param = 0 244 | indent_with_tabs = 0 245 | indent_cmt_with_tabs = false 246 | indent_align_string = false 247 | indent_xml_string = 0 248 | indent_brace = 0 249 | indent_braces = 0 250 | indent_braces_no_func = 0 251 | indent_braces_no_class = 0 252 | indent_braces_no_struct = 0 253 | indent_brace_parent = 0 254 | indent_paren_open_brace = true # important 255 | indent_cs_delegate_brace = false 256 | indent_cs_delegate_body = false 257 | indent_namespace = false 258 | indent_namespace_single_indent = false 259 | indent_namespace_level = 0 260 | indent_namespace_limit = 0 261 | indent_extern = false 262 | indent_class = false 263 | indent_class_colon = false 264 | indent_class_on_colon = false 265 | indent_constr_colon = false 266 | indent_ctor_init_leading = 2 267 | indent_ctor_init = 0 268 | indent_else_if = false 269 | indent_var_def_blk = 0 270 | indent_var_def_cont = false 271 | indent_shift = false 272 | indent_func_def_force_col1 = false 273 | indent_func_call_param = false 274 | indent_func_def_param = false 275 | indent_func_def_param_paren_pos_threshold = 0 276 | indent_func_proto_param = false 277 | indent_func_class_param = false 278 | indent_func_ctor_var_param = false 279 | indent_template_param = false 280 | indent_func_param_double = false 281 | indent_func_const = 0 282 | indent_func_throw = 0 283 | indent_macro_brace = true 284 | indent_member = 1 285 | indent_member_single = false 286 | indent_sing_line_comments = 0 287 | indent_relative_single_line_comments = false 288 | indent_switch_case = 0 289 | indent_switch_break_with_case = false 290 | indent_switch_pp = true 291 | indent_case_shift = 0 292 | indent_case_brace = 0 293 | indent_col1_comment = false 294 | indent_col1_multi_string_literal = false 295 | indent_label = 1 296 | indent_access_spec = 1 297 | indent_access_spec_body = false 298 | indent_paren_nl = false 299 | indent_paren_close = 0 300 | indent_paren_after_func_def = false 301 | indent_paren_after_func_decl = false 302 | indent_paren_after_func_call = false 303 | indent_comma_paren = false 304 | indent_bool_paren = false 305 | indent_semicolon_for_paren = false 306 | indent_first_bool_expr = false 307 | indent_first_for_expr = false 308 | indent_square_nl = false 309 | indent_preserve_sql = false 310 | indent_align_assign = true 311 | indent_align_paren = true 312 | indent_oc_block = false 313 | indent_oc_block_msg = 0 314 | indent_oc_msg_colon = 0 315 | indent_oc_msg_prioritize_first_colon = true 316 | indent_oc_block_msg_xcode_style = false 317 | indent_oc_block_msg_from_keyword = false 318 | indent_oc_block_msg_from_colon = false 319 | indent_oc_block_msg_from_caret = false 320 | indent_oc_block_msg_from_brace = false 321 | indent_min_vbrace_open = 0 322 | indent_vbrace_open_on_tabstop = false 323 | indent_token_after_brace = true 324 | indent_cpp_lambda_body = false 325 | indent_using_block = true 326 | indent_ternary_operator = 0 327 | indent_off_after_return_new = false 328 | indent_single_after_return = false 329 | indent_ignore_asm_block = false 330 | nl_collapse_empty_body = false 331 | nl_assign_leave_one_liners = false 332 | nl_class_leave_one_liners = false 333 | nl_enum_leave_one_liners = false 334 | nl_getset_leave_one_liners = false 335 | nl_cs_property_leave_one_liners = false 336 | nl_func_leave_one_liners = false 337 | nl_cpp_lambda_leave_one_liners = false 338 | nl_if_leave_one_liners = false 339 | nl_while_leave_one_liners = false 340 | nl_for_leave_one_liners = false 341 | nl_oc_msg_leave_one_liner = false 342 | nl_oc_mdef_brace = ignore 343 | nl_oc_block_brace = ignore 344 | nl_oc_before_interface = ignore 345 | nl_oc_before_implementation = ignore 346 | nl_oc_before_end = ignore 347 | nl_oc_interface_brace = ignore 348 | nl_oc_implementation_brace = ignore 349 | nl_start_of_file = ignore 350 | nl_start_of_file_min = 0 351 | nl_end_of_file = ignore 352 | nl_end_of_file_min = 0 353 | nl_assign_brace = ignore 354 | nl_assign_square = ignore 355 | nl_tsquare_brace = ignore 356 | nl_after_square_assign = ignore 357 | nl_fcall_brace = ignore 358 | nl_enum_brace = ignore 359 | nl_enum_class = ignore 360 | nl_enum_class_identifier = ignore 361 | nl_enum_identifier_colon = ignore 362 | nl_enum_colon_type = ignore 363 | nl_struct_brace = ignore 364 | nl_union_brace = ignore 365 | nl_if_brace = ignore 366 | nl_brace_else = ignore 367 | nl_elseif_brace = ignore 368 | nl_else_brace = ignore 369 | nl_else_if = ignore 370 | nl_before_if_closing_paren = ignore 371 | nl_brace_finally = ignore 372 | nl_finally_brace = ignore 373 | nl_try_brace = ignore 374 | nl_getset_brace = ignore 375 | nl_for_brace = ignore 376 | nl_catch_brace = ignore 377 | nl_oc_catch_brace = ignore 378 | nl_brace_catch = ignore 379 | nl_oc_brace_catch = ignore 380 | nl_brace_square = ignore 381 | nl_brace_fparen = ignore 382 | nl_while_brace = ignore 383 | nl_scope_brace = ignore 384 | nl_unittest_brace = ignore 385 | nl_version_brace = ignore 386 | nl_using_brace = ignore 387 | nl_brace_brace = ignore 388 | nl_do_brace = ignore 389 | nl_brace_while = ignore 390 | nl_switch_brace = ignore 391 | nl_synchronized_brace = ignore 392 | nl_multi_line_cond = false 393 | nl_multi_line_sparen_open = ignore 394 | nl_multi_line_sparen_close = ignore 395 | nl_multi_line_define = false 396 | nl_before_case = false 397 | nl_after_case = false 398 | nl_case_colon_brace = ignore 399 | nl_before_throw = ignore 400 | nl_namespace_brace = ignore 401 | nl_template_class = ignore 402 | nl_template_class_decl = ignore 403 | nl_template_class_decl_special = ignore 404 | nl_template_class_def = ignore 405 | nl_template_class_def_special = ignore 406 | nl_template_func = ignore 407 | nl_template_func_decl = ignore 408 | nl_template_func_decl_special = ignore 409 | nl_template_func_def = ignore 410 | nl_template_func_def_special = ignore 411 | nl_template_var = ignore 412 | nl_template_using = ignore 413 | nl_class_brace = ignore 414 | nl_class_init_args = ignore 415 | nl_constr_init_args = ignore 416 | nl_enum_own_lines = ignore 417 | nl_func_type_name = ignore 418 | nl_func_type_name_class = ignore 419 | nl_func_class_scope = ignore 420 | nl_func_scope_name = ignore 421 | nl_func_proto_type_name = ignore 422 | nl_func_paren = ignore 423 | nl_func_paren_empty = ignore 424 | nl_func_def_paren = ignore 425 | nl_func_def_paren_empty = ignore 426 | nl_func_call_paren = ignore 427 | nl_func_call_paren_empty = ignore 428 | nl_func_decl_start = ignore 429 | nl_func_def_start = ignore 430 | nl_func_decl_start_single = ignore 431 | nl_func_def_start_single = ignore 432 | nl_func_decl_start_multi_line = false 433 | nl_func_def_start_multi_line = false 434 | nl_func_decl_args = ignore 435 | nl_func_def_args = ignore 436 | nl_func_decl_args_multi_line = false 437 | nl_func_def_args_multi_line = false 438 | nl_func_decl_end = ignore 439 | nl_func_def_end = ignore 440 | nl_func_decl_end_single = ignore 441 | nl_func_def_end_single = ignore 442 | nl_func_decl_end_multi_line = false 443 | nl_func_def_end_multi_line = false 444 | nl_func_decl_empty = ignore 445 | nl_func_def_empty = ignore 446 | nl_func_call_empty = ignore 447 | nl_func_call_start = ignore 448 | nl_func_call_start_multi_line = false 449 | nl_func_call_args_multi_line = false 450 | nl_func_call_end_multi_line = false 451 | nl_template_start = false 452 | nl_template_args = false 453 | nl_template_end = false 454 | nl_oc_msg_args = false 455 | nl_fdef_brace = ignore 456 | nl_fdef_brace_cond = ignore 457 | nl_cpp_ldef_brace = ignore 458 | nl_return_expr = ignore 459 | nl_after_semicolon = false 460 | nl_paren_dbrace_open = ignore 461 | nl_type_brace_init_lst = ignore 462 | nl_type_brace_init_lst_open = ignore 463 | nl_type_brace_init_lst_close = ignore 464 | nl_after_brace_open = false 465 | nl_after_brace_open_cmt = false 466 | nl_after_vbrace_open = false 467 | nl_after_vbrace_open_empty = false 468 | nl_after_brace_close = false 469 | nl_after_vbrace_close = false 470 | nl_brace_struct_var = ignore 471 | nl_define_macro = false 472 | nl_squeeze_paren_close = false 473 | nl_squeeze_ifdef = false 474 | nl_squeeze_ifdef_top_level = false 475 | nl_before_if = ignore 476 | nl_after_if = ignore 477 | nl_before_for = ignore 478 | nl_after_for = ignore 479 | nl_before_while = ignore 480 | nl_after_while = ignore 481 | nl_before_switch = ignore 482 | nl_after_switch = ignore 483 | nl_before_synchronized = ignore 484 | nl_after_synchronized = ignore 485 | nl_before_do = ignore 486 | nl_after_do = ignore 487 | nl_before_return = false 488 | nl_after_return = false 489 | nl_before_member = ignore 490 | nl_after_member = ignore 491 | nl_ds_struct_enum_cmt = false 492 | nl_ds_struct_enum_close_brace = false 493 | nl_class_colon = ignore 494 | nl_constr_colon = ignore 495 | nl_namespace_two_to_one_liner = false 496 | nl_create_if_one_liner = false 497 | nl_create_for_one_liner = false 498 | nl_create_while_one_liner = false 499 | nl_create_func_def_one_liner = false 500 | nl_create_list_one_liner = false 501 | nl_split_if_one_liner = false 502 | nl_split_for_one_liner = false 503 | nl_split_while_one_liner = false 504 | nl_max = 0 505 | nl_max_blank_in_func = 0 506 | nl_before_func_body_proto = 0 507 | nl_before_func_body_def = 0 508 | nl_before_func_class_proto = 0 509 | nl_before_func_class_def = 0 510 | nl_after_func_proto = 0 511 | nl_after_func_proto_group = 0 512 | nl_after_func_class_proto = 0 513 | nl_after_func_class_proto_group = 0 514 | nl_class_leave_one_liner_groups = false 515 | nl_after_func_body = 0 516 | nl_after_func_body_class = 0 517 | nl_after_func_body_one_liner = 0 518 | nl_func_var_def_blk = 0 519 | nl_typedef_blk_start = 0 520 | nl_typedef_blk_end = 0 521 | nl_typedef_blk_in = 0 522 | nl_var_def_blk_start = 0 523 | nl_var_def_blk_end = 0 524 | nl_var_def_blk_in = 0 525 | nl_before_block_comment = 0 526 | nl_before_c_comment = 0 527 | nl_before_cpp_comment = 0 528 | nl_after_multiline_comment = false 529 | nl_after_label_colon = false 530 | nl_after_struct = 0 531 | nl_before_class = 0 532 | nl_after_class = 0 533 | nl_before_namespace = 0 534 | nl_inside_namespace = 0 535 | nl_after_namespace = 0 536 | nl_before_access_spec = 0 537 | nl_after_access_spec = 0 538 | nl_comment_func_def = 0 539 | nl_after_try_catch_finally = 0 540 | nl_around_cs_property = 0 541 | nl_between_get_set = 0 542 | nl_property_brace = ignore 543 | eat_blanks_after_open_brace = false 544 | eat_blanks_before_close_brace = false 545 | nl_remove_extra_newlines = 0 546 | nl_after_annotation = ignore 547 | nl_between_annotation = ignore 548 | pos_arith = ignore 549 | pos_assign = ignore 550 | pos_bool = ignore 551 | pos_compare = ignore 552 | pos_conditional = ignore 553 | pos_comma = ignore 554 | pos_enum_comma = ignore 555 | pos_class_comma = ignore 556 | pos_constr_comma = ignore 557 | pos_class_colon = ignore 558 | pos_constr_colon = ignore 559 | code_width = 0 560 | ls_for_split_full = false 561 | ls_func_split_full = false 562 | ls_code_width = false 563 | align_keep_tabs = false 564 | align_with_tabs = false 565 | align_on_tabstop = false 566 | align_number_right = false 567 | align_keep_extra_space = false 568 | align_func_params = false 569 | align_func_params_span = 0 570 | align_func_params_thresh = 0 571 | align_func_params_gap = 0 572 | align_constr_value_span = 0 573 | align_constr_value_thresh = 0 574 | align_constr_value_gap = 0 575 | align_same_func_call_params = false 576 | align_same_func_call_params_span = 0 577 | align_same_func_call_params_thresh = 0 578 | align_var_def_span = 0 579 | align_var_def_star_style = 0 580 | align_var_def_amp_style = 0 581 | align_var_def_thresh = 0 582 | align_var_def_gap = 0 583 | align_var_def_colon = false 584 | align_var_def_colon_gap = 0 585 | align_var_def_attribute = false 586 | align_var_def_inline = false 587 | align_assign_span = 0 588 | align_assign_func_proto_span = 0 589 | align_assign_thresh = 0 590 | align_assign_decl_func = 0 591 | align_enum_equ_span = 0 592 | align_enum_equ_thresh = 0 593 | align_var_class_span = 0 594 | align_var_class_thresh = 0 595 | align_var_class_gap = 0 596 | align_var_struct_span = 1 # does nice aligny things 597 | align_var_struct_thresh = 0 598 | align_var_struct_gap = 0 599 | align_struct_init_span = 0 600 | align_typedef_span = 0 601 | align_typedef_gap = 0 602 | align_typedef_func = 0 603 | align_typedef_star_style = 0 604 | align_typedef_amp_style = 0 605 | align_right_cmt_span = 0 606 | align_right_cmt_gap = 0 607 | align_right_cmt_mix = false 608 | align_right_cmt_same_level = false 609 | align_right_cmt_at_col = 0 610 | align_func_proto_span = 0 611 | align_func_proto_thresh = 0 612 | align_func_proto_gap = 0 613 | align_on_operator = false 614 | align_mix_var_proto = false 615 | align_single_line_func = false 616 | align_single_line_brace = false 617 | align_single_line_brace_gap = 0 618 | align_oc_msg_spec_span = 0 619 | align_nl_cont = false 620 | align_pp_define_together = false 621 | align_pp_define_span = 0 622 | align_pp_define_gap = 0 623 | align_left_shift = true 624 | align_asm_colon = false 625 | align_oc_msg_colon_span = 0 626 | align_oc_msg_colon_first = false 627 | align_oc_decl_colon = false 628 | cmt_width = 0 629 | cmt_reflow_mode = 0 630 | cmt_convert_tab_to_spaces = false 631 | cmt_indent_multi = true 632 | cmt_c_group = false 633 | cmt_c_nl_start = false 634 | cmt_c_nl_end = false 635 | cmt_cpp_to_c = false 636 | cmt_cpp_group = false 637 | cmt_cpp_nl_start = false 638 | cmt_cpp_nl_end = false 639 | cmt_star_cont = false 640 | cmt_sp_before_star_cont = 0 641 | cmt_sp_after_star_cont = 0 642 | cmt_multi_check_last = true 643 | cmt_multi_first_len_minimum = 4 644 | cmt_insert_file_header = "" 645 | cmt_insert_file_footer = "" 646 | cmt_insert_func_header = "" 647 | cmt_insert_class_header = "" 648 | cmt_insert_oc_msg_header = "" 649 | cmt_insert_before_preproc = false 650 | cmt_insert_before_inlines = true 651 | cmt_insert_before_ctor_dtor = false 652 | mod_full_brace_do = ignore 653 | mod_full_brace_for = ignore 654 | mod_full_brace_function = ignore 655 | mod_full_brace_if = ignore 656 | mod_full_brace_if_chain = false 657 | mod_full_brace_if_chain_only = false 658 | mod_full_brace_while = ignore 659 | mod_full_brace_using = ignore 660 | mod_full_brace_nl = 0 661 | mod_full_brace_nl_block_rem_mlcond = false 662 | mod_paren_on_return = ignore 663 | mod_pawn_semicolon = false 664 | mod_full_paren_if_bool = false 665 | mod_remove_extra_semicolon = false 666 | mod_add_long_function_closebrace_comment = 0 667 | mod_add_long_namespace_closebrace_comment = 0 668 | mod_add_long_class_closebrace_comment = 0 669 | mod_add_long_switch_closebrace_comment = 0 670 | mod_add_long_ifdef_endif_comment = 0 671 | mod_add_long_ifdef_else_comment = 0 672 | mod_sort_case_sensitive = false 673 | mod_sort_import = false 674 | mod_sort_using = false 675 | mod_sort_include = false 676 | mod_move_case_break = false 677 | mod_case_brace = ignore 678 | mod_remove_empty_return = false 679 | mod_enum_last_comma = ignore 680 | mod_sort_oc_properties = false 681 | mod_sort_oc_property_class_weight = 0 682 | mod_sort_oc_property_thread_safe_weight = 0 683 | mod_sort_oc_property_readwrite_weight = 0 684 | mod_sort_oc_property_reference_weight = 0 685 | mod_sort_oc_property_getter_weight = 0 686 | mod_sort_oc_property_setter_weight = 0 687 | mod_sort_oc_property_nullability_weight = 0 688 | pp_indent = ignore 689 | pp_indent_at_level = false 690 | pp_indent_count = 1 691 | pp_space = ignore 692 | pp_space_count = 0 693 | pp_indent_region = 0 694 | pp_region_indent_code = false 695 | pp_indent_if = 0 696 | pp_if_indent_code = false 697 | pp_define_at_level = false 698 | pp_ignore_define_body = false 699 | pp_indent_case = true 700 | pp_indent_func_def = true 701 | pp_indent_extern = true 702 | pp_indent_brace = true 703 | include_category_0 = "" 704 | include_category_1 = "" 705 | include_category_2 = "" 706 | use_indent_func_call_param = true 707 | use_indent_continue_only_once = false 708 | indent_cpp_lambda_only_once = false 709 | use_sp_after_angle_always = false 710 | use_options_overriding_for_qt_macros = true 711 | warn_level_tabs_found_in_verbatim_string_literals = 2 712 | # option(s) with 'not default' value: 0 713 | # 714 | -------------------------------------------------------------------------------- /lsystems_v7.c: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////// 2 | // 3 | // lsystems_v7.c 4 | // Matt Zucker 5 | // 6 | ////////////////////////////////////////////////////////////////////// 7 | // 8 | // Based on documentation in https://en.wikipedia.org/wiki/L-system and 9 | // http://paulbourke.net/fractals/lsys/ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef _OPENMP 19 | #include 20 | #endif 21 | 22 | ////////////////////////////////////////////////////////////////////// 23 | // for benchmarking 24 | 25 | double get_time_as_double(void); 26 | 27 | ////////////////////////////////////////////////////////////////////// 28 | // dynamic array 29 | 30 | // dynamic array data type 31 | typedef struct darray { 32 | 33 | size_t elem_size; 34 | size_t capacity; 35 | size_t count; 36 | 37 | unsigned char* data; 38 | 39 | } darray_t; 40 | 41 | // dynamic array functions 42 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity); 43 | void darray_resize(darray_t* darray, size_t new_count); 44 | void darray_extend(darray_t* darray, const void* elements, size_t count); 45 | void darray_push_back(darray_t* darray, const void* elem); 46 | void darray_pop_back(darray_t* darray, void* elem); 47 | void* darray_elem_ptr(darray_t* darray, size_t idx); 48 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx); 49 | void darray_get(const darray_t* darray, size_t idx, void* dst); 50 | void darray_set(darray_t* darray, size_t idx, const void* src); 51 | void darray_clear(darray_t* darray); 52 | void darray_destroy(darray_t* darray); 53 | 54 | ////////////////////////////////////////////////////////////////////// 55 | // geometry utils 56 | 57 | // 2D point 58 | typedef struct point2d { 59 | float x, y; 60 | } point2d_t; 61 | 62 | // 2D rotation 63 | typedef struct rot2d { 64 | float c, s; 65 | } rot2d_t; 66 | 67 | // 2D transformation 68 | typedef struct xform { 69 | point2d_t pos; 70 | rot2d_t rot; 71 | float angle; 72 | } xform_t; 73 | 74 | // identity transform 75 | static const xform_t IDENTITY_XFORM = { 76 | { 0.f, 0.f, }, { 1.f, 0.f }, 0.f 77 | }; 78 | 79 | // 2D geometry functions 80 | int positive_mod(int ticks, int divisor); 81 | point2d_t rotate_point(const rot2d_t R, const point2d_t p); 82 | rot2d_t rotate_compose(const rot2d_t R2, const rot2d_t R1); 83 | point2d_t translate_point(const point2d_t p, const point2d_t q); 84 | xform_t xform_inverse(xform_t xform); 85 | xform_t xform_compose(xform_t xform2, xform_t xform1); 86 | point2d_t xform_transform_point(xform_t xform, point2d_t p); 87 | 88 | ////////////////////////////////////////////////////////////////////// 89 | // L-System types/functions 90 | 91 | // misc enums 92 | enum { 93 | LSYS_MAX_RULES = 128, 94 | LSYS_MAX_CYCLE_LENGTH = 256, 95 | LSYS_INIT_STRING_CAPACITY = 4096, 96 | LSYS_INIT_STATES_CAPACITY = 64, 97 | LSYS_INIT_SEGMENTS_CAPACITY = 1024 98 | }; 99 | 100 | // line segment is 2 points 101 | typedef struct lsys_segment { 102 | point2d_t p0, p1; 103 | } lsys_segment_t; 104 | 105 | // rule tagged with string length for string replacement 106 | typedef struct lsys_sized_string { 107 | const char* replacement; 108 | size_t length; 109 | } lsys_sized_string_t; 110 | 111 | // L-System datatype 112 | typedef struct lsystem { 113 | 114 | const char* name; 115 | const char* start; 116 | lsys_sized_string_t rules[LSYS_MAX_RULES]; 117 | unsigned char draw_chars[LSYS_MAX_RULES]; 118 | double turn_angle_rad; 119 | int rotation_cycle_length; 120 | rot2d_t rotations[LSYS_MAX_CYCLE_LENGTH]; 121 | 122 | } lsys_t; 123 | 124 | // lsystem character + replacement pair, for defining L-Systems 125 | typedef struct lsys_rule_def { 126 | char symbol; 127 | const char* replacement; 128 | } lsys_rule_def_t; 129 | 130 | // datatype for memoizing a single L-System rule 131 | typedef struct lsys_memo { 132 | 133 | size_t memo_iterations; 134 | 135 | size_t segment_start; 136 | size_t segment_count; 137 | 138 | xform_t init_inverse; 139 | xform_t delta_xform; 140 | 141 | } lsys_memo_t; 142 | 143 | // datatype for memoizing an entire L-system 144 | typedef struct lsys_memo_set { 145 | 146 | size_t total_iterations; 147 | size_t min_memo_segments; 148 | size_t min_parallel_segments; 149 | 150 | lsys_memo_t* memos[LSYS_MAX_RULES]; 151 | 152 | } lsys_memo_set_t; 153 | 154 | void lsys_create(lsys_t* lsys, 155 | const char* name, 156 | const char* start, 157 | lsys_rule_def_t const rules[], 158 | double turn_angle_deg, 159 | const char* draw_chars); 160 | 161 | void lsys_print(const lsys_t* lsys); 162 | 163 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations); 164 | 165 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 166 | const char* lstring); 167 | 168 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 169 | size_t total_iterations, 170 | size_t min_memo_segments, 171 | size_t min_parallel_segments); 172 | 173 | ////////////////////////////////////////////////////////////////////// 174 | // set up some known L-Systems 175 | 176 | enum { 177 | LSYS_SIERPINSKI_ARROWHEAD = 0, // depth 17 takes ~5 sec 178 | LSYS_SIERPINSKI_TRIANGLE, // depth 16 takes ~3 sec 179 | LSYS_DRAGON_CURVE, // depth 26 takes ~6 sec 180 | LSYS_BARNSLEY_FERN, // depth 13 takes ~7 sec 181 | LSYS_STICKS, // depth 16 takes ~4 sec 182 | LSYS_HILBERT, // depth 13 takes ~3 sec 183 | LSYS_PENTAPLEXITY, // depth 9 takes ~2 sec 184 | NUM_KNOWN_LSYSTEMS 185 | }; 186 | 187 | lsys_t KNOWN_LSYSTEMS[NUM_KNOWN_LSYSTEMS]; 188 | 189 | void initialize_known_lsystems(void); 190 | 191 | ////////////////////////////////////////////////////////////////////// 192 | // options for running this program 193 | 194 | typedef enum lsys_method { 195 | LSYS_METHOD_RECURSION, 196 | LSYS_METHOD_STRING 197 | } lsys_method_t; 198 | 199 | typedef struct options { 200 | lsys_t* lsys; 201 | size_t total_iterations; 202 | size_t max_segments; 203 | lsys_method_t method; 204 | size_t min_memo_segments; 205 | size_t min_parallel_segments; 206 | } options_t; 207 | 208 | void parse_options(int argc, char** argv, options_t* opts); 209 | 210 | ////////////////////////////////////////////////////////////////////// 211 | 212 | double get_time_as_double(void) { 213 | 214 | struct timespec tp; 215 | 216 | clock_gettime(CLOCK_REALTIME, &tp); 217 | 218 | return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9; 219 | 220 | } 221 | 222 | ////////////////////////////////////////////////////////////////////// 223 | 224 | int positive_mod(int ticks, int divisor) { 225 | 226 | int rval = ticks % divisor; 227 | if (ticks < 0) { 228 | rval += divisor; 229 | } 230 | 231 | return rval; 232 | 233 | } 234 | 235 | point2d_t rotate_point(const rot2d_t R, const point2d_t p) { 236 | 237 | return (point2d_t){ 238 | R.c * p.x - R.s * p.y, 239 | R.s * p.x + R.c * p.y 240 | }; 241 | 242 | } 243 | 244 | rot2d_t rotate_compose(const rot2d_t R2, const rot2d_t R1) { 245 | 246 | return (rot2d_t) { 247 | R2.c * R1.c - R2.s * R1.s, 248 | R2.s * R1.c + R2.c * R1.s, 249 | }; 250 | 251 | } 252 | 253 | point2d_t translate_point(const point2d_t p, const point2d_t q) { 254 | 255 | return (point2d_t) { p.x + q.x, p.y + q.y }; 256 | 257 | } 258 | 259 | xform_t xform_inverse(xform_t xform) { 260 | 261 | rot2d_t rinv = { xform.rot.c, -xform.rot.s }; 262 | 263 | return (xform_t) { 264 | .pos = rotate_point(rinv, (point2d_t){ -xform.pos.x, -xform.pos.y }), 265 | .rot = rinv, 266 | .angle = -xform.angle 267 | }; 268 | 269 | } 270 | 271 | xform_t xform_compose(xform_t xform2, 272 | xform_t xform1) { 273 | 274 | return (xform_t) { 275 | 276 | .pos = translate_point(xform2.pos, 277 | rotate_point(xform2.rot, xform1.pos)), 278 | 279 | .rot = rotate_compose(xform2.rot, xform1.rot), 280 | 281 | .angle = xform2.angle + xform1.angle 282 | 283 | }; 284 | 285 | } 286 | 287 | point2d_t xform_transform_point(xform_t xform, 288 | point2d_t p) { 289 | 290 | return translate_point(rotate_point(xform.rot, p), xform.pos); 291 | 292 | } 293 | 294 | ////////////////////////////////////////////////////////////////////// 295 | 296 | void darray_create(darray_t* darray, size_t elem_size, size_t capacity) { 297 | 298 | size_t alloc_size = elem_size * capacity; 299 | 300 | darray->elem_size = elem_size; 301 | darray->count = 0; 302 | darray->capacity = capacity; 303 | darray->data = malloc(alloc_size); 304 | 305 | } 306 | 307 | void darray_resize(darray_t* darray, size_t new_count) { 308 | 309 | if (new_count > darray->capacity) { 310 | 311 | size_t new_capacity = darray->capacity; 312 | 313 | while (new_capacity <= new_count) { 314 | new_capacity *= 2; 315 | } 316 | 317 | size_t alloc_size = darray->elem_size * new_capacity; 318 | 319 | darray->data = realloc(darray->data, alloc_size); 320 | darray->capacity = new_capacity; 321 | 322 | } 323 | 324 | darray->count = new_count; 325 | 326 | } 327 | 328 | void darray_extend(darray_t* darray, const void* elements, size_t count) { 329 | 330 | size_t offset = darray->elem_size * darray->count; 331 | 332 | darray_resize(darray, darray->count + count); 333 | 334 | memcpy(darray->data + offset, elements, count*darray->elem_size); 335 | 336 | } 337 | 338 | void darray_push_back(darray_t* darray, const void* elem) { 339 | darray_extend(darray, elem, 1); 340 | } 341 | 342 | void* darray_elem_ptr(darray_t* darray, size_t idx) { 343 | return darray->data + idx*darray->elem_size; 344 | } 345 | 346 | const void* darray_const_elem_ptr(const darray_t* darray, size_t idx) { 347 | return darray->data + idx*darray->elem_size; 348 | } 349 | 350 | void darray_get(const darray_t* darray, size_t idx, void* dst) { 351 | memcpy(dst, darray_const_elem_ptr(darray, idx), darray->elem_size); 352 | } 353 | 354 | void darray_set(darray_t* darray, size_t idx, const void* src) { 355 | memcpy(darray_elem_ptr(darray, idx), src, darray->elem_size); 356 | } 357 | 358 | void darray_pop_back(darray_t* darray, void* dst) { 359 | darray->count -= 1; 360 | size_t offset = darray->count * darray->elem_size; 361 | memcpy(dst, (const void*)darray->data + offset, darray->elem_size); 362 | } 363 | 364 | void darray_clear(darray_t* darray) { 365 | darray->count = 0; 366 | } 367 | 368 | void darray_destroy(darray_t* darray) { 369 | free(darray->data); 370 | memset(darray, 0, sizeof(darray_t)); 371 | } 372 | 373 | ////////////////////////////////////////////////////////////////////// 374 | 375 | void lsys_create(lsys_t* lsys, 376 | const char* name, 377 | const char* start, 378 | lsys_rule_def_t const rules[], 379 | double turn_angle_deg, 380 | const char* draw_chars) { 381 | 382 | lsys->name = name; 383 | lsys->start = start; 384 | 385 | memset(lsys->rules, 0, sizeof(lsys->rules)); 386 | memset(lsys->draw_chars, 0, sizeof(lsys->draw_chars)); 387 | 388 | for (const lsys_rule_def_t* src_rule=rules; src_rule->symbol; ++src_rule) { 389 | lsys_sized_string_t* dst_rule = lsys->rules + (int)src_rule->symbol; 390 | dst_rule->replacement = src_rule->replacement; 391 | dst_rule->length = strlen(src_rule->replacement); 392 | } 393 | 394 | lsys->turn_angle_rad = turn_angle_deg * M_PI / 180.f; 395 | 396 | for (int i=0; i<=LSYS_MAX_CYCLE_LENGTH; ++i) { 397 | 398 | if (i > 0 && fmod(turn_angle_deg*i, 360.) == 0) { 399 | lsys->rotation_cycle_length = i; 400 | break; 401 | } 402 | 403 | float theta = lsys->turn_angle_rad * i; 404 | 405 | lsys->rotations[i].c = cosf(theta); 406 | lsys->rotations[i].s = sinf(theta); 407 | 408 | } 409 | 410 | if (draw_chars) { 411 | lsys->draw_chars[0] = 1; 412 | for (const char* c=draw_chars; *c; ++c) { 413 | lsys->draw_chars[(int)*c] = 1; 414 | } 415 | } 416 | 417 | } 418 | 419 | void lsys_print(const lsys_t* lsys) { 420 | 421 | printf("%s:\n", lsys->name); 422 | printf(" start: %s\n", lsys->start); 423 | printf(" rules:\n"); 424 | for (int i=0; irules[i].replacement) { 426 | printf(" %c -> %s\n", i, lsys->rules[i].replacement); 427 | } 428 | } 429 | printf(" turn_angle_deg: %g\n", lsys->turn_angle_rad * 180.f / M_PI); 430 | if (lsys->rotation_cycle_length) { 431 | printf(" rotation_cycle_length: %d\n", lsys->rotation_cycle_length); 432 | } 433 | printf("\n"); 434 | 435 | } 436 | 437 | char* lsys_build_string(const lsys_t* lsys, size_t total_iterations) { 438 | 439 | darray_t string_darrays[2]; 440 | 441 | for (int i=0; i<2; ++i) { 442 | darray_create(string_darrays + i, sizeof(char), 443 | LSYS_INIT_STRING_CAPACITY); 444 | } 445 | 446 | int cur_idx = 0; 447 | 448 | darray_extend(string_darrays + cur_idx, 449 | lsys->start, 450 | strlen(lsys->start)); 451 | 452 | for (int i=0; idata; 462 | const char* end = start + src_darray->count; 463 | 464 | for (const char* c=start; c!=end; ++c) { 465 | 466 | const lsys_sized_string_t* rule = lsys->rules + (int)*c; 467 | 468 | if (rule->replacement) { 469 | 470 | darray_extend(dst_darray, 471 | rule->replacement, 472 | rule->length); 473 | 474 | } else { 475 | 476 | darray_push_back(dst_darray, c); 477 | 478 | } 479 | 480 | } 481 | 482 | cur_idx = next_idx; 483 | 484 | } 485 | 486 | const char nul = '\0'; 487 | darray_push_back(string_darrays + cur_idx, &nul); 488 | 489 | darray_destroy(string_darrays + (1 - cur_idx)); 490 | 491 | return (char*)string_darrays[cur_idx].data; 492 | 493 | } 494 | 495 | void _lsys_execute_symbol(const lsys_t* lsys, 496 | const char symbol, 497 | darray_t* segments, 498 | xform_t* state, 499 | darray_t* xform_stack) { 500 | 501 | if (isalpha(symbol)) { 502 | 503 | if (lsys->draw_chars[0] && !lsys->draw_chars[(int)symbol]) { 504 | return; 505 | } 506 | 507 | float xnew = state->pos.x + state->rot.c; 508 | float ynew = state->pos.y + state->rot.s; 509 | 510 | lsys_segment_t seg = { { state->pos.x, state->pos.y}, 511 | { xnew, ynew } }; 512 | 513 | darray_push_back(segments, &seg); 514 | 515 | state->pos.x = xnew; 516 | state->pos.y = ynew; 517 | 518 | } else if (symbol == '+' || symbol == '-') { 519 | 520 | if (lsys->rotation_cycle_length) { 521 | 522 | int delta = (symbol == '+') ? 1 : -1; 523 | 524 | int t = positive_mod((int)state->angle + delta, 525 | lsys->rotation_cycle_length); 526 | 527 | state->angle = t; 528 | state->rot = lsys->rotations[t]; 529 | 530 | } else { 531 | 532 | float delta = ( (symbol == '+') ? 533 | lsys->turn_angle_rad : -lsys->turn_angle_rad ); 534 | 535 | state->angle += delta; 536 | 537 | state->rot.c = cosf(state->angle); 538 | state->rot.s = sinf(state->angle); 539 | 540 | } 541 | 542 | } else if (symbol == '[') { 543 | 544 | darray_push_back(xform_stack, state); 545 | 546 | } else if (symbol == ']') { 547 | 548 | darray_pop_back(xform_stack, state); 549 | 550 | } else { 551 | 552 | fprintf(stderr, "invalid character in string: %c\n", symbol); 553 | exit(1); 554 | 555 | } 556 | 557 | } 558 | 559 | darray_t* lsys_segments_from_string(const lsys_t* lsys, 560 | const char* lstring) { 561 | 562 | darray_t* segments = malloc(sizeof(darray_t)); 563 | darray_create(segments, sizeof(lsys_segment_t), 564 | LSYS_INIT_SEGMENTS_CAPACITY); 565 | 566 | darray_t xform_stack; 567 | darray_create(&xform_stack, sizeof(xform_t), 568 | LSYS_INIT_STATES_CAPACITY); 569 | 570 | xform_t cur_state = IDENTITY_XFORM; 571 | 572 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 573 | 574 | _lsys_execute_symbol(lsys, *psymbol, segments, 575 | &cur_state, &xform_stack); 576 | 577 | } 578 | 579 | darray_destroy(&xform_stack); 580 | 581 | return segments; 582 | 583 | } 584 | 585 | void _lsys_segments_r(const lsys_t* lsys, 586 | const char* lstring, 587 | size_t remaining_iterations, 588 | darray_t* segments, 589 | xform_t* cur_state, 590 | darray_t* xform_stack, 591 | lsys_memo_set_t* mset) { 592 | 593 | for (const char* psymbol=lstring; *psymbol; ++psymbol) { 594 | 595 | int symbol = *psymbol; 596 | 597 | const lsys_sized_string_t* rule = lsys->rules + symbol; 598 | 599 | if (remaining_iterations && rule->replacement) { 600 | 601 | size_t segment_start = segments->count; 602 | xform_t xform_start = *cur_state; 603 | 604 | if (mset) { 605 | 606 | lsys_memo_t* memo = mset->memos[symbol]; 607 | 608 | if (memo && memo->memo_iterations == remaining_iterations) { 609 | 610 | xform_t update_xform = 611 | xform_compose(*cur_state, memo->init_inverse); 612 | 613 | darray_resize(segments, segment_start + memo->segment_count); 614 | 615 | const lsys_segment_t* src = 616 | darray_const_elem_ptr(segments, memo->segment_start); 617 | 618 | lsys_segment_t* dst = 619 | darray_elem_ptr(segments, segment_start); 620 | 621 | #ifdef _OPENMP 622 | int do_parallelize = 623 | (memo->segment_count >= mset->min_parallel_segments); 624 | #endif 625 | 626 | #pragma omp parallel for if (do_parallelize) 627 | for (size_t i=0; isegment_count; ++i) { 628 | lsys_segment_t newsrc = { 629 | xform_transform_point(update_xform, src[i].p0), 630 | xform_transform_point(update_xform, src[i].p1) 631 | }; 632 | dst[i] = newsrc; 633 | } 634 | 635 | *cur_state = xform_compose(*cur_state, memo->delta_xform); 636 | 637 | continue; 638 | 639 | } 640 | 641 | } 642 | 643 | _lsys_segments_r(lsys, rule->replacement, 644 | remaining_iterations-1, 645 | segments, cur_state, xform_stack, 646 | mset); 647 | 648 | if (mset && !mset->memos[symbol]) { 649 | 650 | size_t segment_count = segments->count - segment_start; 651 | 652 | if (segment_count > mset->min_memo_segments || 653 | remaining_iterations == mset->total_iterations - 2) { 654 | 655 | lsys_memo_t* new_memo = malloc(sizeof(lsys_memo_t)); 656 | 657 | new_memo->memo_iterations = remaining_iterations; 658 | new_memo->segment_start = segment_start; 659 | new_memo->segment_count = segment_count; 660 | new_memo->init_inverse = xform_inverse(xform_start); 661 | 662 | new_memo->delta_xform = xform_compose(new_memo->init_inverse, 663 | *cur_state); 664 | 665 | mset->memos[symbol] = new_memo; 666 | 667 | } 668 | 669 | } 670 | 671 | } else { 672 | 673 | _lsys_execute_symbol(lsys, *psymbol, segments, 674 | cur_state, xform_stack); 675 | 676 | } 677 | 678 | } 679 | 680 | } 681 | 682 | darray_t* lsys_segments_recursive(const lsys_t* lsys, 683 | size_t total_iterations, 684 | size_t min_memo_segments, 685 | size_t min_parallel_segments) { 686 | 687 | darray_t* segments = malloc(sizeof(darray_t)); 688 | darray_create(segments, sizeof(lsys_segment_t), 689 | LSYS_INIT_SEGMENTS_CAPACITY); 690 | 691 | darray_t xform_stack; 692 | darray_create(&xform_stack, sizeof(xform_t), 693 | LSYS_INIT_STATES_CAPACITY); 694 | 695 | xform_t cur_state = IDENTITY_XFORM; 696 | 697 | lsys_memo_set_t mset; 698 | memset(&mset, 0, sizeof(mset)); 699 | 700 | mset.total_iterations = total_iterations; 701 | mset.min_memo_segments = min_memo_segments; 702 | mset.min_parallel_segments = min_parallel_segments; 703 | 704 | _lsys_segments_r(lsys, lsys->start, 705 | total_iterations, segments, 706 | &cur_state, &xform_stack, 707 | &mset); 708 | 709 | for (int i=0; imax_segments = 100000; 795 | 796 | int i=1; 797 | int required_count = 0; 798 | 799 | for (; ilsys = KNOWN_LSYSTEMS + j; 813 | break; 814 | } 815 | } 816 | 817 | if (!ok) { break; } 818 | 819 | } else if (required_count == 1) { 820 | 821 | int d; 822 | 823 | if (sscanf(arg, "%d", &d) != 1 || d <= 0) { 824 | ok = 0; 825 | break; 826 | } 827 | 828 | opts->total_iterations = d; 829 | 830 | } else { 831 | 832 | ok = 0; 833 | break; 834 | 835 | } 836 | 837 | ++required_count; 838 | 839 | } else if (!strcmp(arg, "-s")) { 840 | 841 | opts->method = LSYS_METHOD_STRING; 842 | 843 | } else if (!strcmp(arg, "-r")) { 844 | 845 | opts->method = LSYS_METHOD_RECURSION; 846 | 847 | } else if (!strcmp(arg, "-x")) { 848 | 849 | if (++i == argc) { ok = 0; break; } 850 | 851 | int d; 852 | 853 | if (sscanf(argv[i], "%d", &d) != 1) { 854 | ok = 0; break; 855 | } 856 | 857 | if (d >= -1) { 858 | opts->max_segments = (size_t)d; 859 | } else { 860 | ok = 0; 861 | break; 862 | } 863 | 864 | } else if (!strcmp(arg, "-M")) { 865 | 866 | disable_memoization = 1; 867 | 868 | #ifdef _OPENMP 869 | } else if (!strcmp(arg, "-P")) { 870 | 871 | disable_parallelization = 1; 872 | #endif 873 | } else if (!strcmp(arg, "-R")) { 874 | 875 | disable_precomputed_rotation = 1; 876 | 877 | } else { 878 | 879 | fprintf(stderr, "error: unrecognized option %s\n\n", arg); 880 | 881 | ok = 0; 882 | break; 883 | 884 | } 885 | 886 | } 887 | 888 | if (!ok || !opts->lsys || !opts->total_iterations) { 889 | printf("usage: %s [options] LSYSTEM ITERATIONS\n" 890 | "\n" 891 | "where LSYSTEM is one of:\n", argv[0]); 892 | for (int j=0; jmethod == LSYS_METHOD_STRING ? "string" : "recursion"); 911 | 912 | if (opts->method == LSYS_METHOD_STRING) { 913 | if (disable_memoization) { 914 | printf("warning: disabling memoization has no effect for string method!\n"); 915 | } 916 | } 917 | 918 | int have_memo = (opts->method == LSYS_METHOD_RECURSION) && !disable_memoization; 919 | 920 | if (!have_memo) { 921 | if (disable_parallelization) { 922 | printf("warning: disabling parallelization has no effect when not memoizing\n"); 923 | } 924 | opts->min_memo_segments = (size_t)-1; 925 | opts->min_parallel_segments = (size_t)-1; 926 | if (opts->method == LSYS_METHOD_RECURSION) { 927 | printf("memoization is disabled\n"); 928 | } 929 | } else if (disable_parallelization) { 930 | opts->min_memo_segments = 10000; 931 | opts->min_parallel_segments = (size_t)-1; 932 | printf("memoizing runs with > %d segments\n", 933 | (int)opts->min_memo_segments); 934 | } else { 935 | opts->min_memo_segments = 100000; 936 | opts->min_parallel_segments = 5000; 937 | printf("memoizing runs with > %d segments and parallelizing when > %d segments\n", 938 | (int)opts->min_memo_segments, (int)opts->min_parallel_segments); 939 | } 940 | 941 | if (disable_precomputed_rotation) { 942 | printf("disabling precomputed rotation!\n"); 943 | opts->lsys->rotation_cycle_length = 0; 944 | } 945 | 946 | printf("\n"); 947 | 948 | lsys_print(opts->lsys); 949 | 950 | } 951 | 952 | ////////////////////////////////////////////////////////////////////// 953 | // main program 954 | 955 | int main(int argc, char** argv) { 956 | 957 | // initialize lsystems 958 | initialize_known_lsystems(); 959 | 960 | // parse command-line options 961 | options_t opts; 962 | parse_options(argc, argv, &opts); 963 | 964 | //////////////////////////////////////////////////////////// 965 | // now get the segments 966 | 967 | double start = get_time_as_double(); 968 | 969 | darray_t* segments; 970 | 971 | if (opts.method == LSYS_METHOD_STRING) { 972 | 973 | char* lstring = lsys_build_string(opts.lsys, 974 | opts.total_iterations); 975 | 976 | segments = lsys_segments_from_string(opts.lsys, lstring); 977 | 978 | free(lstring); 979 | 980 | } else { 981 | 982 | segments = lsys_segments_recursive(opts.lsys, opts.total_iterations, 983 | opts.min_memo_segments, 984 | opts.min_parallel_segments); 985 | 986 | } 987 | 988 | double elapsed = get_time_as_double() - start; 989 | printf("generated %d segments in %.6f s (%.3f ns/segment).\n", 990 | (int)segments->count, elapsed, 1e9 * elapsed/segments->count); 991 | 992 | //////////////////////////////////////////////////////////// 993 | // either output or not 994 | 995 | if (segments->count > opts.max_segments) { 996 | 997 | printf("...maximum of %d segments exceeded, skipping output!\n", 998 | (int)opts.max_segments); 999 | 1000 | } else { 1001 | 1002 | const lsys_segment_t* segment = (const lsys_segment_t*)segments->data; 1003 | const lsys_segment_t* end = segment + segments->count; 1004 | 1005 | FILE* outfile = fopen("segments.txt", "w"); 1006 | 1007 | for ( ; segment != end; ++segment) { 1008 | fprintf(outfile, "%g %g %g %g\n", 1009 | segment->p0.x, segment->p0.y, 1010 | segment->p1.x, segment->p1.y); 1011 | } 1012 | 1013 | fclose(outfile); 1014 | 1015 | printf("wrote segments.txt\n"); 1016 | 1017 | } 1018 | 1019 | //////////////////////////////////////////////////////////// 1020 | // clean up 1021 | 1022 | darray_destroy(segments); 1023 | free(segments); 1024 | 1025 | return 0; 1026 | 1027 | } 1028 | --------------------------------------------------------------------------------