jeff-dh / SolidPython

A python frontend for solid modelling that compiles to OpenSCAD
143 stars 25 forks source link

Escaped OpenSCAD identifiers don't get exposed (because a single _ makes them private) #10

Closed jreiberkyle closed 1 year ago

jreiberkyle commented 1 year ago

A part of my workflow is to import stls into my openscad project. I couldn't figure out a way to do this in solid2 so I created a new class that extends OpenSCADObject, shown below. Would there be any interest in adding this to solid2 somewhere?

class imported(OpenSCADObject):
    def __init__(self, path, name='Imported', params={}):
        self.path = path
        super().__init__(name=name, params=params)

    def _render(self):
        return f'import("{self.path}");'
jeff-dh commented 1 year ago

https://github.com/jeff-dh/SolidPython/blob/07f56361058a6861ef157136acdb3a0e1c1a2740/solid2/core/builtin_primitives.py#L656

but we just discovered a bug, becaused it's (still) called import_ instead of _import.

I need to think about it -- the renaming -- a little, because the _foobar policy does not work properly in this situation (_foobar -> private class)

jeff-dh commented 1 year ago

So, I can't come up with a solution for the naming issue right now. The issue is the following:

OpenSCAD identifiers -- which get imported into SolidPython -- might be illegal python identifiers (keywords like import or they might start with $ or with a digit). The solution implemented when refactoring SolidPython is to prepend a single underscore for each illegal identifier. This turns out to have the issue, that a single underscore defines a private "thing" in python and therefor _import is not exposed. I don't have a solution for this up until now. If anyone has any idea, let me know. (trailing underscores do not work, because 12ptStar -> 12ptStar_ is still an illegal python identifier)

For now you should use the import_ "command". Note, there's also import_stl and import_dxf. All of those "commands" are part of the builtins and get imported by default when you execute from solid2 import *.

Even though this is not the ideal solution, I guess it makes your proposal obsolete, right?

I would leave this ticket open (and rename it) because of the escape identifier issue.

jeff-dh commented 1 year ago

In other words, we need a different escaping mechanism or a "python hack" to expose private classes / functions.

Any ideas and suggestions are welcome!

cf. https://github.com/jeff-dh/SolidPython/blob/07f56361058a6861ef157136acdb3a0e1c1a2740/solid2/core/utils.py#L13

jreiberkyle commented 1 year ago

import_() works great! For me, this was more of an awareness issue than an issue with the naming convention. From my standpoint, where I'd rather not endure a breaking change update such as _fn becoming something else, I'd be happy with adding import_() (and any functions with similar override-averting name changes) specifically to the docs (aka a summary of how to map from one to the other).

jreiberkyle commented 1 year ago

For the sake of brainstorming, a more radical idea that came to mind is a function look up table or factory, akaopenscad('import') which returns import_. This is a paradigm shift from how things are implemented and I'm not sure of the usability, but it is an idea!

jreiberkyle commented 1 year ago

I'd be happy to contribute an example for using import_ if one doesn't exist yet. Let me know where you'd like to see it.

jeff-dh commented 1 year ago

Yeah, I'd be happy to merge it. Add an example as 18_xyz.py to the example folder and create a pull request.

I'm wondering what you're actually doing (considering your other ticket). If you manipulate a imported stl with BOSL2 (e.g. attaching stuff to a stl using BOSL2?) I think that would be a really cool example. (Don't know whether it's possible at all....)

jreiberkyle commented 1 year ago

here's an example of what I'm doing with import:

```python
def traffic_cone():
    cone_file = 'traffic-cone.stl'
    cone_center = [44.5/2, 44.5/2]
    return import_(cone_file).translate([-v for v in cone_center])

No BOSL2 here but it is possible to attach stuff to a stl with BOSL2 just like anything else, you just have to do the work of making it attachable, which requires you know the size / shape of the stl.

Given that we are working in Python and not subject to the OpenSCAD paradigm of not knowing the size of anything, I've begun to fall out of love with attaching objects using the BOSL2 attachable solution, though I absolutely love it for orienting geometries. I still feel like I have a lot to learn in how to enable attachments with non-cube geometries. It is definitely powerful in the OpenSCAD paradigm where modules do not carry any additional information beside the base geometry. However, we have to know the size to make a shape attachable, and once we've made it attachable it's not easy to access the size parameter again. I find myself tempted to add a _size property to OpenSCADObject and do my own math, but I have satisfied myself thus far with sometimes returning a tuple of (OpenSCADObject, size).

Here's an example of that approach:

def bottom_cone_band(hole=False, _fn=FN_VIEW, anchor=aCENTER, spin=0, orient=UP):
    top_height = CONE_HEIGHT - TOP_BAND_SPACING - TOP_BAND_WIDTH - BOTTOM_BAND_SPACING
    obj, size = _cone_band_and_size(BOTTOM_BAND_WIDTH, top_height, hole=hole, _fn=_fn)
    return attachable(anchor, spin, orient, size=size) (obj)

def _cone_band_and_size(width, top_height, _fn):
    shape_width = EXTRUDE_WIDTH+epsilon

    base_height = top_height - width
    top_od = CONE_BOTTOM_OD - 2*top_height/tan(CONE_TAPER_ANGLE_RAD)
    bottom_od = CONE_BOTTOM_OD - 2*base_height/tan(CONE_TAPER_ANGLE_RAD)
    obj = tube(h=width, od1=bottom_od+2*epsilon, od2=top_od+2*epsilon, wall=shape_width, _fn=_fn, anchor=aCENTER)
    size = [bottom_od, bottom_od, width]

    return obj.up(dz), size
jeff-dh commented 1 year ago

I extracted the current essence of this issue into #17 and gonna close this issue.