Replies: 12 comments 31 replies
-
1st i was impressed that i got manifold as simple as
pip install manifold3d
bold trial and succeeded. i start liking pip
then i coded our 1st example but it quit with a segfault unfortunately. it
appears it comes with the show line
Generally i am wondering, whats the perfect display engine to manifold3d.
(i prefer something where i feel inside the cockpit)
Manifold3d python layer appears very attractive, but i like the concept of
c code (like openscad/pythonscad) layer above manifold.
It gives me the freedom to implement new fancy operations on points when i
have an idea
When only getting manifold directly into python, i don't have a fast c
layer for my algorithms ...
…On Wed, Dec 18, 2024 at 8:06 PM jeff ***@***.***> wrote:
@ipsod <https://github.com/ipsod> @gsohler <https://github.com/gsohler>
After our discussion in #71
<#71> &
gsohler/openscad#51 <gsohler/openscad#51> I had
a closer look at "it" (as indented by ipsod ;) and had a look at
libmanifold directly.
To my suprise libmanifold is actually exactly the alternative backend for
SolidPython that I was looking for for quite some time. And furthermore:
it's api is very close to the openscad api and it has a python interface.
This makes a library like SolidPython almost(!) redundant.
With manifold3d and trimesh you can do this:
>>> from manifold3d import Manifold>>> from trimesh import Trimesh>>> c = Manifold.cube((1, 2, 3))>>> s = Manifold.sphere(3)>>> mesh = (c + s).to_mesh()>>> tmesh = Trimesh(mesh.vert_properties, mesh.tri_verts)>>> tmesh.show()SceneViewer(width=1366, height=724)>>> tmesh.export("test.stl")
That's all the base features we're looking for out of the box in less than
10 lines, right?
As I said earlier this make a library like SolidPython from my point of
view almost redundant. But only almost, because:
What I think SolidPython is about is that SolidPython provides and
intuitive and easy to use api to model 3d objects in python without any
overhead. Even though libmanifold and trimesh together provide all the base
features we need I don't think that's the api I want to use when "scading"
stuff.
With a SolidPython like library the lines above become:
from solidxyz import cube, sphere
(cube(1, 2, 3) + sphere(3)).save("test.stl")
And that's why I think we still want something like SolidPython but
instead of using openscad we could use libmanifold (and trimesh) as
backend. I drafted a very basic solidng
<https://gist.github.com/jeff-dh/60947454c6e358ad5f8a9fc407787903>
library. With that mini wrapper library (~90 lines) ontop of libmanifold
and trimesh, we can actually run SolidPython code, create stls and even
show a window displaying the model.
This is our cuboctahedron exercise with solidng:
from solidng import sphere, cube
#custom "primitive"def cuboctahedron(r=1.0, fn=10):
def cuboctahedronFunc(v):
dirs=[[ 0.0, 0.0, 1.0], [ 1.0, 0.0, 0.0], [ 0.0, 1.0, 0.0],
[ 0.5, 0.5, 0.5], [ 0.5, 0.5,-0.5],
[ 0.5,-0.5, 0.5], [ 0.5,-0.5,-0.5]]
cf = max([abs(v.dot(dir)) for dir in dirs])
return 10/pow(cf,1.5)
def unitsphere2cuboctahedron(v):
from numpy import array
return array(v) * cuboctahedronFunc(array(v))*r
s = sphere(segments=fn)
return s.warp(unitsphere2cuboctahedron)
#a cuboctahedron ontop of a boxcubo = cuboctahedron(r=1.0,fn=100)box = cube(cubo.bounding_box.size[0], cubo.bounding_box.size[1], 5, True)\
.translate(0, 0, -cubo.bounding_box.size[2]/2-2.5)\
.color(1, 0, 0)
scene = cubo + box
#outputscene.export("supersphere.stl")scene.export("supersphere.3mf")
print("Cuboctahedron:")print(f" center mass: {cubo.center_of_mass}")print(f" volume: {cubo.volume}")print(f" bounding box: {cubo.bounding_box.min}, {cubo.bounding_box.max}")
scene.show()
outputs:
Cuboctahedron:
center mass: [ 2.83840607e-16 -1.41920303e-16 -2.12880455e-16]
volume: 8544.651898297738
bounding box: [-11.71239151 -11.71239151 -11.71239151], [11.71239151 11.71239151 11.71239151]
cubobox.png (view on web)
<https://github.com/user-attachments/assets/748e1a45-6048-4ed0-a892-07ad5671a296>
The car example
<https://github.com/jeff-dh/SolidPython/blob/master-2.0.0-beta-dev/solid2/examples/06-functions.py>
works with solidng without any modifications (except import and save).
So the big question for me now is: What do we actually want?
Do we want / need any openscad "support"? If so, what do we need / want?
Do we want to generate openscad code (to release it on thingiverse) or do
we only care about creating stl files?
Do we want to stick to bosl2 or do we maybe implement our own standard
library in python?
You get the point? What do we actually want to do?
So I'd like to hear your thoughts and ideas about it!
—
Reply to this email directly, view it on GitHub
<#72>, or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACCO4MWI2E6JJG2IX4JKDJ32GHBUHAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43ERDJONRXK43TNFXW4OZXG4YTANJRGU>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
That's exciting. Do it from scratch. Don't mingle languages, because the boundaries get complex. Choose simplicity. I don't like when I end up having to debug OpenSCAD code - the context switch is jarring, and I'm perpetually a beginner. I don't care about the customizer. It's easy to add on something like that, with FastAPI or whatever. Maybe hard to deploy to thingiverse, but... don't care. I've been working on some code around initial object placement and sizing. Instead of a center bool, you can use "C" for center, "U" for sitting on the origin, "D" for below it, and also "LFD" or "DFL" (etc.) for "left front down". I also sometimes give objects not just measurements, but extensions, like a normal hole will have extensions up and down 0.01mm (and "UD" == "Z", in the language, so it's easy to extend both), and these extensions get in the way when placing the object if we aren't aware. I'd really like it if this could be extended. I want to be able to say |
Beta Was this translation helpful? Give feedback.
-
Yes it is.
I tried to do my own 3D CAD in the past from scratch, but openscad had so
many more features than my cad, that I would never be able to reach
openscad level.
my 2nd attempt was more successful, sitting into the OpenSCAD boat and
starting from there.
The preview algorithm with GoldFeather is especially genius and I like
being able to use it. In the meantime i feel very comfortable with the
openscad code(like it *was* my code, but of course it's not)
I see the clear design criteria of not mixing scad and python language, but
i still did because
* i want pythonscad to be a superset of openscad, users which are
interested in pythonscad can still use their old models, but have the
opportunity to test something new
* also mixing SCAD code with python works quite well in almost any
combination. since few days i am able to instantiate BOSL2 SCAD models
directly from python
* Another exciting fact is that I can instantiate python class member
functions from SCAD. This would be named OpenSCAD++
BTW: I also have per-axis center options. my characters are like <|>
…On Wed, Dec 18, 2024 at 9:51 PM ipsod ***@***.***> wrote:
That's exciting.
Do it from scratch. Don't mingle languages, because the boundaries get
complex. Choose simplicity.
I don't like when I end up having to debug OpenSCAD code - the context
switch is jarring, and I'm perpetually a beginner.
I'd like to write some documentation. I've been using NiceGUI, and I love
their docs - would like to do something like that. With NiceGUI (you can
deploy it directly), or a Django app, or even PHP (weird to mix, but basic
PHP is so easy to deploy and maintain, and you just need to accept a few
variables and pass them through), we could make our own customizer.
I've been working on some code around initial object placement and sizing.
Instead of a center bool, you can use "C" for center, "U" for sitting on
the origin, "D" for below it, and also "LFD" or "DFL" (etc.) for "left
front down". I also sometimes give objects not just measurements, but
extensions, like a normal hole will have extensions up and down 0.01mm (and
"UD" == "Z", in the language, so it's easy to extend both), and these
extensions get in the way when placing the object if we aren't aware. I'd
really like it if this could be extended. I want to be able to say this_box.right
= other_box.left - 3, or this_box.bottom = other_box.top. I think that
this sort of language makes the most common sorts of projects and
operations much faster and more intuitive.
—
Reply to this email directly, view it on GitHub
<#72 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACCO4MRSMUK6NJE4S7BGWXL2GHN5ZAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNRRGAZDIMI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
my center argument is a 3 bytes string for 3 dimensions
I'd say:
code says more than a 1000 words ..
for(int i=0;i<3;i++) {
switch(centerstr[i]) {
case '|': node->center[i] = 0; break;
case '0': node->center[i] = 0; break;
case ' ': node->center[i] = 0; break;
case '_': node->center[i] = 0; break;
case '>': node->center[i] = -1; break;
case ']': node->center[i] = -1; break;
case ')': node->center[i] = -1; break;
case '+': node->center[i] = -1; break;
case '<': node->center[i] = 1; break;
case '[': node->center[i] = 1; break;
case '(': node->center[i] = 1; break;
case '-': node->center[i] = 1; break;
default:
PyErr_SetString(PyExc_TypeError, "Center code chars not
recognized, must be + - or 0");
return NULL;
}
===========
i have seen your automatically created table with constants. I am sure you
know what it means, and i believe its abbreviations to english/american
words meaning relative positions on axis.
However every designer which does not speak english need to learn the codes
like a dictionary
…On Wed, Dec 18, 2024 at 10:42 PM ipsod ***@***.***> wrote:
You know better than me how much work is involved, I'm sure.
I'm not really interested in OpenSCAD, personally - it's just been the
backend for SolidPython for me for a while.
BTW: I also have per-axis center options. my characters are like <|>
What does that look like, in code? Mine looks like: cyl(d=3, h=2,
center=U).
I think this is better than passing strings for center. But, I do clobber
a fair band of constant names to get it so clean.
click to show code
from enum import Enumimport inspect
@dataclassclass XYZ:
x: float = 0
y: float = 0
z: float = 0
def __hash__(self):
return hash((self.x, self.y, self.z))
def __iter__(self):
return iter([self.x, self.y, self.z])
def __getitem__(self, index):
return [self.x, self.y, self.z][index]
def __setitem__(self, index, value):
if index == 0:
self.x = value
elif index == 1:
self.y = value
elif index == 2:
self.z = value
else:
raise IndexError(f"Index {index} out of range for XYZ")
def __list__(self):
return [self.x, self.y, self.z]
def __add__(self, other):
if isinstance(other, XYZ):
return XYZ(self.x + other.x, self.y + other.y, self.z + other.z)
elif isinstance(other, (int, float)):
return XYZ(self.x + other, self.y + other, self.z + other)
elif isinstance(other, (tuple, list)):
return XYZ(self.x + other[0], self.y + other[1], self.z + other[2])
else:
raise TypeError(f"Unsupported operand type(s) for +: '{type(self)}' and '{type(other)}'")
def __mul__(self, other):
if isinstance(other, XYZ):
return XYZ(self.x * other.x, self.y * other.y, self.z * other.z)
elif isinstance(other, (int, float)):
return XYZ(self.x * other, self.y * other, self.z * other)
elif isinstance(other, (tuple, list)):
return XYZ(self.x * other[0], self.y * other[1], self.z * other[2])
else:
raise TypeError(f"Unsupported operand type(s) for *: '{type(self)}' and '{type(other)}'")
def __rmul__(self, other):
if isinstance(other, XYZ):
return XYZ(other.x * self.x, other.y * self.y, other.z * self.z)
elif isinstance(other, (int, float)):
return XYZ(other * self.x, other * self.y, other * self.z)
elif isinstance(other, (tuple, list)):
return XYZ(other[0] * self.x, other[1] * self.y, other[2] * self.z)
else:
raise TypeError(f"Unsupported operand type(s) for *: '{type(self)}' and '{type(other)}'")
def __truediv__(self, other):
if isinstance(other, XYZ):
return XYZ(self.x / other.x, self.y / other.y, self.z / other.z)
elif isinstance(other, (int, float)):
return XYZ(self.x / other, self.y / other, self.z / other)
elif isinstance(other, (tuple, list)):
return XYZ(self.x / other[0], self.y / other[1], self.z / other[2])
else:
raise TypeError(f"Unsupported operand type(s) for /: '{type(self)}' and '{type(other)}'")
def __floordiv__(self, other):
if isinstance(other, XYZ):
return XYZ(self.x // other.x, self.y // other.y, self.z // other.z)
elif isinstance(other, (int, float)):
return XYZ(self.x // other, self.y // other, self.z // other)
elif isinstance(other, (tuple, list)):
return XYZ(self.x // other[0], self.y // other[1], self.z // other[2])
else:
raise TypeError(f"Unsupported operand type(s) for //: '{type(self)}' and '{type(other)}'")
def __neg__(self):
return XYZ(-self.x, -self.y, -self.z)
def __abs__(self):
return XYZ(abs(self.x), abs(self.y), abs(self.z))
class PLACE(Enum):
""" place, as in "place the object here" relation of object's center to the origin - center disregards extensions - default is U, which puts the object's base on the origin - this is how the world usually works we use directions because they let you think in terms of how to push the object (up, down, left, right, etc.). 2d: Up, Down, Left, Right 3d: Up, Down, Left, Right, Forward, Back 2d: y-axis: Forward, Back or Up, Down 3d: y-axis: Forward, Back """
def __new__(cls, xyz: XYZ):
obj = object.__new__(cls)
obj._value_ = xyz
return obj
def as_2d(self) -> tuple[int, int]:
""" Return the value for 2d coordinates (x, y) where y can represent either: - up/down (screen coordinates) - forward/back (top-down view) """
if not self.could_be_2d():
raise ValueError("Not 2D")
return (self.x, self.y if self.y != 0 else self.z)
@classmethod
def parse(cls, text: str) -> tuple[int, int, int]:
"""Convert string like 'UR' to coordinates (1, 0, 1)"""
x = y = z = 0
text = text.upper()
if text == 'C':
return (0, 0, 0)
for char in text:
match char:
case 'L': x = -1
case 'R': x = 1
case 'F': y = -1
case 'B': y = 1
case 'U': z = 1
case 'D': z = -1
case _: raise ValueError(f"Invalid alignment character: {char}")
return (x, y, z)
@classmethod
def from_str(cls, text: str) -> tuple[int, int, int]:
"""Create alignment from string like 'BUL'"""
return cls.parse(text)
def could_be_2d(self) -> bool:
return self.y == 0 or self.z == 0
@classmethod
def generate_direction_codes(cls):
""" Generate and update the direction codes in this file """
# basic directions and their coordinates
base_dirs = {
'C': (0, 0, 0), # center
'L': (-1, 0, 0), # left
'R': (1, 0, 0), # right
'F': (0, 1, 0), # forward
'B': (0, -1, 0), # back
'U': (0, 0, 1), # up
'D': (0, 0, -1), # down
}
# generate all combinations
combinations = set()
# add single directions rst (except C by itself)
for dir, coords in base_dirs.items():
combinations.add((dir, coords))
# add CC and CCC explicitly
combinations.add(("CC", (0, 0, 0)))
combinations.add(("CCC", (0, 0, 0)))
# generate combinations with C
main_dirs = 'LRFBUD'
# single letter with C combinations (LC, CL, LCC, CLC, CCL)
for d in main_dirs:
c = base_dirs[d]
# LC, CL
combinations.add((f"{d}C", c))
combinations.add((f"C{d}", c))
# LCC, CLC, CCL
combinations.add((f"{d}CC", c))
combinations.add((f"C{d}C", c))
combinations.add((f"CC{d}", c))
# generate 2-letter combinations
for d1 in main_dirs:
for d2 in main_dirs:
if d1 != d2:
c1 = base_dirs[d1]
c2 = base_dirs[d2]
combined = (
c1[0] + c2[0],
c1[1] + c2[1],
c1[2] + c2[2],
)
# add both orderings
combinations.add((f"{d1}{d2}", combined))
combinations.add((f"{d2}{d1}", combined))
# add with C
combinations.add((f"{d1}{d2}C", combined))
combinations.add((f"{d1}C{d2}", combined))
combinations.add((f"C{d1}{d2}", combined))
# generate 3-letter combinations
for d1 in main_dirs:
for d2 in main_dirs:
for d3 in main_dirs:
if len({d1, d2, d3}) == 3: # all different
c1 = base_dirs[d1]
c2 = base_dirs[d2]
c3 = base_dirs[d3]
combined = (
c1[0] + c2[0] + c3[0],
c1[1] + c2[1] + c3[1],
c1[2] + c2[2] + c3[2],
)
# add all possible orderings
combinations.add((f"{d1}{d2}{d3}", combined))
combinations.add((f"{d1}{d3}{d2}", combined))
combinations.add((f"{d2}{d1}{d3}", combined))
combinations.add((f"{d2}{d3}{d1}", combined))
combinations.add((f"{d3}{d1}{d2}", combined))
combinations.add((f"{d3}{d2}{d1}", combined))
# Generate the new code
new_code = [" # BEGIN GENERATED CODE", " # generated from above"]
for name, coords in combinations:
new_code.append(f" {name} = XYZ{coords}")
for name, coords in combinations:
new_code.append(f"{name} = {cls.__name__}.{name}")
new_code.append("# END GENERATED CODE")
# Read the current file
current_file = inspect.getfile(cls)
with open(current_file, 'r') as f:
lines = f.readlines()
# Find the generated code section
start_idx = -1
end_idx = -1
for i, line in enumerate(lines):
if line.strip() == "# BEGIN GENERATED CODE":
start_idx = i
elif line.strip() == "# END GENERATED CODE":
end_idx = i
break
if start_idx != -1 and end_idx != -1:
# Replace the section
new_lines = lines[:start_idx] + [line + '\n' for line in new_code] + lines[end_idx+1:]
# Write back to starting position in file
with open(current_file, 'r+') as f:
f.seek(sum(len(line) for line in lines[:start_idx]))
f.writelines(new_lines[start_idx:])
f.truncate()
else:
print("Could not find generated code section")
# Just print the codes as before
for line in new_code:
print(line)
# BEGIN GENERATED CODE
# generated from above
DC = XYZ(0, 0, -1)
FRL = XYZ(0, 1, 0)
DL = XYZ(-1, 0, -1)
FRD = XYZ(1, 1, -1)
UDL = XYZ(-1, 0, 0)
BC = XYZ(0, -1, 0)
CFB = XYZ(0, 0, 0)
DBL = XYZ(-1, -1, -1)
LDU = XYZ(-1, 0, 0)
FBU = XYZ(0, 0, 1)
FDL = XYZ(-1, 1, -1)
BFU = XYZ(0, 0, 1)
FBC = XYZ(0, 0, 0)
CRF = XYZ(1, 1, 0)
RU = XYZ(1, 0, 1)
DFU = XYZ(0, 1, 0)
CBU = XYZ(0, -1, 1)
LFR = XYZ(0, 1, 0)
LUR = XYZ(0, 0, 1)
CLU = XYZ(-1, 0, 1)
FLD = XYZ(-1, 1, -1)
DRB = XYZ(1, -1, -1)
CLR = XYZ(0, 0, 0)
BCL = XYZ(-1, -1, 0)
DFC = XYZ(0, 1, -1)
BUC = XYZ(0, -1, 1)
UDR = XYZ(1, 0, 0)
LBC = XYZ(-1, -1, 0)
LUC = XYZ(-1, 0, 1)
BR = XYZ(1, -1, 0)
FU = XYZ(0, 1, 1)
BFC = XYZ(0, 0, 0)
DRC = XYZ(1, 0, -1)
DCF = XYZ(0, 1, -1)
FBR = XYZ(1, 0, 0)
RFU = XYZ(1, 1, 1)
RLD = XYZ(0, 0, -1)
BUR = XYZ(1, -1, 1)
CFC = XYZ(0, 1, 0)
UBC = XYZ(0, -1, 1)
BLU = XYZ(-1, -1, 1)
DR = XYZ(1, 0, -1)
DRU = XYZ(1, 0, 0)
BCF = XYZ(0, 0, 0)
BCU = XYZ(0, -1, 1)
DCB = XYZ(0, -1, -1)
BDF = XYZ(0, 0, -1)
DLR = XYZ(0, 0, -1)
LDB = XYZ(-1, -1, -1)
BFD = XYZ(0, 0, -1)
DBU = XYZ(0, -1, 0)
DUB = XYZ(0, -1, 0)
UBR = XYZ(1, -1, 1)
CUF = XYZ(0, 1, 1)
CL = XYZ(-1, 0, 0)
CU = XYZ(0, 0, 1)
LDR = XYZ(0, 0, -1)
FBL = XYZ(-1, 0, 0)
FRB = XYZ(1, 0, 0)
FBD = XYZ(0, 0, -1)
FR = XYZ(1, 1, 0)
CDU = XYZ(0, 0, 0)
BUL = XYZ(-1, -1, 1)
RDB = XYZ(1, -1, -1)
LCR = XYZ(0, 0, 0)
BRD = XYZ(1, -1, -1)
UFR = XYZ(1, 1, 1)
RUL = XYZ(0, 0, 1)
UD = XYZ(0, 0, 0)
LB = XYZ(-1, -1, 0)
UF = XYZ(0, 1, 1)
CC = XYZ(0, 0, 0)
ULF = XYZ(-1, 1, 1)
CBF = XYZ(0, 0, 0)
RCF = XYZ(1, 1, 0)
BLC = XYZ(-1, -1, 0)
BU = XYZ(0, -1, 1)
LR = XYZ(0, 0, 0)
UB = XYZ(0, -1, 1)
RCC = XYZ(1, 0, 0)
DCC = XYZ(0, 0, -1)
BCD = XYZ(0, -1, -1)
DU = XYZ(0, 0, 0)
ULB = XYZ(-1, -1, 1)
FRU = XYZ(1, 1, 1)
DFB = XYZ(0, 0, -1)
DCL = XYZ(-1, 0, -1)
LUB = XYZ(-1, -1, 1)
CDF = XYZ(0, 1, -1)
RUB = XYZ(1, -1, 1)
RLU = XYZ(0, 0, 1)
RBD = XYZ(1, -1, -1)
RDU = XYZ(1, 0, 0)
CUR = XYZ(1, 0, 1)
CDL = XYZ(-1, 0, -1)
DLB = XYZ(-1, -1, -1)
UR = XYZ(1, 0, 1)
DUC = XYZ(0, 0, 0)
B = XYZ(0, -1, 0)
UDB = XYZ(0, -1, 0)
DBR = XYZ(1, -1, -1)
RF = XYZ(1, 1, 0)
RUC = XYZ(1, 0, 1)
CLC = XYZ(-1, 0, 0)
LF = XYZ(-1, 1, 0)
BDR = XYZ(1, -1, -1)
DBF = XYZ(0, 0, -1)
BL = XYZ(-1, -1, 0)
FCC = XYZ(0, 1, 0)
BCC = XYZ(0, -1, 0)
LFB = XYZ(-1, 0, 0)
FC = XYZ(0, 1, 0)
CBR = XYZ(1, -1, 0)
BRL = XYZ(0, -1, 0)
DFL = XYZ(-1, 1, -1)
ULD = XYZ(-1, 0, 0)
CLB = XYZ(-1, -1, 0)
CDR = XYZ(1, 0, -1)
CRC = XYZ(1, 0, 0)
CCU = XYZ(0, 0, 1)
C = XYZ(0, 0, 0)
CDC = XYZ(0, 0, -1)
LCF = XYZ(-1, 1, 0)
RC = XYZ(1, 0, 0)
FCU = XYZ(0, 1, 1)
URL = XYZ(0, 0, 1)
BFL = XYZ(-1, 0, 0)
R = XYZ(1, 0, 0)
RUD = XYZ(1, 0, 0)
RFC = XYZ(1, 1, 0)
FLU = XYZ(-1, 1, 1)
BLD = XYZ(-1, -1, -1)
LCD = XYZ(-1, 0, -1)
CBD = XYZ(0, -1, -1)
RCU = XYZ(1, 0, 1)
LDC = XYZ(-1, 0, -1)
RCD = XYZ(1, 0, -1)
CBL = XYZ(-1, -1, 0)
LBR = XYZ(0, -1, 0)
ULC = XYZ(-1, 0, 1)
LBF = XYZ(-1, 0, 0)
UBF = XYZ(0, 0, 1)
LFC = XYZ(-1, 1, 0)
LCB = XYZ(-1, -1, 0)
FUR = XYZ(1, 1, 1)
FLC = XYZ(-1, 1, 0)
BLR = XYZ(0, -1, 0)
RFD = XYZ(1, 1, -1)
RBU = XYZ(1, -1, 1)
BRU = XYZ(1, -1, 1)
URC = XYZ(1, 0, 1)
RCL = XYZ(0, 0, 0)
FCD = XYZ(0, 1, -1)
L = XYZ(-1, 0, 0)
CCR = XYZ(1, 0, 0)
RBC = XYZ(1, -1, 0)
DFR = XYZ(1, 1, -1)
UCR = XYZ(1, 0, 1)
RLC = XYZ(0, 0, 0)
CFL = XYZ(-1, 1, 0)
FUD = XYZ(0, 1, 0)
LRU = XYZ(0, 0, 1)
BRC = XYZ(1, -1, 0)
RBF = XYZ(1, 0, 0)
FCR = XYZ(1, 1, 0)
UDC = XYZ(0, 0, 0)
URD = XYZ(1, 0, 0)
LRC = XYZ(0, 0, 0)
CCB = XYZ(0, -1, 0)
UCD = XYZ(0, 0, 0)
ULR = XYZ(0, 0, 1)
DLF = XYZ(-1, 1, -1)
CCD = XYZ(0, 0, -1)
LCU = XYZ(-1, 0, 1)
UBD = XYZ(0, -1, 0)
DUF = XYZ(0, 1, 0)
FB = XYZ(0, 0, 0)
FUC = XYZ(0, 1, 1)
CDB = XYZ(0, -1, -1)
UCF = XYZ(0, 1, 1)
RFB = XYZ(1, 0, 0)
CFR = XYZ(1, 1, 0)
DLC = XYZ(-1, 0, -1)
FLB = XYZ(-1, 0, 0)
CRL = XYZ(0, 0, 0)
BCR = XYZ(1, -1, 0)
LFU = XYZ(-1, 1, 1)
RUF = XYZ(1, 1, 1)
LRB = XYZ(0, -1, 0)
UCL = XYZ(-1, 0, 1)
FUL = XYZ(-1, 1, 1)
F = XYZ(0, 1, 0)
CLD = XYZ(-1, 0, -1)
URB = XYZ(1, -1, 1)
DCU = XYZ(0, 0, 0)
DRL = XYZ(0, 0, -1)
CFU = XYZ(0, 1, 1)
LBD = XYZ(-1, -1, -1)
LUD = XYZ(-1, 0, 0)
LRF = XYZ(0, 1, 0)
UDF = XYZ(0, 1, 0)
LUF = XYZ(-1, 1, 1)
CF = XYZ(0, 1, 0)
CFD = XYZ(0, 1, -1)
FLR = XYZ(0, 1, 0)
RFL = XYZ(0, 1, 0)
RD = XYZ(1, 0, -1)
CUC = XYZ(0, 0, 1)
DBC = XYZ(0, -1, -1)
RLB = XYZ(0, -1, 0)
URF = XYZ(1, 1, 1)
RCB = XYZ(1, -1, 0)
CUB = XYZ(0, -1, 1)
LBU = XYZ(-1, -1, 1)
BDU = XYZ(0, -1, 0)
DB = XYZ(0, -1, -1)
UCB = XYZ(0, -1, 1)
LDF = XYZ(-1, 1, -1)
BFR = XYZ(1, 0, 0)
LRD = XYZ(0, 0, -1)
UC = XYZ(0, 0, 1)
FCB = XYZ(0, 0, 0)
LFD = XYZ(-1, 1, -1)
FDB = XYZ(0, 0, -1)
BLF = XYZ(-1, 0, 0)
LC = XYZ(-1, 0, 0)
UCC = XYZ(0, 0, 1)
CD = XYZ(0, 0, -1)
BDL = XYZ(-1, -1, -1)
D = XYZ(0, 0, -1)
RB = XYZ(1, -1, 0)
DUL = XYZ(-1, 0, 0)
DCR = XYZ(1, 0, -1)
DUR = XYZ(1, 0, 0)
CRD = XYZ(1, 0, -1)
UFC = XYZ(0, 1, 1)
UFD = XYZ(0, 1, 0)
LCC = XYZ(-1, 0, 0)
CUD = XYZ(0, 0, 0)
FCL = XYZ(-1, 1, 0)
BF = XYZ(0, 0, 0)
FD = XYZ(0, 1, -1)
CBC = XYZ(0, -1, 0)
FDC = XYZ(0, 1, -1)
CRB = XYZ(1, -1, 0)
RDL = XYZ(0, 0, -1)
LD = XYZ(-1, 0, -1)
UFL = XYZ(-1, 1, 1)
UBL = XYZ(-1, -1, 1)
RDF = XYZ(1, 1, -1)
BRF = XYZ(1, 0, 0)
U = XYZ(0, 0, 1)
DRF = XYZ(1, 1, -1)
CUL = XYZ(-1, 0, 1)
CCL = XYZ(-1, 0, 0)
UL = XYZ(-1, 0, 1)
CB = XYZ(0, -1, 0)
CLF = XYZ(-1, 1, 0)
DLU = XYZ(-1, 0, 0)
FL = XYZ(-1, 1, 0)
CCF = XYZ(0, 1, 0)
CRU = XYZ(1, 0, 1)
RBL = XYZ(0, -1, 0)
BD = XYZ(0, -1, -1)
CR = XYZ(1, 0, 0)
RDC = XYZ(1, 0, -1)
FUB = XYZ(0, 0, 1)
RL = XYZ(0, 0, 0)
LU = XYZ(-1, 0, 1)
FDR = XYZ(1, 1, -1)
UFB = XYZ(0, 0, 1)
FDU = XYZ(0, 1, 0)
FRC = XYZ(1, 1, 0)
BDC = XYZ(0, -1, -1)
RLF = XYZ(0, 1, 0)
BUD = XYZ(0, -1, 0)
DF = XYZ(0, 1, -1)
BUF = XYZ(0, 0, 1)
CCC = XYZ(0, 0, 0)DC = PLACE.DCFRL = PLACE.FRLDL = PLACE.DLFRD = PLACE.FRDUDL = PLACE.UDLBC = PLACE.BCCFB = PLACE.CFBDBL = PLACE.DBLLDU = PLACE.LDUFBU = PLACE.FBUFDL = PLACE.FDLBFU = PLACE.BFUFBC = PLACE.FBCCRF = PLACE.CRFRU = PLACE.RUDFU = PLACE.DFUCBU = PLACE.CBULFR = PLACE.LFRLUR = PLACE.LURCLU = PLACE.CLUFLD = PLACE.FLDDRB = PLACE.DRBCLR = PLACE.CLRBCL = PLACE.BCLDFC = PLACE.DFCBUC = PLACE.BUCUDR = PLACE.UDRLBC = PLACE.LBCLUC = PLACE.LUCBR = PLACE.BRFU = PLACE.FUBFC = PLACE.BFCDRC = PLACE.DRCDCF = PLACE.DCFFBR = PLACE.FBRRFU = PLACE.RFURLD = PLACE.RLDBUR = PLACE.BURCFC = PLACE.CFCUBC = PLACE.UBCBLU = PLACE.BLUDR = PLACE.DRDRU = PLACE.DRUBCF = PLACE.BCFBCU = PLACE.BCUDCB = PLACE.DCBBDF = PLACE.BDFDLR = PLACE.DLRLDB = PLACE.LDBBFD = PLACE.BFDDBU = PLACE.DBUDUB = PLACE.DUBUBR = PLACE.UBRCUF = PLACE.CUFCL = PLACE.CLCU = PLACE.CULDR = PLACE.LDRFBL = PLACE.FBLFRB = PLACE.FRBFBD = PLACE.FBDFR = PLACE.FRCDU = PLACE.CDUBUL = PLACE.BULRDB = PLACE.RDBLCR = PLACE.LCRBRD = PLACE.BRDUFR = PLACE.UFRRUL = PLACE.RULUD = PLACE.UDLB = PLACE.LBUF = PLACE.UFCC = PLACE.CCULF = PLACE.ULFCBF = PLACE.CBFRCF = PLACE.RCFBLC = PLACE.BLCBU = PLACE.BULR = PLACE.LRUB = PLACE.UBRCC = PLACE.RCCDCC = PLACE.DCCBCD = PLACE.BCDDU = PLACE.DUULB = PLACE.ULBFRU = PLACE.FRUDFB = PLACE.DFBDCL = PLACE.DCLLUB = PLACE.LUBCDF = PLACE.CDFRUB = PLACE.RUBRLU = PLACE.RLURBD = PLACE.RBDRDU = PLACE.RDUCUR = PLACE.CURCDL = PLACE.CDLDLB = PLACE.DLBUR = PLACE.URDUC = PLACE.DUCB = PLACE.BUDB = PLACE.UDBDBR = PLACE.DBRRF = PLACE.RFRUC = PLACE.RUCCLC = PLACE.CLCLF = PLACE.LFBDR = PLACE.BDRDBF = PLACE.DBFBL = PLACE.BLFCC = PLACE.FCCBCC = PLACE.BCCLFB = PLACE.LFBFC = PLACE.FCCBR = PLACE.CBRBRL = PLACE.BRLDFL = PLACE.DFLULD = PLACE.ULDCLB = PLACE.CLBCDR = PLACE.CDRCRC = PLACE.CRCCCU = PLACE.CCUC = PLACE.CCDC = PLACE.CDCLCF = PLACE.LCFRC = PLACE.RCFCU = PLACE.FCUURL = PLACE.URLBFL = PLACE.BFLR = PLACE.RRUD = PLACE.RUDRFC = PLACE.RFCFLU = PLACE.FLUBLD = PLACE.BLDLCD = PLACE.LCDCBD = PLACE.CBDRCU = PLACE.RCULDC = PLACE.LDCRCD = PLACE.RCDCBL = PLACE.CBLLBR = PLACE.LBRULC = PLACE.ULCLBF = PLACE.LBFUBF = PLACE.UBFLFC = PLACE.LFCLCB = PLACE.LCBFUR = PLACE.FURFLC = PLACE.FLCBLR = PLACE.BLRRFD = PLACE.RFDRBU = PLACE.RBUBRU = PLACE.BRUURC = PLACE.URCRCL = PLACE.RCLFCD = PLACE.FCDL = PLACE.LCCR = PLACE.CCRRBC = PLACE.RBCDFR = PLACE.DFRUCR = PLACE.UCRRLC = PLACE.RLCCFL = PLACE.CFLFUD = PLACE.FUDLRU = PLACE.LRUBRC = PLACE.BRCRBF = PLACE.RBFFCR = PLACE.FCRUDC = PLACE.UDCURD = PLACE.URDLRC = PLACE.LRCCCB = PLACE.CCBUCD = PLACE.UCDULR = PLACE.ULRDLF = PLACE.DLFCCD = PLACE.CCDLCU = PLACE.LCUUBD = PLACE.UBDDUF = PLACE.DUFFB = PLACE.FBFUC = PLACE.FUCCDB = PLACE.CDBUCF = PLACE.UCFRFB = PLACE.RFBCFR = PLACE.CFRDLC = PLACE.DLCFLB = PLACE.FLBCRL = PLACE.CRLBCR = PLACE.BCRLFU = PLACE.LFURUF = PLACE.RUFLRB = PLACE.LRBUCL = PLACE.UCLFUL = PLACE.FULF = PLACE.FCLD = PLACE.CLDURB = PLACE.URBDCU = PLACE.DCUDRL = PLACE.DRLCFU = PLACE.CFULBD = PLACE.LBDLUD = PLACE.LUDLRF = PLACE.LRFUDF = PLACE.UDFLUF = PLACE.LUFCF = PLACE.CFCFD = PLACE.CFDFLR = PLACE.FLRRFL = PLACE.RFLRD = PLACE.RDCUC = PLACE.CUCDBC = PLACE.DBCRLB = PLACE.RLBURF = PLACE.URFRCB = PLACE.RCBCUB = PLACE.CUBLBU = PLACE.LBUBDU = PLACE.BDUDB = PLACE.DBUCB = PLACE.UCBLDF = PLACE.LDFBFR = PLACE.BFRLRD = PLACE.LRDUC = PLACE.UCFCB = PLACE.FCBLFD = PLACE.LFDFDB = PLACE.FDBBLF = PLACE.BLFLC = PLACE.LCUCC = PLACE.UCCCD = PLACE.CDBDL = PLACE.BDLD = PLACE.DRB = PLACE.RBDUL = PLACE.DULDCR = PLACE.DCRDUR = PLACE.DURCRD = PLACE.CRDUFC = PLACE.UFCUFD = PLACE.UFDLCC = PLACE.LCCCUD = PLACE.CUDFCL = PLACE.FCLBF = PLACE.BFFD = PLACE.FDCBC = PLACE.CBCFDC = PLACE.FDCCRB = PLACE.CRBRDL = PLACE.RDLLD = PLACE.LDUFL = PLACE.UFLUBL = PLACE.UBLRDF = PLACE.RDFBRF = PLACE.BRFU = PLACE.UDRF = PLACE.DRFCUL = PLACE.CULCCL = PLACE.CCLUL = PLACE.ULCB = PLACE.CBCLF = PLACE.CLFDLU = PLACE.DLUFL = PLACE.FLCCF = PLACE.CCFCRU = PLACE.CRURBL = PLACE.RBLBD = PLACE.BDCR = PLACE.CRRDC = PLACE.RDCFUB = PLACE.FUBRL = PLACE.RLLU = PLACE.LUFDR = PLACE.FDRUFB = PLACE.UFBFDU = PLACE.FDUFRC = PLACE.FRCBDC = PLACE.BDCRLF = PLACE.RLFBUD = PLACE.BUDDF = PLACE.DFBUF = PLACE.BUFCCC = PLACE.CCC# END GENERATED CODE
if __name__ == "__main__":
PLACE.generate_direction_codes()
—
Reply to this email directly, view it on GitHub
<#72 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACCO4MTRUWDVZFJSNQX3FHD2GHT4HAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNRRGA3DIMY>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
you say:
I want to be able to say this_box.right = other_box.left - 3, or
this_box.bottom
= other_box.top.
in PythonSCAD the way you say that is:
other_box |= this_box.align(other_box.left, this_box.right)
The functions get all the data which they need to, but fo course its not as
pretty
maybe Python decorators can help here ?
…On Fri, Dec 20, 2024 at 9:00 PM jeff ***@***.***> wrote:
I want to be able to say this_box.right = other_box.left - 3, or this_box.bottom
= other_box.top.
>>> from solidng import *>>> from solidng.utils import *>>>>>> tri = triarrows()>>>>>> s = sphere(1, segments=32)\
... .grabAt("bb_bottom")\
... .moveTo(tri, "arrow0")>>>>>> cubo = cuboctahedron()\
... .grabAt("ray_intersection", (0, 0, 0), (0, 0, -1))\
... .moveTo(tri, "arrow1")>>>>>> (tri + s + cubo).show()
triarrows.png (view on web)
<https://github.com/user-attachments/assets/ac22d970-a1c3-45b4-9705-8cfcdc9217c1>
#solidng.utils.py
def cuboctahedron(r=1.0, fn=32):
[...]
def arrow(l=10, r1=1, r2=2, ratio=0.7):
head = zcyl(l * (1-ratio), r2, 0).color(1, 0, 0)
body = zcyl(l * ratio , r1).color(0, 0, 1)
return body + head.grabAt("bb_bottom").moveTo(body, "bb_top").up(1)
def triarrows():
arrows = []
anchors = {}
for i in range(3):
arrows += [arrow().right(i * 5)]
anchors[f"arrow{i}"] = arrows[len(arrows)-1].bounding_box.top
res = sum(arrows[1:], arrows[0])
res._anchors.update(anchors)
return res
my_object.left, my_object.right and my_object.back are unfortunately
already in use by convenience functions for translation.....
—
Reply to this email directly, view it on GitHub
<#72 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACCO4MRIN6DZUCDN5KHAUVT2GRZNBAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNRTGI2DGNY>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
the naming conflict is not issue either, you could choose another name like:
mybox.left_set( other_box.right)
mathematically it is
mybox * left_matrix = other_box * right_matrix
IF mybox * left_matrix would be a REFERENCE to mybox it would work out of
the box, unfortunately it's not, so you have to "solve" the equation like
mybox shall transform by right_matrix / left_matrix(simple equation
solving)
…On Fri, Dec 20, 2024 at 10:18 PM jeff ***@***.***> wrote:
?
I guess that's not the issue.
my_box.left(x) is mapping for my_box.translate([-x, 0, 0]).
atm I would use my_box.anchors.left.
—
Reply to this email directly, view it on GitHub
<#72 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACCO4MUOSRFKFF4WZFPZ5AL2GSCTZAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNRTGM3TCMA>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
and:
my_box.left = orther_box.right
would just set my_box to a different position in space. by no means it
combines them together(and you usually like to do that)
On Fri, Dec 20, 2024 at 10:27 PM Guenther Sohler ***@***.***>
wrote:
… the naming conflict is not issue either, you could choose another name
like:
mybox.left_set( other_box.right)
mathematically it is
mybox * left_matrix = other_box * right_matrix
IF mybox * left_matrix would be a REFERENCE to mybox it would work out of
the box, unfortunately it's not, so you have to "solve" the equation like
mybox shall transform by right_matrix / left_matrix(simple equation
solving)
On Fri, Dec 20, 2024 at 10:18 PM jeff ***@***.***> wrote:
> ?
>
> I guess that's not the issue.
>
> my_box.left(x) is mapping for my_box.translate([-x, 0, 0]).
>
> atm I would use my_box.anchors.left.
>
> —
> Reply to this email directly, view it on GitHub
> <#72 (reply in thread)>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ACCO4MUOSRFKFF4WZFPZ5AL2GSCTZAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNRTGM3TCMA>
> .
> You are receiving this because you were mentioned.Message ID:
> ***@***.***>
>
|
Beta Was this translation helpful? Give feedback.
-
ipsod, you write:
# Case 1: Method call
box.left(5) # Calls __call__, translates -5 units
# Case 2: Assignment
box.left = 10 # Calls __set__, moves left side to absolute position 10
The multi-purpose usage of left attribute is sophisticated.
but after you assigned left with
box.left = 10
you cannot move it with
box.left(5) because left is not a function anymore
(yes, you might want to do that in complicated models and if the two
statement sit in different locations)
…On Fri, Dec 20, 2024 at 10:41 PM jeff ***@***.***> wrote:
One disadvantage from my perspective for the assignment syntax is that the
semantic of multiple assignments in a row is not intuitive (you can attach
multiple objects at the same anchor). Same for overwriting __setattr__
for a function, the result could be confusing.
I'll think about it.
—
Reply to this email directly, view it on GitHub
<#72 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACCO4MXG3KZOKH52EU7WFND2GSFGZAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNRTGM4DINI>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
And your attribute left/right indicates that you are solely focused on
the x coordinate,
but even when you attach left of other_box to right of this_box , this
does not define if there is an additional rotation in YZ plane.
Maybe consider using a 4x4 matrix instead of a x coordinate as "attachment
point" ;)
…On Fri, Dec 20, 2024 at 10:46 PM jeff ***@***.***> wrote:
I think it would be best to focus on a single group of direction words.
Like, instead of "front", we say "forward". So that you either say "move
the object forward" or "from the object's forward face".
I would like to attach or align the top or bottom of something to the
front of something else. Maybe I'll move it up, down or forward afterwards
;)
—
Reply to this email directly, view it on GitHub
<#72 (reply in thread)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACCO4MV5FZSBIKZ3JFHQFVL2GSF2NAVCNFSM6AAAAABT3LVVKOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTCNRTGM4DONQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
Is this moving along? Also, have you seen "iommi" for django? Some of the paradigms used would translate well over here. Like, check out the way pages are constructed: https://docs.iommi.rocks/en/latest/pages.html And, particularly, his way of customizing deeply-nested subclasses would be nice: https://docs.iommi.rocks/en/latest/philosophy.html#everything-has-a-name Lots of his choices are laid out here: https://kodare.net/2024/09/30/why-iommi-is-weird.html |
Beta Was this translation helpful? Give feedback.
-
Unfortunately only very very little and slow. Too much other stuff going on..... I did some more experiments with "generic 6dof anchors" which worked but had an issue so I would have to revisit it. Furthermore I completely switched to trimesh (and let it use manifold in the background). But as mentioned unfortunately no more progress yet. I had a brief look at iommi stuff. What in particual would you like to do? I don't really see the point of |
Beta Was this translation helpful? Give feedback.
-
I would do something like this: def MySlider(param1=7, width=10):
....
def FullyPrintedLinearActuator(someParam=1, whatver=2, slider=None):
slider = slider or MySlider()
....
FullyPrintedLInearActuator(slider=MySlider(width=15)).save_as_stl() I don't see any boilderplate code (except maybe `slider = slider or MySlider(), but that's more a python flaw on evaluating default parameters....) |
Beta Was this translation helpful? Give feedback.
-
@ipsod @gsohler
After our discussion in #71 & gsohler/openscad#51 I had a closer look at "it" (as indented by ipsod ;) and had a look at libmanifold directly.
To my suprise libmanifold is actually exactly the alternative backend for SolidPython that I was looking for for quite some time. And furthermore: it's api is very close to the openscad api and it has a python interface.
This makes a library like SolidPython almost(!) redundant.
With manifold3d and trimesh you can do this:
That's all the base features we're looking for out of the box in less than 10 lines, right?
As I said earlier this make a library like SolidPython from my point of view almost redundant. But only almost, because:
What I think SolidPython is about is that SolidPython provides and intuitive and easy to use api to model 3d objects in python without any overhead. Even though libmanifold and trimesh together provide all the base features we need I don't think that's the api I want to use when "scading" stuff.
With a SolidPython like library the lines above become:
And that's why I think we still want something like SolidPython but instead of using openscad we could use libmanifold (and trimesh) as backend. I drafted a very basic
solidng
library. With that mini wrapper library (~90 lines) ontop of libmanifold and trimesh, we can actually run SolidPython code, create stls and even show a window displaying the model.This is our cuboctahedron exercise with
solidng
:outputs:
The car example works with
solidng
without any modifications (exceptimport
andsave
).So the big question for me now is: What do we actually want?
Do we want / need any openscad "support"? If so, what do we need / want?
Do we want to generate openscad code (to release it on thingiverse) or do we only care about creating stl files?
Do we want to stick to bosl2 or do we maybe implement our own standard library in python?
You get the point? What do we actually want to do?
So I'd like to hear your thoughts and ideas about it!
Beta Was this translation helpful? Give feedback.
All reactions