mirror of
https://github.com/kennetek/gridfinity-rebuilt-openscad.git
synced 2024-11-09 10:00:50 +00:00
OpenScad Python Runner - Make camera_args a variable
As opposed to being a parameter of `create_image`. Also made CameraArguments support fluent.
This commit is contained in:
parent
cd56b0e421
commit
a4807fd3ab
4 changed files with 62 additions and 50 deletions
|
@ -2,6 +2,7 @@
|
|||
Helpful classes for running OpenScad from Python.
|
||||
@Copyright Arthur Moore 2024 MIT License
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
|
@ -24,17 +25,27 @@ class Vec3(NamedTuple):
|
|||
y: float
|
||||
z: float
|
||||
|
||||
@dataclass
|
||||
@dataclass(frozen=True)
|
||||
class CameraArguments:
|
||||
"""
|
||||
Controls the camera position when outputting to png format.
|
||||
@see `openscad -h`.
|
||||
Supports fluid interface.
|
||||
"""
|
||||
translate: Vec3
|
||||
rotate: Vec3
|
||||
distance: float
|
||||
|
||||
def as_argument(self):
|
||||
def with_translation(self, new_translate: Vec3) -> CameraArguments:
|
||||
return CameraArguments(translate=new_translate, rotate=self.rotate, distance=self.distance)
|
||||
|
||||
def with_rotation(self, new_rotate: Vec3) -> CameraArguments:
|
||||
return CameraArguments(translate=self.translate, rotate=new_rotate, distance=self.distance)
|
||||
|
||||
def with_distance(self, new_distance: float) -> CameraArguments:
|
||||
return CameraArguments(translate=self.translate, rotate=rotate, distance=new_distance)
|
||||
|
||||
def as_argument(self) -> str:
|
||||
return '--camera=' \
|
||||
f'{",".join(map(str,self.translate))},{",".join(map(str,self.rotate))},{self.distance}'
|
||||
|
||||
|
@ -79,6 +90,13 @@ def set_variable_argument(var: str, val: str) -> [str, str]:
|
|||
"""
|
||||
return ['-D', f'{var}={str(val)}']
|
||||
|
||||
class CameraRotations:
|
||||
'''Pre-defined useful camera rotations'''
|
||||
Default = Vec3(0,0,0),
|
||||
AngledTop = Vec3(45,0,45)
|
||||
AngledBottom = Vec3(225,0,225)
|
||||
Top = Vec3(45,0,0)
|
||||
|
||||
class OpenScadRunner:
|
||||
'''Helper to run the openscad binary'''
|
||||
scad_file_path: Path
|
||||
|
@ -88,7 +106,7 @@ class OpenScadRunner:
|
|||
'''If set, a temporary parameter file is created, and used with these variables'''
|
||||
|
||||
WINDOWS_DEFAULT_PATH = 'C:\\Program Files\\OpenSCAD\\openscad.exe'
|
||||
TOP_ANGLE_CAMERA = CameraArguments(Vec3(0,0,0),Vec3(45,0,45),50)
|
||||
TOP_ANGLE_CAMERA = CameraArguments(Vec3(0,0,0),Vec3(45,0,45),150)
|
||||
|
||||
common_arguments = [
|
||||
#'--hardwarnings', // Does not work when setting variables by using functions
|
||||
|
@ -106,9 +124,10 @@ class OpenScadRunner:
|
|||
self.openscad_binary_path = self.WINDOWS_DEFAULT_PATH
|
||||
self.scad_file_path = file_path
|
||||
self.image_folder_base = Path('.')
|
||||
self.camera_arguments = None
|
||||
self.parameters = None
|
||||
|
||||
def create_image(self, camera_args: CameraArguments, args: [str], image_file_name: str):
|
||||
def create_image(self, args: [str], image_file_name: str):
|
||||
"""
|
||||
Run the code, to create an image.
|
||||
@Important The only verification is that no errors occured.
|
||||
|
@ -119,11 +138,13 @@ class OpenScadRunner:
|
|||
|
||||
image_path = self.image_folder_base.joinpath(image_file_name)
|
||||
command_arguments = self.common_arguments + \
|
||||
[camera_args.as_argument()] + args + \
|
||||
([self.camera_arguments.as_argument()] if self.camera_arguments != None else []) + \
|
||||
args + \
|
||||
["-o", str(image_path), str(self.scad_file_path)]
|
||||
#print(command_arguments)
|
||||
|
||||
if self.parameters != None:
|
||||
#print(self.parameters)
|
||||
params = ParameterFile(parameterSets={"python_generated": self.parameters})
|
||||
with NamedTemporaryFile(prefix="gridfinity-rebuilt-", suffix=".json", mode='wt',delete_on_close=False) as file:
|
||||
json.dump(params, file, sort_keys=True, indent=2, cls=DataClassJSONEncoder)
|
||||
|
|
|
@ -23,26 +23,26 @@ class TestBinHoles(unittest.TestCase):
|
|||
parameter_file_path = Path("gridfinity-rebuilt-bins.json")
|
||||
parameter_file_data = ParameterFile.from_json(parameter_file_path.read_text())
|
||||
cls.default_parameters = parameter_file_data.parameterSets["Default"]
|
||||
cls.camera_args = CameraArguments(Vec3(0,0,0),Vec3(225,0,225),150)
|
||||
|
||||
def setUp(self):
|
||||
self.scad_runner = OpenScadRunner(Path('../gridfinity-rebuilt-bins.scad'))
|
||||
self.scad_runner.image_folder_base = Path('../images/base_hole_options/')
|
||||
self.scad_runner.parameters = self.default_parameters.copy()
|
||||
self.scad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 150)
|
||||
|
||||
def test_no_holes(self):
|
||||
vars = self.scad_runner.parameters
|
||||
vars["refined_holes"] = False
|
||||
vars["magnet_holes"] = False
|
||||
vars["screw_holes"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('no_holes.png'))
|
||||
self.scad_runner.create_image([], Path('no_holes.png'))
|
||||
|
||||
def test_refined_holes(self):
|
||||
vars = self.scad_runner.parameters
|
||||
vars["refined_holes"] = True
|
||||
vars["magnet_holes"] = False
|
||||
vars["screw_holes"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('refined_holes.png'))
|
||||
self.scad_runner.create_image([], Path('refined_holes.png'))
|
||||
|
||||
def test_refined_and_screw_holes(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -50,7 +50,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["magnet_holes"] = False
|
||||
vars["screw_holes"] = True
|
||||
vars["printable_hole_top"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('refined_and_screw_holes.png'))
|
||||
self.scad_runner.create_image([], Path('refined_and_screw_holes.png'))
|
||||
|
||||
def test_screw_holes_plain(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -58,7 +58,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["magnet_holes"] = False
|
||||
vars["screw_holes"] = True
|
||||
vars["printable_hole_top"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('screw_holes_plain.png'))
|
||||
self.scad_runner.create_image([], Path('screw_holes_plain.png'))
|
||||
|
||||
def test_screw_holes_printable(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -66,7 +66,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["magnet_holes"] = False
|
||||
vars["screw_holes"] = True
|
||||
vars["printable_hole_top"] = True
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('screw_holes_printable.png'))
|
||||
self.scad_runner.create_image([], Path('screw_holes_printable.png'))
|
||||
|
||||
def test_magnet_holes_plain(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -76,7 +76,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["crush_ribs"] = False
|
||||
vars["chamfer_holes"] = False
|
||||
vars["printable_hole_top"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('magnet_holes_plain.png'))
|
||||
self.scad_runner.create_image([], Path('magnet_holes_plain.png'))
|
||||
|
||||
def test_magnet_holes_chamfered(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -86,7 +86,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["crush_ribs"] = False
|
||||
vars["chamfer_holes"] = True
|
||||
vars["printable_hole_top"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('magnet_holes_chamfered.png'))
|
||||
self.scad_runner.create_image([], Path('magnet_holes_chamfered.png'))
|
||||
|
||||
def test_magnet_holes_printable(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -96,7 +96,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["crush_ribs"] = False
|
||||
vars["chamfer_holes"] = False
|
||||
vars["printable_hole_top"] = True
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('magnet_holes_printable.png'))
|
||||
self.scad_runner.create_image([], Path('magnet_holes_printable.png'))
|
||||
|
||||
def test_magnet_holes_with_crush_ribs(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -106,7 +106,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["crush_ribs"] = True
|
||||
vars["chamfer_holes"] = False
|
||||
vars["printable_hole_top"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('magnet_holes_with_crush_ribs.png'))
|
||||
self.scad_runner.create_image([], Path('magnet_holes_with_crush_ribs.png'))
|
||||
|
||||
def test_magnet_and_screw_holes_plain(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -116,7 +116,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["crush_ribs"] = False
|
||||
vars["chamfer_holes"] = False
|
||||
vars["printable_hole_top"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('magnet_and_screw_holes_plain.png'))
|
||||
self.scad_runner.create_image([], Path('magnet_and_screw_holes_plain.png'))
|
||||
|
||||
def test_magnet_and_screw_holes_printable(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -126,7 +126,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["crush_ribs"] = False
|
||||
vars["chamfer_holes"] = False
|
||||
vars["printable_hole_top"] = True
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('magnet_and_screw_holes_printable.png'))
|
||||
self.scad_runner.create_image([], Path('magnet_and_screw_holes_printable.png'))
|
||||
|
||||
def test_magnet_and_screw_holes_all(self):
|
||||
vars = self.scad_runner.parameters
|
||||
|
@ -136,7 +136,7 @@ class TestBinHoles(unittest.TestCase):
|
|||
vars["crush_ribs"] = True
|
||||
vars["chamfer_holes"] = True
|
||||
vars["printable_hole_top"] = True
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('magnet_and_screw_holes_all.png'))
|
||||
self.scad_runner.create_image([], Path('magnet_and_screw_holes_all.png'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -15,73 +15,64 @@ class TestHoleCutouts(unittest.TestCase):
|
|||
Currently only makes sure code runs, and outputs pictures for manual verification.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.scad_runner = OpenScadRunner(Path('../gridfinity-rebuilt-holes.scad'))
|
||||
cls.scad_runner.image_folder_base = Path('../images/hole_cutouts/')
|
||||
def setUp(self):
|
||||
self.scad_runner = OpenScadRunner(Path('../gridfinity-rebuilt-holes.scad'))
|
||||
self.scad_runner.image_folder_base = Path('../images/hole_cutouts/')
|
||||
self.scad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledTop, 50)
|
||||
|
||||
def test_refined_hole(self):
|
||||
"""
|
||||
refined_hole() is special, since top_angle_camera is not appropriate for it.
|
||||
refined_hole() is special, since top_angle_camera is not appropriate for it.
|
||||
"""
|
||||
camera_args = CameraArguments(Vec3(0,0,0),Vec3(225,0,225),50)
|
||||
self.scad_runner.camera_arguments = self.scad_runner.camera_arguments.with_rotation(CameraRotations.AngledBottom)
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=true, magnet_hole=false, screw_hole=false, crush_ribs=false, chamfer=false, supportless=false)')
|
||||
self.scad_runner.create_image(camera_args, test_args, Path('refined_hole.png'))
|
||||
self.scad_runner.create_image(test_args, Path('refined_hole.png'))
|
||||
|
||||
def test_plain_magnet_hole(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=true, screw_hole=false, crush_ribs=false, chamfer=false, supportless=false)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('magnet_hole.png'))
|
||||
self.scad_runner.create_image(test_args, Path('magnet_hole.png'))
|
||||
|
||||
def test_plain_screw_hole(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=false, screw_hole=true, crush_ribs=false, chamfer=false, supportless=false)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('screw_hole.png'))
|
||||
self.scad_runner.create_image(test_args, Path('screw_hole.png'))
|
||||
|
||||
def test_magnet_and_screw_hole(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=true, screw_hole=true, crush_ribs=false, chamfer=false, supportless=false)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('magnet_and_screw_hole.png'))
|
||||
self.scad_runner.create_image(test_args, Path('magnet_and_screw_hole.png'))
|
||||
|
||||
def test_chamfered_magnet_hole(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=true, screw_hole=false, crush_ribs=false, chamfer=true, supportless=false)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('chamfered_magnet_hole.png'))
|
||||
self.scad_runner.create_image(test_args, Path('chamfered_magnet_hole.png'))
|
||||
|
||||
def test_magnet_hole_crush_ribs(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=true, screw_hole=false, crush_ribs=true, chamfer=false, supportless=false)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('magnet_hole_crush_ribs.png'))
|
||||
self.scad_runner.create_image(test_args, Path('magnet_hole_crush_ribs.png'))
|
||||
|
||||
def test_magnet_hole_supportless(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=true, screw_hole=false, crush_ribs=false, chamfer=false, supportless=true)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('magnet_hole_supportless.png'))
|
||||
self.scad_runner.create_image(test_args, Path('magnet_hole_supportless.png'))
|
||||
|
||||
def test_magnet_and_screw_hole_supportless(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=true, screw_hole=true, crush_ribs=false, chamfer=false, supportless=true)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('magnet_and_screw_hole_supportless.png'))
|
||||
self.scad_runner.create_image(test_args, Path('magnet_and_screw_hole_supportless.png'))
|
||||
|
||||
def test_all_hole_options(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=true, screw_hole=true, crush_ribs=true, chamfer=true, supportless=true)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('all_hole_options.png'))
|
||||
self.scad_runner.create_image(test_args, Path('all_hole_options.png'))
|
||||
|
||||
def test_no_hole(self):
|
||||
test_args = set_variable_argument('test_options',
|
||||
'bundle_hole_options(refined_hole=false, magnet_hole=false, screw_hole=false, crush_ribs=true, chamfer=true, supportless=true)')
|
||||
self.scad_runner.create_image(self.scad_runner.TOP_ANGLE_CAMERA,
|
||||
test_args, Path('no_hole.png'))
|
||||
self.scad_runner.create_image(test_args, Path('no_hole.png'))
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -23,27 +23,27 @@ class TestSpiralVaseBase(unittest.TestCase):
|
|||
parameter_file_path = Path("gridfinity-spiral-vase.json")
|
||||
parameter_file_data = ParameterFile.from_json(parameter_file_path.read_text())
|
||||
cls.default_parameters = parameter_file_data.parameterSets["Default"]
|
||||
cls.camera_args = CameraArguments(Vec3(0,0,0),Vec3(225,0,225),150)
|
||||
|
||||
def setUp(self):
|
||||
self.scad_runner = OpenScadRunner(Path('../gridfinity-spiral-vase.scad'))
|
||||
self.scad_runner.image_folder_base = Path('../images/spiral_vase_base/')
|
||||
self.scad_runner.parameters = self.default_parameters.copy()
|
||||
self.scad_runner.parameters["type"] = 1 # Create a Base
|
||||
self.scad_runner.camera_arguments = CameraArguments(Vec3(0,0,0), CameraRotations.AngledBottom, 150)
|
||||
|
||||
def test_no_holes(self):
|
||||
vars = self.scad_runner.parameters
|
||||
vars["enable_holes"] = False
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('no_holes_bottom.png'))
|
||||
self.camera_args = CameraArguments(Vec3(0,0,0),Vec3(45,0,0),150)
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('no_holes_top.png'))
|
||||
self.scad_runner.create_image([], Path('no_holes_bottom.png'))
|
||||
self.scad_runner.camera_arguments = self.scad_runner.camera_arguments.with_rotation(CameraRotations.Top)
|
||||
self.scad_runner.create_image([], Path('no_holes_top.png'))
|
||||
|
||||
def test_refined_holes(self):
|
||||
vars = self.scad_runner.parameters
|
||||
vars["enable_holes"] = True
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('with_holes_bottom.png'))
|
||||
self.camera_args = CameraArguments(Vec3(0,0,0),Vec3(45,0,0),150)
|
||||
self.scad_runner.create_image(self.camera_args, [], Path('with_holes_top.png'))
|
||||
self.scad_runner.create_image([], Path('with_holes_bottom.png'))
|
||||
self.scad_runner.camera_arguments = self.scad_runner.camera_arguments.with_rotation(CameraRotations.Top)
|
||||
self.scad_runner.create_image([], Path('with_holes_top.png'))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
Loading…
Reference in a new issue