diff --git a/docs/usage.md b/docs/usage.md index 6e24e97..9225e8e 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -233,3 +233,13 @@ Vitamins are only ever previewed, so they are optimised to draw quickly in F5 an In OpenCSG 3D difference and intersection are relatively slow and the negative volumes interfere with nearby objects when they are composed into assemblies. For this reason as much as possible is done by unioning primitives and extruded 2D shapes. Any 3D differences or intersections are wrapped in ```render()``` so that CGAL will compute a polyhedron that is cached and reused. This will be very slow the first time it renders but very fast afterwards. + +### Multiple configurations + +Some parametric designs might have several configurations, for example a 3D printer with different size options. If several configurations need to be supported at the + same time multiple sets of BOMS, STLS and DXFs need to be generated in separate diectories. NopSCADlib supports this by having multiple configuration files named +```config_.scad```. All the scripts take an optional first parameter that selects one of these config files by specifying ```target_name```. + +The target config file is selected by generating ```target.scad``` that includes ```config_.scad```. +The rest of the project includes ```target.scad``` to use the configuration. +Additionally all the generated file directories (assemblies, bom, stls, dxfs, etc. are placed in a sub-directory called ``````. diff --git a/scripts/bom.py b/scripts/bom.py index b0169d0..6e10e66 100755 --- a/scripts/bom.py +++ b/scripts/bom.py @@ -189,8 +189,12 @@ def parse_bom(file = "openscad.log", name = None): print(line[:-1]) return main +def usage(): + print("\nusage:\n\tbom [target_config] [_assembly] - Generate BOMs for a project or an accessory to a project.") + sys.exit(1) + def boms(target = None, assembly = None): - bom_dir = set_config(target) + "bom" + bom_dir = set_config(target, usage) + "bom" if assembly: bom_dir += "/accessories" if not os.path.isdir(bom_dir): @@ -239,11 +243,24 @@ def boms(target = None, assembly = None): print("done") if __name__ == '__main__': - args = len(sys.argv) - if args > 1: - if args > 2: - boms(sys.argv[1], sys.argv[2]) - else: - boms(sys.argv[1]) + if len(sys.argv) > 3: usage() + + if len(sys.argv) == 3: + target, assembly = sys.argv[1], sys.argv[2] else: - boms(); + if len(sys.argv) == 2: + if sys.argv[1][-9:] == "_assembly": + target, assembly = None, sys.argv[1] + else: + target, assembly = sys.argv[1], None + else: + target, assembly = None, None + + if assembly: + if assembly[-9:] != "_assembly": usage() + + try: + boms(target, assembly) + except Exception as e: + print(str(e)) + sys.exit(1) diff --git a/scripts/c14n_stl.py b/scripts/c14n_stl.py index 73f0611..1c46e19 100644 --- a/scripts/c14n_stl.py +++ b/scripts/c14n_stl.py @@ -113,5 +113,5 @@ if __name__ == '__main__': if len(sys.argv) == 2: canonicalise(sys.argv[1]) else: - print("usage: c14n_stl file") + print("\nusage:\n\t c14n_stl file - Canonicalise an STL file created by OpenSCAD.") sys.exit(1) diff --git a/scripts/doc_scripts.py b/scripts/doc_scripts.py index d272728..e0aca8c 100755 --- a/scripts/doc_scripts.py +++ b/scripts/doc_scripts.py @@ -26,6 +26,7 @@ from __future__ import print_function import os from tests import do_cmd +import argparse dir = 'scripts' @@ -74,4 +75,5 @@ They should work with both Python 2 and Python 3. if __name__ == '__main__': + argparse.ArgumentParser().parse_args() doc_scripts() diff --git a/scripts/exports.py b/scripts/exports.py index c1c24ca..dab9219 100644 --- a/scripts/exports.py +++ b/scripts/exports.py @@ -46,11 +46,21 @@ def bom_to_parts(target_dir, part_type, assembly = None): part_files.append(last_word[:-4] + '.' + part_type) return part_files +def usage(t): + print("\nusage:\n\t%ss [target_config] [.%s] ... [.%s] - Generate specified %s files or all if none specified." % ( t, t, t, t.upper())) + sys.exit(1) + def make_parts(target, part_type, parts = None): # + # Check list of parts is the correct type + # + if parts: + for p in parts: + if not p.endswith('.' + part_type): usage(part_type) + # # Make the target directory # - top_dir = set_config(target) + top_dir = set_config(target, lambda: usage(part_type)) target_dir = top_dir + part_type + 's' deps_dir = top_dir + "deps" if not os.path.isdir(target_dir): @@ -142,5 +152,5 @@ def make_parts(target, part_type, parts = None): print(part, "is not a", part_type, "file") else: print("Could not find a module called", part[:-4] + module_suffix, "to make", part) - sys.exit(1) + usage(part_type) times.print_times() diff --git a/scripts/gallery.py b/scripts/gallery.py index 046f9bd..c71406b 100755 --- a/scripts/gallery.py +++ b/scripts/gallery.py @@ -30,6 +30,7 @@ import re from shutil import copyfile from tests import update_image import sys +import argparse project_dirs = ['../..', 'examples'] target_dir = 'gallery' @@ -39,7 +40,6 @@ def gallery(force): if not os.path.isdir(target_dir): os.makedirs(target_dir) - paths = sorted([pdir + '/' + i for pdir in project_dirs for i in os.listdir(pdir) if os.path.isdir(pdir + '/' + i + '/assemblies')], key = lambda s: os.path.basename(s)) with open(output_name, 'wt') as output_file: print("# A gallery of projects made with NopSCADlib", file = output_file) @@ -78,4 +78,8 @@ def gallery(force): if __name__ == '__main__': init() - gallery(force = len(sys.argv) > 1 and sys.argv[1] == '-f') + parser = argparse.ArgumentParser() + parser.add_argument("-f", help = "run make_all in each project to force update", action="store_true") + args = parser.parse_args() + + gallery(force = args.f) diff --git a/scripts/make_all.py b/scripts/make_all.py index c77659a..5d3d333 100755 --- a/scripts/make_all.py +++ b/scripts/make_all.py @@ -27,9 +27,17 @@ from bom import boms from render import render from views import views from plateup import plateup +from set_config import set_config + + +def usage(): + print("\nusage:\n\tmake_all [target_config] - Make all the manufacturing files and readme for a project.") + sys.exit(1) if __name__ == '__main__': + if len(sys.argv) > 2: usage() target = None if len(sys.argv) == 1 else sys.argv[1] + set_config(target, usage) boms(target) for part in ['stl', 'dxf']: make_parts(target, part) diff --git a/scripts/panels.py b/scripts/panels.py index 1ef83e6..e388687 100644 --- a/scripts/panels.py +++ b/scripts/panels.py @@ -25,9 +25,15 @@ import sys from plateup import plateup +def usage(): + print("\nusage:\n\tpanels [target_config] - Aggregate DXF files for routing together.") + sys.exit(1) + if __name__ == '__main__': + if len(sys.argv) > 2: usage() + if len(sys.argv) > 1: target = sys.argv[1] else: target = None - plateup(target, 'dxf') + plateup(target, 'dxf', usage) diff --git a/scripts/plateup.py b/scripts/plateup.py index b07bc71..07699e7 100644 --- a/scripts/plateup.py +++ b/scripts/plateup.py @@ -31,11 +31,11 @@ from shutil import copyfile source_dirs = { "stl" : "platters", "dxf" : "panels" } target_dirs = { "stl" : "printed", "dxf" : "routed" } -def plateup(target, part_type): +def plateup(target, part_type, usage = None): # # Make the target directory # - top_dir = set_config(target) + top_dir = set_config(target, usage) parts_dir = top_dir + part_type + 's' target_dir = parts_dir + '/' + target_dirs[part_type] source_dir = top_dir + source_dirs[part_type] diff --git a/scripts/platters.py b/scripts/platters.py index 4a59948..fba139e 100644 --- a/scripts/platters.py +++ b/scripts/platters.py @@ -25,9 +25,15 @@ import sys from plateup import plateup +def usage(): + print("\nusage:\n\tplatters [target_config] - Aggregate STL files for printing together.") + sys.exit(1) + if __name__ == '__main__': + if len(sys.argv) > 2: usage() + if len(sys.argv) > 1: target = sys.argv[1] else: target = None - plateup(target, 'stl') + plateup(target, 'stl', usage) diff --git a/scripts/render.py b/scripts/render.py index 1461177..d160eb3 100755 --- a/scripts/render.py +++ b/scripts/render.py @@ -30,11 +30,15 @@ from tests import do_cmd, update_image, colour_scheme, background from deps import mtime from colorama import init +def usage(): + print("\nusage:\n\trender [target_config] - Render images of the stl and dxf files."); + sys.exit(1) + def render(target, type): # # Make the target directory # - target_dir = set_config(target) + type + 's' + target_dir = set_config(target, usage) + type + 's' if not os.path.isdir(target_dir): os.makedirs(target_dir) # @@ -71,6 +75,7 @@ def render(target, type): if __name__ == '__main__': init() + if len(sys.argv) > 2: usage() target = sys.argv[1] if len(sys.argv) > 1 else None render(target, 'stl') render(target, 'dxf') diff --git a/scripts/set_config.py b/scripts/set_config.py index 82ac7ca..8c4fe08 100755 --- a/scripts/set_config.py +++ b/scripts/set_config.py @@ -45,20 +45,27 @@ def valid_targets_string(): return result -def set_config(target): +def set_config(target, usage = None): + if target and target[:1] == '-' and usage: usage() targets = valid_targets() if not target: if not targets: return "" print("Must specify a configuration: " + valid_targets_string()) + if usage: + usage() sys.exit(1) if not targets: print("Not a muli-configuration project (no config_.scad files found)") + if usage: + usage() sys.exit(1) if not target in targets: print(target + " is not a configuration, avaliable configurations are: " + valid_targets_string()) + if usage: + usage() sys.exit(1) fname = source_dir + "/target.scad" @@ -75,10 +82,13 @@ def set_config(target): f. write(text); return target + "/" +def usage(): + print("\nusage:\n\tset_config config_name") + sys.exit(1) + if __name__ == '__main__': args = len(sys.argv) if args == 2: - set_config(sys.argv[1]) + set_config(sys.argv[1], usage) else: - print("usage: set_config config_name") - sys.exit(1) + usage() diff --git a/scripts/tests.py b/scripts/tests.py index 3d3dea5..14676d0 100755 --- a/scripts/tests.py +++ b/scripts/tests.py @@ -85,6 +85,10 @@ def depluralise(name): def is_plural(name): return name != depluralise(name) +def usage(): + print("\nusage:\n\ttests [test_name1] ... [test_nameN] - Run specified tests or all tests in none specified."); + sys.exit(1) + def tests(tests): scad_dir = "tests" deps_dir = scad_dir + "/deps" @@ -96,6 +100,7 @@ def tests(tests): doc_name = "readme.md" index = {} bodies = {} + done = [] times.read_times() options.check_options(deps_dir) # @@ -114,6 +119,7 @@ def tests(tests): for scad in scads: base_name = scad[:-5] if not tests or base_name in tests: + done.append(base_name) print(base_name) cap_name = base_name[0].capitalize() + base_name[1:] base_name = base_name.lower() @@ -234,6 +240,14 @@ def tests(tests): body += ['\nTop'] body += ["\n---"] + for test in done: + if test in tests: + tests.remove(test) + if tests: + for test in tests: + print(Fore.MAGENTA + "Could not find a test called", test, Fore.WHITE) + usage() + with open(doc_name, "wt") as doc_file: print('# NopSCADlib', file = doc_file) print('''\ @@ -279,4 +293,6 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa do_cmd('codespell -L od readme.md'.split()) if __name__ == '__main__': + for arg in sys.argv[1:]: + if arg[:1] == '-': usage() tests(sys.argv[1:]) diff --git a/scripts/views.py b/scripts/views.py index bf82868..b376933 100755 --- a/scripts/views.py +++ b/scripts/views.py @@ -97,12 +97,16 @@ def titalise(name): cap_next = c == ' ' return result +def usage(): + print("\nusage:\n\t views [target_config] [_assembly] ... [_assembly] - Create assembly images and readme.") + sys.exit(1) + def views(target, do_assemblies = None): done_assemblies = [] # # Make the target directory # - top_dir = set_config(target) + top_dir = set_config(target, usage) target_dir = top_dir + 'assemblies' deps_dir = top_dir + "deps" bom_dir = top_dir + "bom" @@ -396,4 +400,7 @@ if __name__ == '__main__': else: target, assemblies = None, sys.argv[1:] + for a in assemblies: + if a[-9:] != "_assembly": usage() + views(target, assemblies)