forked from florianfesti/boxes
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
283 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
type Part = str | ||
type Edge = int | ||
type Tree = tuple[(Part, Edge), list[(Edge, Tree)]] | ||
|
||
class Assembly: | ||
"""An assembly describes how parts of a generator belong together. | ||
It is populated by calling base and antiparallel_edge methods. | ||
""" | ||
def __init__(self): | ||
self._base = None | ||
self._antiparallel_edges = [] | ||
|
||
def base(self, part, edge): | ||
self._base = (part, edge) | ||
|
||
def antiparallel_edge(self, part_a, edge_a, part_b, edge_b): | ||
"""Note that the indicated edges of parts A and part B are | ||
antiparallel. | ||
Antiparallel edges are the typical case in boxes scripts: Parts usually | ||
all follow the same direction (counterclockwise), and boxes are usually | ||
built so that the same side of the material faces out.""" | ||
self._antiparallel_edges.append(((part_a, edge_a), (part_b, edge_b))) | ||
|
||
def tree(self) -> Tree: | ||
"""Greedily assemble a tree out of all connected parts starting from | ||
the base. Currently this goes depth-first to make any alignment issues | ||
as visible as possible. | ||
""" | ||
|
||
def tree_from(self, start, exclude) -> tuple[(Tree, set[Part])]: | ||
children = [] | ||
covered_parts = exclude | {start[0]} | ||
for ((a, ae), (b, be)) in self._antiparallel_edges: | ||
child_tree = None | ||
if a == start[0] and b not in covered_parts: | ||
child_tree, new_covered_parts = tree_from(self, (b, be), covered_parts) | ||
child_tree_startingedge = ae | ||
elif b == start[0] and a not in covered_parts: | ||
child_tree, new_covered_parts = tree_from(self, (a, ae), covered_parts) | ||
child_tree_startingedge = be | ||
if child_tree is not None: | ||
covered_parts |= new_covered_parts | ||
children.append((child_tree_startingedge, child_tree)) | ||
|
||
return ((start, children), covered_parts) | ||
|
||
return tree_from(self, self._base, set())[0] | ||
|
||
def explain(self, *, _tree=None, _indent=0): | ||
if _tree is None: | ||
_tree = self.tree() | ||
|
||
((part, edge), children) = _tree | ||
print(" "*_indent, f"Painting part {part} starting at its edge {edge}") | ||
for (parentedge, child) in children: | ||
print(" "*_indent, f"at its edge {parentedge}:") | ||
self.explain(_tree=child, _indent=_indent + 1) | ||
|
||
def openscad(self, file, *, box, _tree=None, _indent=0): | ||
# FIXME: Given this takes box and assembly, it should move to a method | ||
# of the box, maybe (b/c the assembly may become a property of that) | ||
|
||
if _tree is None: | ||
_tree = self.tree() | ||
|
||
((part, edge), children) = _tree | ||
|
||
i = " " * _indent | ||
|
||
(parent_part,) = [p for p in box.surface.parts if p.name == part] | ||
|
||
print(f'{i}reverse_{part}_{edge}() {{ part("{part}"); ', file=file); | ||
for (parent_edge, child) in children: | ||
# We could inline the lengths and translations; currently they are | ||
# generated independently and probably that's a good thing because | ||
# it's useful for other interactions with the visualizations as | ||
# well (like for pointing out things in the planarized plot maybe? | ||
# Well, at least during debugging that was useful.) ... and before | ||
# we accessed self.surface.parts for the extra_metadata, this code | ||
# was just a function of the assembly and not the box/surface | ||
|
||
(child_part,) = [p for p in box.surface.parts if p.name == child[0][0]] | ||
# 0 and 1 are the coords and length we take for granted as a symbol in openscad | ||
parent_edge_kind = parent_part.oriented_markers[parent_edge][2]['edge_kind'] | ||
child_edge_kind = child_part.oriented_markers[child[0][1]][2]['edge_kind'] | ||
|
||
# This particular check is also done in verify() | ||
print(f'{i} assert(length_{part}_{parent_edge} == length_{child[0][0]}_{child[0][1]});', file=file) | ||
|
||
print(f'{i} forward_{part}_{parent_edge}() // Shift coordinates to the correct edge of the parent', file=file) | ||
|
||
parent_positive = getattr(parent_edge_kind, "positive", None) | ||
child_positive = getattr(child_edge_kind, "positive", None) | ||
from . import edges | ||
if isinstance(parent_edge_kind, edges.FingerJointEdge) and parent_positive == True and isinstance(child_edge_kind, edges.FingerHoleEdge): | ||
print(f'{i} translate([0, 0, -2*3]) rotate([-fold_angle, 0, 0]) // Fold up', file=file) # FIXME thickness and extract parameters | ||
elif isinstance(parent_edge_kind, edges.FingerJointEdge) and parent_positive == True and isinstance(child_edge_kind, edges.StackableEdge): | ||
print(f'{i} translate([0, 0, -2*3 - 8]) rotate([-fold_angle, 0, 0]) // Fold up', file=file) # FIXME thickness and extract parameters, worse: the 8 are just a guess | ||
elif parent_positive == False and child_positive == True: | ||
print(f'{i} rotate([-fold_angle, 0, 0]) translate([0, 0, 3]) // Fold up', file=file) # FIXME thickness | ||
elif parent_positive == True and child_positive == False: | ||
# It'd be great to autoamte that as inverse-of the other directin | ||
print(f'{i} translate([0, 0, -3]) rotate([-fold_angle, 0, 0]) // Fold up', file=file) # FIXME thickness | ||
else: | ||
print(f"Warning: No rules describe how to assemble a {parent_edge_kind} and a {child_edge_kind}. Coarsely rotating; children are marked in transparent red.") | ||
print(f"{i} #", file=file) | ||
print(f'{i} rotate([-fold_angle, 0, 0])', file=file) | ||
|
||
print(f'{i} translate([length_{part}_{parent_edge}, 0, 0]) rotate([0, 0, 180]) // Edge is antiparallel', file=file) | ||
|
||
|
||
self.openscad(file=file, box=box, _tree=child, _indent=_indent + 1) | ||
print(f"{i}}}", file=file) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.