diff options
-rw-r--r-- | Makefile | 101 | ||||
-rwxr-xr-x | generate_sources.sh | 23 | ||||
-rw-r--r-- | src/_lib.scad | 83 | ||||
-rw-r--r-- | src/_settings.scad | 12 | ||||
-rw-r--r-- | src/jig.scad | 4 | ||||
-rw-r--r-- | src/pcb_shape.scad | 8 | ||||
-rwxr-xr-x | support/inkscape_svg_filter_layers.py | 51 |
7 files changed, 168 insertions, 114 deletions
@@ -6,20 +6,10 @@ ASYMPTOTE := asy # Settings affecting the compiled results. You can overwrite these in a file called settings.mk in the same directory as this makefile. See readme.creole. DXF_FLATNESS := 0.1 -FLAT_SCAD_FILES := # Non-file goals. .PHONY: all clean generated dxf stl asy pdf -# Remove targets whose command failed. -.DELETE_ON_ERROR: - -# Do not print commands as they are executed. They do not show the actual invocation of the respective tools anyways but just the Pyton wrappers. -.SILENT: - -# Set the default goal. Prevents it from being overwritten accidentially from config.mk or settings.mk. -.DEFAULT_GOAL := all - # Include the configuration files. -include config.mk settings.mk @@ -27,95 +17,24 @@ FLAT_SCAD_FILES := PYTHON_CMD := PYTHONPATH="support" $(PYTHON) INKSCAPE_CMD := INKSCAPE=$(INKSCAPE) DXF_FLATNESS=$(DXF_FLATNESS) $(PYTHON_CMD) -m inkscape OPENSCAD_CMD := OPENSCAD=$(OPENSCAD) $(PYTHON_CMD) -m openscad -ASYMPTOTE_CMD := ASYMPTOTE=$(ASYMPTOTE) $(PYTHON_CMD) -m asymptote - -# Function with arguments (ext, subst_ext, names). -# Takes a list of file names and returns all elements whose basename do not start with a `_' and which have extension ext. The returned names will have their extension replaced by subst_ext. -filter_compiled = $(foreach i,$(patsubst %$1,%$2,$(filter %$1,$3)),$(if $(filter-out _%,$(notdir $i)),$i)) - -# All considered source and target files currently existing. -EXISTING_FILES := $(shell find src -not \( \( -name '.*' -or -name '* *' \) -prune \) -type f) - -# Run generate_scad.sh to get the names of all files that should be generated using that same script. -GENERATED_FILES := $(shell ./generate_sources.sh) - -# All visible files in the src directory that either exist or can be generated. Ignore files whose names contain spaces. -SRC_FILES := $(sort $(GENERATED_FILES) $(EXISTING_FILES)) - -# STL files produced from OpenSCAD files. -SCAD_STL_FILES := $(call filter_compiled,.scad,.stl,$(filter-out $(FLAT_SCAD_FILES),$(SRC_FILES))) - -# DXF files produced from OpenSCAD fiels. Ignore non-OpenSCAD files in FLAT_SCAD_FILES. -SCAD_DXF_FILES := $(call filter_compiled,.scad,.dxf,$(filter $(FLAT_SCAD_FILES),$(SRC_FILES))) - -# DXF files produced from SVG files. This excludes SVG files that are exported to Asymptote Files. Also, ignores an SVG file, if the same DXF file can also be produced from an OpenSCAD file. This is just to get reproducable builds without aborting it. -SVG_DXF_FILES := $(filter-out $(SCAD_DXF_FILES),$(call filter_compiled,.svg,.dxf,$(SRC_FILES))) - -# Asymptote files produced from SVG files. -SVG_ASY_FILES := $(call filter_compiled,.svg,.asy,$(SRC_FILES)) -# PDF files which can be generated from Asymptote files. We exclude SVG_ASY_FILES because they don't contain any drawing primitives and thus won't produce a PDF. -ASY_PDF_FILES := $(call filter_compiled,.asy,.pdf,$(filter-out $(SVG_ASY_FILES),$(SRC_FILES))) +all: src/jig.stl src/pcb_shape.dxf -# Makefiles which are generated while compiling to record dependencies. -DEPENDENCY_FILES := $(patsubst %,%.d,$(SCAD_STL_FILES) $(SCAD_DXF_FILES) $(ASY_PDF_FILES)) - -# Files that may be used from OpenSCAD files and thus must exist before OpenSCAD is called. -SCAD_ORDER_DEPS := $(filter %.scad %.dxf,$(SRC_FILES)) $(SVG_DXF_FILES) - -# Files that may be used from Asymptote files. -ASY_DEPS := $(filter %.asy,$(SRC_FILES)) $(SVG_ASY_FILES) - -# Dependencies which may affect the result of all build products. -GLOBAL_DEPS := Makefile $(wildcard config.mk settings.mk) - -# All existing target files. -EXISTING_TARGETS := $(filter $(SVG_DXF_FILES) $(SCAD_DXF_FILES) $(SCAD_STL_FILES) $(SVG_ASY_FILES) $(ASY_PDF_FILES) $(GENERATED_FILES) $(DEPENDENCY_FILES),$(EXISTING_FILES)) - -# Goal to build Everything. Also generates files which aren't compiled to anything else. Deined here to make it the default goal. -all: generated $(SCAD_DXF_FILES) $(SCAD_STL_FILES) $(ASY_PDF_FILES) - -# Everything^-1. clean: - echo [clean] $(EXISTING_TARGETS) - rm -rf $(EXISTING_TARGETS) - -# Goals to build the project up to a specific step. -generated: $(GENERATED_FILES) -dxf: $(SVG_DXF_FILES) $(SCAD_DXF_FILES) -stl: $(SCAD_STL_FILES) -asy: $(SVG_ASY_FILES) -pdf: $(ASY_PDF_FILES) + rm -f src/input.preprocessed.dxf + rm -f src/input.preprocessed.svg + rm -f src/jig.stl + rm -f src/pcb_shape.dxf -# Rule to convert an SVG file to a DXF file. -$(SVG_DXF_FILES): %.dxf: %.svg $(GLOBAL_DEPS) - echo [inkscape] $@ +src/input.preprocessed.dxf: src/input.preprocessed.svg $(INKSCAPE_CMD) $< $@ -# Rule to export an SVG file to an Asymptote file. -$(SVG_ASY_FILES): %.asy: %.svg $(GLOBAL_DEPS) - echo [inkscape] $@ - $(INKSCAPE_CMD) $< $@ - -# Rule to compile an OpenSCAD file to a DXF file. -$(SCAD_DXF_FILES): %.dxf: %.scad $(GLOBAL_DEPS) | $(SCAD_ORDER_DEPS) - echo [openscad] $@ +src/pcb_shape.dxf: src/pcb_shape.scad src/input.preprocessed.dxf $(OPENSCAD_CMD) $< $@ -# Rule to compile an OpenSCAD file to an STL file. -$(SCAD_STL_FILES): %.stl: %.scad $(GLOBAL_DEPS) | $(SCAD_ORDER_DEPS) - echo [openscad] $@ +src/jig.stl: src/jig.scad src/input.preprocessed.dxf $(OPENSCAD_CMD) $< $@ -# Rule to compile an Asymptote file to a PDF file. -$(ASY_PDF_FILES): %.pdf: %.asy $(GLOBAL_DEPS) | $(ASY_DEPS) - echo [asymptote] $@ - $(ASYMPTOTE_CMD) $< $@ - -# Rule for automaticaly generated OpenSCAD files. -$(GENERATED_FILES): generate_sources.sh $(GLOBAL_DEPS) - echo [generate] $@ - ./generate_sources.sh $@ +src/input.preprocessed.svg: input.svg + support/inkscape_svg_filter_layers.py $< $@ --only --name "Test Points" "Mounting Holes" "Grip Slots" "Outline" -# Include dependency files produced by an earlier build. --include $(DEPENDENCY_FILES) diff --git a/generate_sources.sh b/generate_sources.sh deleted file mode 100755 index 0a2af79..0000000 --- a/generate_sources.sh +++ /dev/null @@ -1,23 +0,0 @@ -#! /usr/bin/env bash - -set -e -o pipefail - -current_file_name=$1 - -# This function should be called for each generated file with the file's name as the first argument and the command to call to produce the file's content as the remaining arguments. -function generate_file() { - file_name=$1 - shift - generate_command=("$@") - - if ! [ "$current_file_name" ]; then - echo "$file_name" - elif [ "$current_file_name" == "$file_name" ]; then - mkdir -p "$(dirname "$file_name")" - "${generate_command[@]}" > "$file_name" - fi -} - -# Call generate_file for each file to be generated. -# E.g.: -# generate_file src/test.scad echo "cube();" diff --git a/src/_lib.scad b/src/_lib.scad new file mode 100644 index 0000000..2654ed7 --- /dev/null +++ b/src/_lib.scad @@ -0,0 +1,83 @@ + +module hole(l, step, w){ + translate([0, 0, -eps]) { + union(){ + linear_extrude(l+eps*2) + children(); + minkowski(){ + linear_extrude(eps) children(); + cylinder(step, w, 0); + } + } + marker_r = 4; + marker_w = 1; + linear_extrude(1) + difference() { + offset(marker_r+0.5*marker_w) children(); + offset(marker_r-0.5*marker_w) children(); + } + } +} + +module top_chamfer(height, chamfer){ + difference(){ + linear_extrude(height) children(); + translate([0,0,height+eps]) + union() { + for(w=[0:.2:chamfer]){ + mirror([0,0,1]) + linear_extrude(chamfer-w) + difference(){ + offset(1) children(); + offset(-w) children(); + } + } + } + } +} + +module base_shape(wall){ + offset(grip_rounding) offset(-grip_rounding)difference(){ + hull() offset(wall) + children(); + import(input_file, layer="Grip Slots"); + } +} + +module holder(height, depth, wall, tolerance, chamfer){ + difference() { + top_chamfer(height, chamfer/2) + //linear_extrude(height) + base_shape(wall) children(); + translate([0,0,height-depth]) + linear_extrude(depth+eps) offset(tolerance) + children(); + translate([0,0,height-chamfer+eps]) minkowski(){ + linear_extrude(eps) children(); + cylinder(chamfer, 0, chamfer); + } + } +} + +module mounting_hole(height, inset_depth, inset_extra){ + union(){ + translate([0,0,-eps]) + linear_extrude(height+2*eps) + children(); + translate([0,0,height-inset_depth]) + linear_extrude(inset_depth+eps) + offset(inset_extra) + children(); + } +} + +module jig(height, depth, wall, tolerance, chamfer) { + difference(){ + holder(height, depth, wall, tolerance, chamfer) + import(input_file, layer="Outline"); + hole(height-depth, 2, 1) + import(input_file, layer="Test Points"); + mounting_hole(height, 3, 2) + import(input_file, layer="Mounting Holes"); + } +} diff --git a/src/_settings.scad b/src/_settings.scad new file mode 100644 index 0000000..f51736a --- /dev/null +++ b/src/_settings.scad @@ -0,0 +1,12 @@ + +height=15; +depth=6; +wall=10; +pcb_extra=7; +tolerance=0.2; +chamfer=3; +grip_rounding = 1.5; + +input_file = "input.preprocessed.dxf"; +$fs=0.01; +eps=0.1; diff --git a/src/jig.scad b/src/jig.scad new file mode 100644 index 0000000..67580bf --- /dev/null +++ b/src/jig.scad @@ -0,0 +1,4 @@ +include <_settings.scad> +include <_lib.scad> + +jig(height, depth, wall, tolerance, chamfer); diff --git a/src/pcb_shape.scad b/src/pcb_shape.scad new file mode 100644 index 0000000..a3e1db9 --- /dev/null +++ b/src/pcb_shape.scad @@ -0,0 +1,8 @@ +include <_settings.scad> +include <_lib.scad> + +difference(){ + base_shape(wall+pcb_extra) + import(input_file, layer="Outline"); + import(input_file, layer="Mounting Holes"); +}
\ No newline at end of file diff --git a/support/inkscape_svg_filter_layers.py b/support/inkscape_svg_filter_layers.py new file mode 100755 index 0000000..9301e1e --- /dev/null +++ b/support/inkscape_svg_filter_layers.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import xml.etree.ElementTree as xe +import argparse +import re + + +if __name__ != '__main__': + raise SystemError('Not running as shell script') + + +parser = argparse.ArgumentParser() +parser.add_argument('infile', metavar='input.svg', type=argparse.FileType('r')) +parser.add_argument('outfile', metavar='output.svg', type=argparse.FileType('wb')) +parser.add_argument('-n', '--name', nargs='+', default=[], help='Remove layers with this exact name (case-insensitive)') +parser.add_argument('-r', '--regex', nargs='+', default=[], help='Remove layers with names matching this regex') +parser.add_argument('-i', '--invisible', action='store_true', help='Remove hidden (invisible) layers') +parser.add_argument('-o', '--only', action='store_true', help='Invert logic, i.e. keep matched layers and discard others') +parser.add_argument('-d', '--strip-defs', action='store_true', help='Also strip any <defs> tags (off by default)') +args = parser.parse_args() + +doc = xe.fromstring(args.infile.read()) +ns = { + 'svg': 'http://www.w3.org/2000/svg', + 'inkscape': 'http://www.inkscape.org/namespaces/inkscape', + 'sodipodi': 'http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' +} + +if args.strip_defs: + for elem in doc.findall('svg:defs', ns): + doc.remove(elem) + +for i, g in enumerate(doc.findall('svg:g', ns)): + if g.attrib.get(f'{{{ns["inkscape"]}}}groupmode') != 'layer': + continue + + label = g.attrib.get(f'{{{ns["inkscape"]}}}label', '') + match = ( + any(label == name for name in args.name) or + any(re.match(regex, label) for regex in args.regex) or + ('display:none' in g.attrib.get('style', '') and args.hidden) + ) + print(f'Layer {i} "{label}": {"match" if match else "not found"}', end='') + + if match != args.only: + print(', removing.') + doc.remove(g) + else: + print() + +args.outfile.write(xe.tostring(doc)) |