def parse_args(): parser = argparse.ArgumentParser(description='Arguments for an stl astroid.') marble_path.add_tube_arguments(parser, default_slope_angle=7.0, default_output_name='astroid.stl') # outer_radius used for measurements because most articles on the # subject start from the outer radius. for example, # https://en.wikipedia.org/wiki/Astroid parser.add_argument('--outer_radius', default=52, type=float, help='Measurement from the center to the tip of the astroid. inner_radius will be 1/2 this (for a power 3 astroid)') parser.add_argument('--closest_approach', default=None, type=float, help='Measurement from 0,0 to the closest point of the tube center. Will override outer_radius. 26 for a 31mm connector connecting exactly to the tube') parser.add_argument('--subdivisions_per_side', default=50, type=int, help='Subdivisions for each of the 4 sides of the astroid. Note that there will also be rounded corners adding more subdivisions') parser.add_argument('--astroid_power', default=3, type=int, help='Exponent on the various astroid equations. 3 is disappointingly non-curved') parser.add_argument('--cusp_method', default=Cusp.OFFSET, type=lambda x: Cusp[x.upper()], help='How to handle the corners. OFFSET = offset by tube width, CHOP = chop when the cusp is too close to the axis') args = parser.parse_args() if args.closest_approach is not None: args.outer_radius = closest_approach_to_radius(args.cusp_method, args.tube_radius, args.closest_approach, args.astroid_power) if args.outer_radius / 2 < args.tube_radius and args.cusp_method is not Cusp.OFFSET: raise ValueError("Impossible to make an astroid where the tube radius {} is greater than the inner radius {}".format(args.tube_radius, args.outer_radius / 2)) return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser( description='Arguments for a two post loop') marble_path.add_tube_arguments(parser, default_slope_angle=7, default_output_name='loops.stl') parser.add_argument('--num_time_steps', default=280, type=int, help='Number of time steps in the whole ramp') parser.add_argument('--num_loops', default=3, type=int, help='How many times to loop') parser.add_argument('--post_distance', default=134, type=float, help='Distance from one post to another') parser.add_argument('--post_radius', default=15.5, type=float, help='Radius of a post') parser.add_argument('--loop_length', default=None, type=float, help='The ellipse axis between the posts') parser.add_argument('--loop_width', default=70, type=float, help='The ellipse axis perpendicular to the posts') parser.set_defaults(tube_start_angle=-45) args = parser.parse_args(args=sys_args) # To get the loops to just contact the wall of the tube, we calculate: # the center will be at 67 # the tube radius is 12.5 # the post radius is 15.5 # wall thickness is 2 # 67 - 12.5 - 15.5 + 2 = 41 # so we want the loops to be 82 long if args.loop_length is None: args.loop_length = (args.post_distance / 2 - args.tube_radius - args.post_radius + args.wall_thickness) * 2 print("Using a loop length of {}".format(args.loop_length)) return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser(description='Arguments for a spiral - chapter 7 of Curve Design') marble_path.add_tube_arguments(parser, default_slope_angle=2.5, default_output_name='snail.stl') combine_functions.add_post_args(parser) slope_function.add_overlap_args(parser) parser.add_argument('--num_time_steps', default=360, type=int, help='Number of time steps in the whole ramp') parser.add_argument('--post_distance', default=134, type=float, help='Distance from one post to another') args = parser.parse_args(args=sys_args) combine_functions.process_post_args(args) return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser(description='Arguments for an stl limacon. Graph of r = a - b cos(theta)') marble_path.add_tube_arguments(parser, default_slope_angle=12.0, default_output_name='limacon.stl') parser.add_argument('--constant_factor', default=1, type=float, help='The a in the "a - b cos(theta)"') parser.add_argument('--cosine_factor', default=2, type=float, help='The b in the "a - b cos(theta)"') parser.add_argument('--time_steps', default=200, type=int, help='How refined to make the limacon') parser.add_argument('--length', default=134, type=float, help='Distance from one end to the other, ignoring the tube') parser.add_argument('--width', default=None, type=float, help='Distance from left to right, ignoring the tube. If None, the curve will be scaled to match the length') parser.add_argument('--domain_size', default=1.806, type=float, help='Theta goes from -domain_size to domain_size') parser.add_argument('--auto_domain', dest='auto_domain', default=True, action='store_true', help='Dynamically calculate the domain by trying to balance the torque') parser.add_argument('--no_auto_domain', dest='auto_domain', action='store_false', help="Don't dynamically calculate the domain by trying to balance the torque") parser.set_defaults(tube_start_angle=-60) args = parser.parse_args(args=sys_args) if args.cosine_factor == 1.0: raise ValueError("Sorry, but b=1.0 causes a discontinuity in the derivative") if args.cosine_factor == 0.0: raise ValueError("Consider using generate_helix if you just want a circle") if args.cosine_factor < 0.0: raise ValueError("You can simply mirror the resulting curve and set cosine_factor > 0.0") if args.cosine_factor < 1.0: raise ValueError("0.0<b<1.0 not implemented yet") if args.auto_domain: args.domain_size = balance_domain(args.constant_factor, args.cosine_factor) return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser( description='Arguments for an stl zigzag.') marble_path.add_tube_arguments(parser, default_slope_angle=5.0, default_output_name='zigzag.stl') parser.add_argument( '--zigzag_length', default=-5, type=float, help= 'How far a zigzag goes in the y direction. Negative means go down first' ) parser.add_argument( '--zigzag_width', default=30, type=float, help='How far a zigzag goes in the x direction (for both up and down)') parser.add_argument('--num_zigzags', default=5, type=int, help='How many zigzags to make') parser.add_argument( '--subdivisions_per_zigzag', default=40, type=int, help='Subdivisions for each of the zigzags. Half up and half down') # Generally we find that 12.5 works great for the tube - walls of # thickness 2 led to the narbles rolling great without taking up # too much space. The default slant of the zigzag means a # slightly higher default width is better parser.set_defaults(tube_radius=13.25) parser.set_defaults(tube_method=marble_path.Tube.OVAL) parser.set_defaults(tube_wall_height=6) args = parser.parse_args(args=sys_args) return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser(description='Arguments for an stl tube.') marble_path.add_tube_arguments(parser, default_slope_angle=2.9, default_output_name='tube.stl') parser.add_argument('--length', default=50, type=float, help='Length of the tube') parser.add_argument('--num_time_steps', default=100, type=int, help='How refined to make the tube') parser.add_argument( '--rotation', default=0, type=float, help='Rotation on the tube, in degrees. 0 is going north') args = parser.parse_args(args=sys_args) return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser( description='Arguments for an stl cycloid.') marble_path.add_tube_arguments(parser, default_slope_angle=8.0, default_output_name='cycloid.stl') slope_function.add_kink_args(parser) slope_function.add_overlap_args(parser) combine_functions.add_kink_circle_args(parser) extend_function.add_extend_args(parser, default_extra_t=0.1) # Start & end times for the curve parser.add_argument('--domain', default=None, type=float, help='If set, the domain will be -domain..domain') parser.add_argument('--min_domain', default=None, type=float, help='t0 in the domain [t0, t1]') parser.add_argument( '--max_domain', default=None, type=float, help= 't1 in the domain [t0, t1]. If left None, will be derived from the coefficients' ) # Parameters for the equation of this curve parser.add_argument('--x_coeff', default=1, type=float, help='Coefficient A of x=t+A sin(Bt)') parser.add_argument('--x_t_coeff', default=4, type=int, help='Coefficient B of x=t+A sin(Bt)') parser.add_argument('--y0', default=1, type=float, help='Coefficient y0 of y(t) = y0 + C cos(Dt + phase)') parser.add_argument('--y_coeff', default=-1, type=float, help='Coefficient C of y(t) = y0 + C cos(Dt + phase)') parser.add_argument('--y_t_coeff', default=4, type=int, help='Coefficient D of y(t) = y0 + C cos(Dt + phase)') parser.add_argument('--use_sign', dest='use_sign', default=True, action='store_true', help='multiply y by sign(t)') parser.add_argument('--no_use_sign', dest='use_sign', action='store_false', help="Don't multiply y by sign(t)") parser.add_argument('--num_time_steps', default=400, type=int, help='Number of time steps to model') parser.add_argument('--scale', default=None, type=float, help='Scale by which to multiple f_t') # TODO: just make separate x_scale & y_scale arguments parser.add_argument( '--y_scale', default=0.8, type=float, help='Make the model a little squished or stretched vertically') parser.add_argument( '--y_phase', default=0.0, type=float, help='Adjust the phase of y(t) = y0 + C cos(Dt + phase)') parser.add_argument( '--reg_x', default=0.0, type=float, help= 'How much to use regularization on x around t=0. Idea is to make squiggle with loops not have sharp corners' ) parser.add_argument( '--reg_y', default=0.0, type=float, help= 'How much to use regularization on y around t=0. Idea is to make squiggle with loops not have sharp corners' ) parser.add_argument( '--reg_power', default=4.0, type=float, help= 'How tightly to apply the reg around t=0. Higher t means less wide effect' ) args = parser.parse_args(args=sys_args) if args.domain is not None: args.min_domain = -args.domain args.max_domain = args.domain if args.min_domain is None or args.max_domain is None: width = math.pi * 4 / math.gcd(args.x_t_coeff, args.y_t_coeff) if args.min_domain is None and args.max_domain is None: args.min_domain = -width / 2 args.max_domain = width / 2 elif args.min_domain is None: args.min_domain = args.max_domain - width else: args.max_domain = args.min_domain + width return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser( description='Arguments for a random trig graph p173 of Curves.') marble_path.add_tube_arguments(parser, default_slope_angle=5.0, default_output_name='trig.stl') slope_function.add_kink_args(parser) combine_functions.add_kink_circle_args(parser) parser.add_argument('--num_time_steps', default=400, type=int, help='Number of time steps to model') parser.add_argument('--power', default=3, type=int, help='Power to put on (x=t+sin^k t, y=B sin t)') parser.add_argument('--y_coeff', default=2, type=float, help='B in the expression (x=t+sin^k t, y=B sin t)') parser.add_argument('--start_t', default=0.0, type=float, help='Curve goes from start_t to end_t') parser.add_argument('--end_t', default=4.0 * math.pi, type=float, help='Curve goes from start_t to end_t') parser.add_argument( '--width', default=134.0, type=float, help= 'How far apart to make the endpoints of the curve. Note that the curve itself may extend past the endpoints. Further note that using the circular kink removal will invalidate this argument.' ) parser.add_argument( '--scale', default=None, type=float, help= 'Multiple all samples by this value. If set to None, will be calculated from the width.' ) args = parser.parse_args(args=sys_args) if args.scale is None: if args.kink_replace_circle: # TODO: this can be compensated for, actually print( "WARNING: trying to calculate an exact width may be invalidated by using kink replacement. Consider setting --scale" ) max_t = args.end_t min_t = args.start_t args.scale = args.width / (max_t - min_t) print("Calculated scale to be ", args.scale) return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser( description='Arguments for a lissajous curve or its relatives') marble_path.add_tube_arguments(parser, default_slope_angle=10.0, default_output_name='lissajous.stl') slope_function.add_overlap_args(parser) combine_functions.add_kink_circle_args(parser) extend_function.add_extend_args(parser) regularization.add_regularization_args(parser) parser.add_argument('--lissA', default=5, type=int, help='value A in the lissajous formula') parser.add_argument('--lissB', default=0, type=float, help='value B in the lissajous formula') parser.add_argument('--lissC', default=3, type=int, help='value C in the lissajous formula') parser.add_argument( '--lissD', default=0, type=float, help= 'value D in the y(t) expression if an alternate formulation is used') parser.add_argument( '--lissN', default=0, type=float, help= 'value N in the y(t) expression if an alternate formulation is used') parser.add_argument('--start_t', default=-0.74, type=float, help='Time to start the equation') parser.add_argument('--end_t', default=0.74, type=float, help='Time to end the equation') parser.add_argument('--num_time_steps', default=250, type=int, help='Number of time steps in the whole curve') # TODO: refactor the scale arguments? parser.add_argument('--x_scale', default=40, type=float, help='Scale the shape by this much in the x direction') parser.add_argument('--y_scale', default=50, type=float, help='Scale the shape by this much in the y direction') parser.add_argument('--scale', default=None, type=float, help='Scale both directions by this much') parser.add_argument('--lissajous', default=Lissajous.BASIC, type=lambda x: Lissajous[x.upper()], help='What formula to use. Options are ' + " ".join(i.name for i in Lissajous)) # TODO: refactor the regularization argument? args = parser.parse_args(args=sys_args) if args.scale is not None: args.x_scale = args.scale args.y_scale = args.scale return args
def parse_args(sys_args=None): parser = argparse.ArgumentParser( description='Arguments for an stl clover.') marble_path.add_tube_arguments(parser, default_slope_angle=6.0, default_output_name='clover.stl') combine_functions.add_zero_circle_args(parser) parser.add_argument('--flower_power', default=4, type=float, help='Coefficient A of (cos^A theta + sin^A theta)^B') parser.add_argument('--pinch_power', default=1.5, type=float, help='Coefficient B of (cos^A theta + sin^A theta)^B') parser.add_argument( '--closest_approach', default=26.0, type=float, help= 'Measurement from 0,0 to the closest point of the tube center. 26 for a 31mm connector connecting exactly to the tube' ) parser.add_argument( '--twist_numerator', default=1, type=int, help='Twist the flower - instead of cos(theta), use cos(m/n theta)') parser.add_argument( '--twist_denominator', default=1, type=int, help='Twist the flower - instead of cos(theta), use cos(m/n theta)') parser.add_argument( '--twist_wiggle', default=0, type=float, help= 'Wiggle the twist - instead of cos(m/n theta), use cos(m/n (theta + w sin (8 theta)))' ) parser.add_argument( '--scale', default=None, type=float, help= 'Amount to scale in the x/y directions. Will override closest_approach if set.' ) parser.add_argument('--start_t', default=math.pi / 3, type=float, help='Time to start the equation') parser.add_argument( '--end_t', default=(2 + 1 / 6) * math.pi, type=float, help='Time to end the equation. If None, will be 2pi * b / gcd(a, b)') parser.add_argument('--num_time_steps', default=400, type=int, help='Number of time steps in the whole curve') args = parser.parse_args(args=sys_args) if args.scale is None: args.scale = tune_closest_approach(args) return args
def parse_args(sys_args=None): # TODO: add an argument which does the math for rotations if you give it the angle of helix you want, for example parser = argparse.ArgumentParser(description='Arguments for an stl helix.') marble_path.add_tube_arguments(parser, default_slope_angle=2.0, default_output_name='helix.stl') parser.add_argument( '--helix_radius', default=19, type=float, help='measurement from the axis to the center of any part of the ramp') parser.add_argument( '--helix_sides', default=64, type=int, help='how many sides it takes to go around the axis once') parser.add_argument( '--vertical_displacement', default=None, type=float, help= 'how far to move up in one complete rotation. tube_radius*2 means the next layer will be barely touching the previous layer' ) parser.add_argument( '--rotations', default=1, type=float, help= 'rotations is how far around to go. will be discretized using helix_sides' ) parser.add_argument( '--initial_rotation', default=0, type=float, help='How much to offset the rotation of the helix curve') parser.add_argument( '--clockwise', dest='clockwise', default=False, action='store_true', help= 'Rotate clockwise. Note that rotating clockwise will make the initial rotation start from the left side of the helix' ) parser.add_argument('--counterclockwise', dest='clockwise', action='store_false', help="Rotate counterclockwise") args = parser.parse_args(sys_args) if args.vertical_displacement is not None: # slope_angle is the angle of the slope as it goes up the spiral # faces will be tilted this much to allow better connections with # the next piece slope_angle = calculate_slope_angle(args.helix_radius, args.vertical_displacement) print("Derived slope angle:", slope_angle) args.slope_angle = slope_angle return args