🎉 First commit
This commit is contained in:
commit
206c6e1bf0
149
.gitignore
vendored
Normal file
149
.gitignore
vendored
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
|
||||||
|
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||||
|
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||||
|
|
||||||
|
### Python ###
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/python
|
||||||
|
|
||||||
|
|
||||||
|
# VSCode
|
||||||
|
.vscode
|
15
bin/customizer.py
Normal file
15
bin/customizer.py
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import argparse
|
||||||
|
from openscad_pycustomizer import scad_parser
|
||||||
|
import json
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description='Process some integers.')
|
||||||
|
parser.add_argument('file', metavar='N', type=str,
|
||||||
|
help='The .scad file')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
print(json.dumps(
|
||||||
|
scad_parser.parse(args.file),
|
||||||
|
cls=scad_parser.CustomizerProfileEncoder,
|
||||||
|
indent=4
|
||||||
|
))
|
77
data/example.scad
Normal file
77
data/example.scad
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// Random value with no section
|
||||||
|
Value = 2; // Number
|
||||||
|
|
||||||
|
/* [Drop down box:] */
|
||||||
|
// combo box for number
|
||||||
|
Numbers=2; // [0, 1, 2, 3]
|
||||||
|
|
||||||
|
// combo box for string
|
||||||
|
Strings="foo"; // [foo, bar, baz]
|
||||||
|
|
||||||
|
//labeled combo box for numbers
|
||||||
|
Labeled_values=10; // [10:L, 20:M, 30:XL]
|
||||||
|
|
||||||
|
//labeled combo box for string
|
||||||
|
Labeled_value="S"; // [S:Small, M:Medium, L:Large]
|
||||||
|
|
||||||
|
/*[ Slider ]*/
|
||||||
|
// slider widget for number
|
||||||
|
slider =34; // [10:100]
|
||||||
|
|
||||||
|
//step slider for number
|
||||||
|
stepSlider=2; //[0:5:100]
|
||||||
|
|
||||||
|
/* [Checkbox] */
|
||||||
|
|
||||||
|
//description
|
||||||
|
Variable = true;
|
||||||
|
|
||||||
|
/*[Spinbox] */
|
||||||
|
|
||||||
|
// spinbox with step size 1
|
||||||
|
Spinbox = 5;
|
||||||
|
|
||||||
|
/* [Textbox] */
|
||||||
|
|
||||||
|
//Text box for vector with more than 4 elements
|
||||||
|
Vector6=[12,34,44,43,23,23];
|
||||||
|
|
||||||
|
// Text box for string
|
||||||
|
String="hello";
|
||||||
|
|
||||||
|
/* [Special vector] */
|
||||||
|
//Text box for vector with less than or equal to 4 elements
|
||||||
|
Vector1=[12]; //[0:2:50]
|
||||||
|
Vector2=[12,34]; //[0:2:50]
|
||||||
|
Vector3=[12,34,46]; //[0:2:50]
|
||||||
|
Vector4=[12,34,46,24]; //[0:2:50]
|
||||||
|
|
||||||
|
|
||||||
|
cube([2,3,4]);
|
||||||
|
|
||||||
|
|
||||||
|
echo(version=version());
|
||||||
|
|
||||||
|
intersection()
|
||||||
|
{
|
||||||
|
linear_extrude(height = 100, center = true, convexity= 3)
|
||||||
|
import(file = "advance_intersection.dxf");
|
||||||
|
rotate([0, 90, 0])
|
||||||
|
linear_extrude(height = 100, center = true, convexity= 3)
|
||||||
|
import(file = "advance_intersection.dxf");
|
||||||
|
rotate([90, 0, 0])
|
||||||
|
linear_extrude(height = 100, center = true, convexity= 3)
|
||||||
|
import(file = "advance_intersection.dxf");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Written by Clifford Wolf <clifford@clifford.at> and Marius
|
||||||
|
// Kintel <marius@kintel.net>
|
||||||
|
//
|
||||||
|
// To the extent possible under law, the author(s) have dedicated all
|
||||||
|
// copyright and related and neighboring rights to this software to the
|
||||||
|
// public domain worldwide. This software is distributed without any
|
||||||
|
// warranty.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the CC0 Public Domain
|
||||||
|
// Dedication along with this software.
|
||||||
|
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
6
pyproject.toml
Normal file
6
pyproject.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[build-system]
|
||||||
|
requires = [
|
||||||
|
"setuptools>=42",
|
||||||
|
"wheel"
|
||||||
|
]
|
||||||
|
build-backend = "setuptools.build_meta"
|
26
setup.cfg
Normal file
26
setup.cfg
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
[metadata]
|
||||||
|
name = openscad_pycustomizer
|
||||||
|
version = 0.0.1
|
||||||
|
author = Rodolphe Houdas
|
||||||
|
author_email = rodolphe@lunai.re
|
||||||
|
description = Parse customizer data from .scad files
|
||||||
|
long_description = file: README.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
url = https://forge.lunai.re/openscad-pycustomizer
|
||||||
|
project_urls =
|
||||||
|
Bug Tracker = https://forge.lunai.re/openscad-pycustomizer/issues
|
||||||
|
classifiers =
|
||||||
|
Programming Language :: Python :: 3
|
||||||
|
License :: OSI Approved :: MIT License
|
||||||
|
Operating System :: OS Independent
|
||||||
|
|
||||||
|
[options]
|
||||||
|
package_dir =
|
||||||
|
= src
|
||||||
|
scripts =
|
||||||
|
bin/customizer.py
|
||||||
|
packages = find:
|
||||||
|
python_requires = >=3.6
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
where = src
|
0
src/openscad_pycustomizer/__init__.py
Normal file
0
src/openscad_pycustomizer/__init__.py
Normal file
167
src/openscad_pycustomizer/scad_parser.py
Normal file
167
src/openscad_pycustomizer/scad_parser.py
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
import dataclasses
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Types that must be supported:
|
||||||
|
* int
|
||||||
|
* float
|
||||||
|
* string
|
||||||
|
* bool
|
||||||
|
* list
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Element:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Variable(Element):
|
||||||
|
text: str
|
||||||
|
value: str
|
||||||
|
description: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class IntVariable(Variable):
|
||||||
|
value: int
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class FloatVariable(Variable):
|
||||||
|
value: float
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class StringVariable(Variable):
|
||||||
|
value: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BoolVariable(Variable):
|
||||||
|
value: bool
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Section(Element):
|
||||||
|
text: str
|
||||||
|
elements: list[Element]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CustomizerData:
|
||||||
|
elements: list[Element]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def flatten(data):
|
||||||
|
"""
|
||||||
|
Return a list of parameters
|
||||||
|
"""
|
||||||
|
elems = {}
|
||||||
|
for elem in data.elements:
|
||||||
|
if isinstance(elem, Section):
|
||||||
|
for param in elem.elements:
|
||||||
|
elems[param.text] = param.value
|
||||||
|
else:
|
||||||
|
elems[elem.text] = elem.value
|
||||||
|
return elems
|
||||||
|
|
||||||
|
|
||||||
|
class EnhancedJSONEncoder(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
if dataclasses.is_dataclass(o):
|
||||||
|
return dataclasses.asdict(o)
|
||||||
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomizerProfileEncoder(json.JSONEncoder):
|
||||||
|
def default(self, o):
|
||||||
|
if isinstance(o, CustomizerData):
|
||||||
|
return CustomizerData.flatten(o)
|
||||||
|
return super().default(o)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_type(value):
|
||||||
|
# Parse type
|
||||||
|
if ((value[0] == "\"" or value[0] == "'")
|
||||||
|
and (value[-1] == "\"" or value[-1] == "'")):
|
||||||
|
value = value.strip("\"").strip("'")
|
||||||
|
|
||||||
|
elif value == "true" or value == "false":
|
||||||
|
value = bool(value)
|
||||||
|
|
||||||
|
elif value[0] == "[" and value[-1] == "]":
|
||||||
|
value = value[1:-2].split(",")
|
||||||
|
for index in range(len(value)):
|
||||||
|
value[index] = parse_type(value[index])
|
||||||
|
|
||||||
|
elif len(value.split(".")):
|
||||||
|
value = float(value)
|
||||||
|
|
||||||
|
else:
|
||||||
|
value = int(value)
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
def parse(file):
|
||||||
|
with open(file) as scad_file:
|
||||||
|
section = None
|
||||||
|
all_elems = []
|
||||||
|
elems = []
|
||||||
|
description = None
|
||||||
|
for line in scad_file.readlines():
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
# Checking for section title
|
||||||
|
if line[0:2] == "/*":
|
||||||
|
(middle, end) = line[2:].strip().split("*/")
|
||||||
|
middle = middle.strip()
|
||||||
|
|
||||||
|
if section is None:
|
||||||
|
all_elems.extend(elems)
|
||||||
|
else:
|
||||||
|
all_elems.append(Section(section, elems))
|
||||||
|
|
||||||
|
section = middle
|
||||||
|
elems = []
|
||||||
|
|
||||||
|
# Checking for comment
|
||||||
|
elif line[0:2] == "//":
|
||||||
|
description = line[2:].strip()
|
||||||
|
|
||||||
|
# Checking for text
|
||||||
|
elif len(line.strip()) > 0 and len(line.split("=")) == 2:
|
||||||
|
(name, value) = line.split("=")
|
||||||
|
name = name.strip()
|
||||||
|
value = value.split(";")[0].strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
value = parse_type(value)
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Error parsing a value: {e}")
|
||||||
|
print("Stopping parsing")
|
||||||
|
break
|
||||||
|
|
||||||
|
elems.append(
|
||||||
|
Variable(
|
||||||
|
name,
|
||||||
|
value,
|
||||||
|
"" if description is None else description
|
||||||
|
)
|
||||||
|
)
|
||||||
|
description = None
|
||||||
|
|
||||||
|
elif len(line) == 0:
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
all_elems.append(Section(section, elems))
|
||||||
|
|
||||||
|
data = CustomizerData(all_elems)
|
||||||
|
return data
|
Loading…
Reference in a new issue