Skip to content

Commit

Permalink
Added support for defining parts using OpenSCAD
Browse files Browse the repository at this point in the history
  • Loading branch information
openvmp committed Jan 7, 2024
1 parent 37f5e3d commit f77f807
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 1 deletion.
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,20 @@ parts:
<br/>
Store the model in "cube.3mf"
</td>
<td><img src="https://github.com/openvmp/partcad/blob/main/examples/part_stl/cube.png?raw=true"></td>
<td><img src="https://github.com/openvmp/partcad/blob/main/examples/part_3mf/cube.png?raw=true"></td>
</tr>
<tr>
<td><a href="https://en.wikipedia.org/wiki/OpenSCAD">OpenSCAD</a></td>
<td>
<code># partcad.yaml
parts:
cube:
type: scad</code>
<br/>
Store the model in "cube.scad"
</td>
<td><img src="https://github.com/openvmp/partcad/blob/main/examples/part_scad/cube.png?raw=true"></td>
</tr>
<tr>
<td><a href="https://github.com/CadQuery/cadquery">CadQuery</a></td>
Expand Down
Binary file added examples/part_scad/cube.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/part_scad/cube.scad
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
translate (v= [0,0,0]) cube (size = 10);
11 changes: 11 additions & 0 deletions examples/part_scad/partcad.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
desc: PartCAD example project to demonstrate parts implemented using SCAD files
parts:
cube:
type: scad
desc: A cube defined in SCAD
render:
png:
prefix: ./
width: 128
height: 128
markdown: README.md
44 changes: 44 additions & 0 deletions src/partcad/part_factory_scad.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#
# OpenVMP, 2024
#
# Author: Roman Kuzmenko
# Created: 2024-01-06
#
# Licensed under Apache License, Version 2.0.
#

import os
import shutil
import subprocess
import tempfile

import build123d as b3d
from . import part_factory as pf
from . import part as p


class PartFactoryScad(pf.PartFactory):
def __init__(self, ctx, project, part_config):
super().__init__(ctx, project, part_config, extension=".scad")
# Complement the config object here if necessary
self._create(part_config)

self.project_dir = project.config_dir

def instantiate(self, part):
scad_path = shutil.which("openscad")
if scad_path is None:
raise Exception(
"OpenSCAD executable is not found. Please, install OpenSCAD first."
)

stl_path = tempfile.mktemp(".stl")
p = subprocess.run(
[scad_path, "--export-format", "binstl", "-o", stl_path, self.path],
capture_output=True,
)

shape = b3d.Mesher().read(stl_path)[0].wrapped
os.unlink(stl_path)

part.set_shape(shape)
4 changes: 4 additions & 0 deletions src/partcad/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import os

from . import project_config
from . import part_factory_scad as pfscad
from . import part_factory_step as pfs
from . import part_factory_stl as pfstl
from . import part_factory_3mf as pf3
Expand Down Expand Up @@ -96,6 +97,9 @@ def init_parts(self):
elif part_config["type"] == "3mf":
logging.info("Initializing 3mf part: %s..." % part_name)
pf3.PartFactory3mf(self.ctx, self, part_config)
elif part_config["type"] == "scad":
logging.info("Initializing OpenSCAD part: %s..." % part_name)
pfscad.PartFactoryScad(self.ctx, self, part_config)
else:
logging.error(
"Invalid part type encountered: %s: %s" % (part_name, part_config)
Expand Down
14 changes: 14 additions & 0 deletions tests/unit/test_part.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#

import partcad as pc
import pytest
import shutil

test_config_local = {
"name": "primitive_local",
Expand Down Expand Up @@ -57,6 +59,18 @@ def test_part_get_3mf():
assert part.get_wrapped() is not None


def test_part_get_scad():
"""Load an OpenSCAD part"""
scad_path = shutil.which("openscad")
if not scad_path is None:
ctx = pc.Context("examples/part_scad")
part = ctx.get_part("cube", "this")
assert part is not None
assert part.get_wrapped() is not None
else:
pytest.skip("No OpenSCAD installed")


def test_part_get_3():
"""Instantiate a project by a local import config and load a part"""
ctx = pc.Context() # Empty config
Expand Down

0 comments on commit f77f807

Please sign in to comment.