From e9c5343ec65160eebc3726e00cce1ca0c1ec1dae Mon Sep 17 00:00:00 2001 From: Ashton Snelgrove Date: Mon, 18 May 2026 14:15:38 -0600 Subject: [PATCH 1/3] Update import process to render the original supplied path rather than the absolute resolved one. --- solid2/core/scad_import.py | 50 ++++++++++++++++++++++++-------------- solid2/core/scad_render.py | 9 ++++--- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/solid2/core/scad_import.py b/solid2/core/scad_import.py index b5f13b04..a9cdad00 100644 --- a/solid2/core/scad_import.py +++ b/solid2/core/scad_import.py @@ -1,4 +1,5 @@ from types import SimpleNamespace +from typing import NamedTuple import inspect from .utils import resolve_scad_filename, escape_openscad_identifier @@ -10,6 +11,15 @@ module_cache_by_resolved_filename = {} extra_scad_includes = [] + +class ScadImport(NamedTuple): + resolved_scad: str + original: str + new_namespace_dict: dict + use_not_include: bool + skip_render: bool + relative: str + def check_module_cache(resolved_scad, use_not_include): global module_cache_by_resolved_filename @@ -19,19 +29,19 @@ def check_module_cache(resolved_scad, use_not_include): #if this is a include call and the module was only used until now #set the "use/include" flag in the module cache - if not use_not_include and cache_entry[1]: - cache_entry[1] = False + if not use_not_include and cache_entry.use_not_include: + cache_entry.use_not_include = False - return cache_entry[0] + return cache_entry.new_namespace_dict return None -def update_module_cache(resolved_scad, new_namespace_dict, use_not_include, skip_render): +def update_module_cache(resolved_scad, original, new_namespace_dict, use_not_include, skip_render, relative): global module_cache_by_resolved_filename module_cache_by_resolved_filename[resolved_scad] = \ - [new_namespace_dict, use_not_include, skip_render] + ScadImport(resolved_scad, original, new_namespace_dict, use_not_include, skip_render, relative) -def load_scad_file_into_dict(resolved_scad, dest_namespace_dict, use_not_include, skip_render): +def load_scad_file_into_dict(resolved_scad, original, dest_namespace_dict, use_not_include, skip_render, relative): #check the cache cached_dict = check_module_cache(resolved_scad, use_not_include) if cached_dict: @@ -43,9 +53,9 @@ def load_scad_file_into_dict(resolved_scad, dest_namespace_dict, use_not_include dest_namespace_dict.update(new_namespace_dict) #and update the cache - update_module_cache(resolved_scad, new_namespace_dict, use_not_include, skip_render) + update_module_cache(resolved_scad, original, new_namespace_dict, use_not_include, skip_render, relative) -def load_scad_dir_into_dict(resolved_scad, dest_namespace_dict, use_not_include, skip_render): +def load_scad_dir_into_dict(resolved_scad, original, dest_namespace_dict, use_not_include, skip_render, relative): assert(resolved_scad.is_dir()) #for each file in the dir @@ -56,13 +66,13 @@ def load_scad_dir_into_dict(resolved_scad, dest_namespace_dict, use_not_include, #load it subspace = ExpSolidNamespace(f) - load_scad_file_or_dir_into_dict(f, subspace.__dict__, use_not_include, skip_render) + load_scad_file_or_dir_into_dict(f, original, subspace.__dict__, use_not_include, skip_render) #and add it to the dest_namespace_dict identifier = escape_openscad_identifier(f.stem) dest_namespace_dict[identifier] = subspace -def load_scad_file_or_dir_into_dict(filename, dest_namespace_dict, use_not_include, skip_render): +def load_scad_file_or_dir_into_dict(filename, dest_namespace_dict, use_not_include, skip_render, relative): assert(dest_namespace_dict != None) resolved_scad = resolve_scad_filename(filename) @@ -72,14 +82,18 @@ def load_scad_file_or_dir_into_dict(filename, dest_namespace_dict, use_not_inclu if resolved_scad.is_file(): load_scad_file_into_dict(resolved_scad, + filename, dest_namespace_dict, use_not_include, - skip_render) + skip_render, + relative) elif resolved_scad.is_dir(): load_scad_dir_into_dict(resolved_scad, + filename, dest_namespace_dict, use_not_include, - skip_render) + skip_render, + relative) # use() & include() mimic OpenSCAD's use/include mechanics. # -- use() makes methods in scad_file_path.scad available to be called. @@ -100,17 +114,17 @@ def get_callers_namespace_dict(depth=2): "they would polute the modules namespace and only if executed. This\n" +\ "has strange side effects! Use them on module level.\n") -def use(filename, skip_render=False): - load_scad_file_or_dir_into_dict(filename, get_callers_namespace_dict(), True, skip_render) +def use(filename, skip_render=False, render_relative=False): + load_scad_file_or_dir_into_dict(filename, get_callers_namespace_dict(), True, skip_render, render_relative) -def include(filename, skip_render=False): - load_scad_file_or_dir_into_dict(filename, get_callers_namespace_dict(), False, skip_render) +def include(filename, skip_render=False, render_relative=False): + load_scad_file_or_dir_into_dict(filename, get_callers_namespace_dict(), False, skip_render, render_relative) -def import_scad(filename, dest_namespace=None, use_not_include=True, skip_render=False): +def import_scad(filename, dest_namespace=None, use_not_include=True, skip_render=False, render_relative=False): if dest_namespace == None: dest_namespace = ExpSolidNamespace(filename) - load_scad_file_or_dir_into_dict(filename, dest_namespace.__dict__, use_not_include, skip_render) + load_scad_file_or_dir_into_dict(filename, dest_namespace.__dict__, use_not_include, skip_render, render_relative) return dest_namespace diff --git a/solid2/core/scad_render.py b/solid2/core/scad_render.py index fdfd455e..678b4e87 100644 --- a/solid2/core/scad_render.py +++ b/solid2/core/scad_render.py @@ -88,17 +88,18 @@ def get_include_string(): else: strings.append(f"include <{file}>;") - for k, v in module_cache_by_resolved_filename.items(): + for v in module_cache_by_resolved_filename.values(): #skip builtins file - if v[2]: #skip_render flag + if v.skip_render: continue + k = v.resolved_scad if not v.relative else v.original - if v[1]: + if v.use_not_include: strings.append(f"use <{k}>;") else: strings.append(f"include <{k}>;") - s = "\n".join(strings) + s = "\n".join(set(strings)) s += "\n\n" if s else '' return s From 4fcde42e568bdb65c13b6f9ffdfdb3a1dcd655e8 Mon Sep 17 00:00:00 2001 From: Ashton Snelgrove Date: Sat, 24 Oct 2026 13:24:40 -0600 Subject: [PATCH 2/3] Render imports in the order of call to import --- solid2/core/scad_render.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solid2/core/scad_render.py b/solid2/core/scad_render.py index 678b4e87..b337da2c 100644 --- a/solid2/core/scad_render.py +++ b/solid2/core/scad_render.py @@ -99,7 +99,7 @@ def get_include_string(): else: strings.append(f"include <{k}>;") - s = "\n".join(set(strings)) + s = "\n".join(strings) s += "\n\n" if s else '' return s From b2ed67e14462315f09a1e58901d00a141d764c2c Mon Sep 17 00:00:00 2001 From: Ashton Snelgrove Date: Sat, 24 Oct 2026 13:25:09 -0600 Subject: [PATCH 3/3] Add floor, ceil, array, and x/y/z access to object access --- solid2/core/object_base/object_base_impl.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/solid2/core/object_base/object_base_impl.py b/solid2/core/object_base/object_base_impl.py index c8f65b44..84d8547c 100644 --- a/solid2/core/object_base/object_base_impl.py +++ b/solid2/core/object_base/object_base_impl.py @@ -165,6 +165,14 @@ def __neg__(self): return self.__unary_operator_base__("-") #other operators def __abs__(self): return OpenSCADConstant(f'abs({self})') + def __floor__(self): return OpenSCADConstant(f'floor({self})') + def __ceil__(self): return OpenSCADConstant(f'ceil({self})') + + #index access + def __getitem__(self, other): return OpenSCADConstant(f'{self}[{other}]') + def x(self): return OpenSCADConstant(f'{self}.x') + def y(self): return OpenSCADConstant(f'{self}.y') + def z(self): return OpenSCADConstant(f'{self}.z') #"illegal" operators def __eq__(self, other): return self.__operator_base__("==", other) #type: ignore