Skip to content

Commit 45207c1

Browse files
committed
Sort vertices in STLs to make them diff nice
1 parent 692de06 commit 45207c1

2 files changed

Lines changed: 85 additions & 1 deletion

File tree

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
OPENSCAD=/usr/bin/openscad
22
OPENSCAD_OPTIONS=-q
3+
STL_SORTER=lib/sort_stl.py
34
IMAGE_OPTIONS=--imgsize=1024,768 --colorscheme "Tomorrow Night" --render true -D orient_for_printing=false
45
THUMBNAIL_OPTIONS=--imgsize=400,300 --colorscheme "Tomorrow Night" --render true -D fast=true -D orient_for_printing=false
56

@@ -28,6 +29,7 @@ clean:
2829
export/%.stl: src/%.scad
2930
@mkdir -p $(shell dirname $@)
3031
$(OPENSCAD) $(OPENSCAD_OPTIONS) -D orient_for_printing=true -o $@ $<
32+
$(STL_SORTER) $@
3133

3234
export-all : $(STLS)
3335

@@ -55,7 +57,7 @@ define logo-part-rule
5557
export/logo/$(1)/$(shell basename $(2))-$(4)-$(3).stl: src/$(2).scad logo/$(1)/$(shell basename $(2)).svg export/$(2).stl
5658
@mkdir -p $$(shell dirname $$@)
5759
$$(OPENSCAD) $$(OPENSCAD_OPTIONS) -D orient_for_printing=true -D logo_enabled=true -D logo_use_prebuild=true -D 'logo_mode="$(4)"' -D 'logo_part="$(3)"' -D 'logo_name="$(1)"' -o $$@ $$<
58-
60+
$$(STL_SORTER) $$@
5961
logo-$1-$4: export/logo/$(1)/$(shell basename $(2))-$(4)-$(3).stl
6062
logo-$1: export/logo/$(1)/$(shell basename $(2))-$(4)-$(3).stl
6163
logos: logo-$1
@@ -86,6 +88,7 @@ define numbered-cover-rule
8688
export/Mounting/AttachmentCover_$(1).stl: src/Mounting/AttachmentCover.scad
8789
@mkdir -p $$(shell dirname $$@)
8890
$(OPENSCAD) $(OPENSCAD_OPTIONS) -D orient_for_printing=true -D attachment_cover_number_text=$(1) -o $$@ $$<
91+
$(STL_SORTER) $@
8992
endef
9093

9194
$(foreach number,$(shell seq 0 1 100), \

lib/sort_stl.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import re
4+
from pathlib import Path
5+
6+
7+
class Element():
8+
def __init__(self, line):
9+
self.startline = line
10+
self.children = []
11+
self.current_child = None
12+
self.endline = None
13+
14+
def parse(self, line):
15+
16+
if self.current_child is None:
17+
for Childtype in self._contains:
18+
if Childtype._startswith.match(line):
19+
self.current_child = Childtype(line)
20+
else:
21+
print(f"{Childtype} did not match {line} for {self.__class__}")
22+
assert self.current_child is not None, f"should have found type for line {line}"
23+
elif self.current_child._endswith.match(line):
24+
self.current_child.endline = line
25+
self.children.append(self.current_child)
26+
self.current_child = None
27+
else:
28+
self.current_child.parse(line)
29+
30+
def __str__(self):
31+
return self.startline + "".join(sorted([str(c) for c in self.children])) + self.endline
32+
33+
34+
class Loop(Element):
35+
_startswith = re.compile("^\s+(?:outer|inner) loop$")
36+
_endswith = re.compile("^\s+endloop$")
37+
_footer = "endloop"
38+
39+
def parse(self, line):
40+
self.children.append(line)
41+
42+
def __str__(self):
43+
return self.startline + "".join(self.children) + self.endline
44+
45+
46+
class Facet(Element):
47+
_startswith = re.compile("^\s+facet\s+(.*)$")
48+
_endswith = re.compile("^\s+endfacet$")
49+
_contains = (Loop,)
50+
51+
52+
class Model(Element):
53+
_startswith = re.compile("^\s*solid\s+(.*)$")
54+
_endswith = re.compile("^\s*endsolid\s+(.*)$")
55+
_contains = (Facet,)
56+
57+
58+
class STL(Element):
59+
_contains = (Model,)
60+
endline = ""
61+
62+
def __init__(self, filename):
63+
self.startline = ""
64+
data = open(filename, "r").readlines()
65+
self.children = []
66+
self.current_child = None
67+
self.read(data)
68+
69+
def read(self, data):
70+
for line in data:
71+
self.parse(line)
72+
73+
74+
if __name__ == "__main__":
75+
parser = argparse.ArgumentParser(description="stl sorter for deterministic diffing")
76+
parser.add_argument('stlfile', metavar="STLFILE", type=Path, help="path of the STL to sort (in place)")
77+
args = parser.parse_args()
78+
79+
s = STL(args.stlfile)
80+
s_as_str = str(s)
81+
open(args.stlfile, "w").write(s_as_str)

0 commit comments

Comments
 (0)