From bce3d3cbc02003ff4da18fb2f2e7ef40244f7954 Mon Sep 17 00:00:00 2001 From: Elod Csirmaz Date: Fri, 15 Mar 2024 12:00:13 +0000 Subject: [PATCH] Documentation --- README.md | 30 ++++++++++++++++++++++++------ images/pathtube.png | Bin 0 -> 5056 bytes openscad_py.py | 13 ++++++++----- 3 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 images/pathtube.png diff --git a/README.md b/README.md index 4b1b546..d39f16a 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ It also contains convenience functions to define a wider range of primitives, as ## Example ``` -Cube([1, 1, 2]).move([0, 0, 1]) +Cube([1, 1, 2]).move([0, 0, 1]).render() ``` -becomes +returns ``` @@ -25,11 +25,29 @@ translate(v=[0, 0, 1]) { cube(size=[1, 1, 2], center=false); } ## Notable convenience functions -- Usual computational geometry functions on the `Point` class that work in an arbitrary number of dimensions. Overloads for algebraic operators +Usual computational geometry functions on the `Point` class that work in an arbitrary number of dimensions. Overloads for algebraic operators. -- `Cylinder.from_ends` constructs a cylinder between two given points in space +``` +distance = (Point((0, 0, 1)) - Point((1, 0, 1))).length() +angle_between = Point((0, 0, 1) * 2).angle(Point((1, 0, 1))) +``` -- `Polyhedron.tube` creates a tube-like or toroid polyhedron from a 2D array of points +`Cylinder.from_ends()` constructs a cylinder between two given points in space -- `PathTube` creates a tube-like or toroid polyhedron from an arbitrary path +``` +openscad_code = Cylinder.from_ends(radius=2, p1=(0, 0, 1), p2=(1, 0, 2)).render() +``` +`Polyhedron.tube()` creates a tube-like or toroid polyhedron from a 2D array of points + +`PathTube` creates a tube-like or toroid polyhedron from an arbitrary path + +``` +PathTube( + points=[(0,0,0), (0,0,1), (1,0,2), (1,1,0), (0,.5,0)], + radius=.2, + fn=4 +) +``` + +![PathTube example](https://raw.github.com/csirmaz/openscad-py/master/images/pathtube.png) diff --git a/images/pathtube.png b/images/pathtube.png new file mode 100644 index 0000000000000000000000000000000000000000..f1995bf682fa99bda3ff697c4747b3df045044c2 GIT binary patch literal 5056 zcmX|Fc|26_7Zwv^8zZ|JWZ(BOrm@5f#+InD4Ju1zn`x%dA~CWvva9UM5?_iegJLXA zX_37w!$f4S2$AJC^~djz`?>Eu_c`ai=XuXL_q=bSqXUMYN0Nt)jg8;N+Tsiw+mT|{ zbppuFlDM1r#9F{DN0z_JS*q7p~@o|w%p=sm17I~S1KSuE)k4Wz$9@?9bG6B z4}WRcW@Myqw2?Rc{`SeM-rvL9ji!7%4nFjSU$5BMd22Sh7!r{3^W=KPP}w1fEzD$2 z+U(}Hjgg((7xlS{q3o{w6b@T?qQL;7ScTnHg2M4umbl4!0JB#_LO&ucwsxc;j()`BKJ(Uv4su4gu(&i!m%;)4KODivkR!6SS~A%}lKRf@sU)PdLZJV$O5gr$iUIoCS*HF7Zm%fH;R39kn@diV$y#vcn38#uuecS z+B7`C!>b4f11UbUumpt%a8zB$G=FjlE)hL6FEOpO0za;uQ)!(Y((>V6tlGLyV$zMD zUc3_1Q7iYEVAXblllV-Upb#{=teo{uMh`yN-`xr?rx~v2l;?~kP|MmvC8QC{lSg?5mGs-)`ADBbkzTc2eG2&(-l(np zeCpNQeDGV__r1W$gR8T16^x0b64K|!mR_Ri`Bw$Ll_<=((ssvzO+6{9==(*vCeqqK zY}%jWCyzcQTw78~$xXTNY5tf2&+~I(?ePs^pWa)I-}(Jh><^?@HZ@kw7@k|{esdtoRy#$`0zIYi<#^Lhi8#dYmIBeq z8#i_0aSCwo(zu2)F^*nRg|WjPIyU)z4?Y~#3$u1@BlHGv2;YKrzTzZg?1_C@lEc5_ z?$hZhDa3sDyU$tYHmrGfYC4m6xLh=ejsmvJcse(>UAkI<4~^F6BUSb=EDiocUJS{F z2dV!xAD8{9b8JqHqFx!suzidd(@wtoY-n-a4?g-BR+}(2H2>hQe-`i0*KZBGylP5) zfGk=P35e{!q}wsk}KYr;ouUIr7fUY5~bzD3Ji;>H%lUNVdDCGJ5Ow#2-H$h`U4c(n*hRC+NQ?aaH0dwLY08l5+w2Czf(jq?|v5 z)LOZUJYZ54wtL2Z!*S!{GxgO5$dTO_y-9$+eroR~jgr&EO;I;r(FpSQyOxD39mmJd zOQ)7*eX4OBPrXpGq)f~)Fswgedy07D>z7mL@f2M?(yJ_g&o=qIyR_`h(eIJV(r2v7 z38EBr1>aem%owyRr!qC=w3$a?_*R$rM$zHH%2aWb-?(FzSM~TbwfU&gfuL|k9+MtC z$#tbe@9wGOD6|!ZKIbrEF*WuQG zy82m9iQY(UOaRm=Qm%X(cUXwasddptFAhYSy^#1Jw%-p{S z*sPih?@?3C!69`K_ z7_DkmLhvQa5}y2e(=t0a$U3gV{#id6@|$hGky{?UolNB*J!VblFpyA zq8#lz%qkXODali+_3AapKDOUNZKlJk>u7ir#f6jux}u|a>JInC4rBcJFXk+GKVkuF z+M_BEZ&Cu|%T{a@I2@`lv^)HSGb5a9-Og4dDdd(Trdj7H#^K{@%lJtdVb>v! zb{k~hFo-#BRv{iCS#h?ToL75GxLv12w zi%;p3lNomfI+W9R(fRNy4#T1@;3&d)sQExPmHwm@M)ZIH1B{jVgxAk|vshgyff1dm z2fPl1IC%{AvvhkFS%NWxh_000jjTnP8UOraj{to{k*DUT3_+57!ia`9aDw`^hq^rh zI{r+Yv3J}>?KfIQdi!W7rJB%Bt0~m!*O)!4G=>;9@MqeO3UTV?oOWlCkSJSQOX9fv zIn3X&mTm(g!D>r+x7ol8oq4D49PUjQ?X~MnAPz{Vgc4=yiJWTEeCYGYh7JpRkLoQk zG_(m!#pDFZJO-;We%X)ly9{-qHXB{fUlk}Z&Z#{8Y>QiD@adwdyX#XpUegp~c<#=) zNzD3vW^KUF_vxZ%np>dyj0VX9t0qO(ck(*lWc5=S4XT0$_5X@tHgo@Jq|b{b5h17# z6w*`p8M2JBY7*7$;g%=nt#HpvOO1uy8`QKl{EkD7yH)z6((v`l;uN8EYDj=r-q186 zL6&F)Dh%FN(QU86y${*2 zqo%+>&&$=Hyfm;AT>!)U%5=PIo4(h-s98g>8}dvvs^Zr^#5cPA^A``a8$K8t@xL&< zHo3oE;L&mI9$nmf^DU}T#GYuPj;vn|^eMN4%qZygNSQN{*jomYb~hIXC10Z&xiy@^ zH|RwNHL`)#R&Fm*B1+V$%9@Eu*pRBkviQs`OQz$4jKRe#&|_c(m1TX9IP8;P zXU1$@-)$>ui9^_5Y+>N1JpP$6(MQWEF~dw_~UobkR2I=0T*N}MV^S}a@g1@S`Jzc)?kz>gpnyiy!jIC zq`RCBhXF-SKM+aH*-8_p-b4E!38Y7ZHK7~Wvmza~R=?6+pq(=`e@NR}KJhJ7x1 z+1CIELq`}}yzb4awVkcZ8}VlDI>LxKvc*A&qonGrXTRCy&fc79mna}k# zuj+Qzrh@CD3*@>EaTxEMPLpuB-~Cm_?BFNx`iqd85DcR!i^o&+C9By?6UcX7-heM% zSj>4~=MN3taNIwK?StL?<~`N;@KtAXd;5>QSM{Vf);`h6Nbl0M6x0pT48^Rz0MB9Y z>)-Rrf(W6oE+W5mc3K6Wv{RW~5Hxf`Q5ez9J#@UVUW47v@QSB3v(f|DLOWqKWd%$YdSX4I#1g$HJs1ebM2Kqr)&UHTVpeMq0h~P@uvp1HbZ7Q^S-}0 ztO@U*KrD$`su1&~rIbsr($Q`_`O~eAYJd zC~LdowR^bS&jptNYliuGAd8;*Ljhn!43gE#3?P6P5|fU{`d}Bj*+t3di?79A&_vm} zM8xMD*{_V832bE=0K;wiOn9JZX7N!9nsuI#)Q|Z3$^cQ>y@~jWVipT}RKJO4)ijOs8CCcqRUy6`2*o}kAy{%F$#_4{yJ*PgFf(@c_?%~}Q7Rfv1IKczS;r|^Gq5St zEI5@`Vgdt^fzXE*v*74coUoJ{=ih<|*u5I(VDCiSk)Y}qZnZwV5rngfLHn$I#XWFC3x9V8;8gb=xJq*wngQu>TIdj&w*FU4gTdzX@O7g;{%=8$w(z^liPR~50z~pW-|4Dyf{+ayv z*nVmW7{M1$r`)A^TLjmDqP?(ul$w!t-A-@+^>@x+P8%YW5N5YhpHnjTQo4Pj&n88F zQ{?_gy4*gUn8j*abQC=DXU}G5YJTK;zbWy>oEe2Ff1;*|td9Itq$BAn`H9Kq8XV#> z1RvY?Z@X@$$IkU1FtM*elE3BKQpDcH9uffL*6gg9DO|pT;nkK0$q{XD_Gu-I-4bm| f{WD;2^N?%bBVA+6KE{%DB*$iB>0nWdx)A+8{px&) literal 0 HcmV?d00001 diff --git a/openscad_py.py b/openscad_py.py index 64c3f86..1a320d4 100644 --- a/openscad_py.py +++ b/openscad_py.py @@ -9,14 +9,14 @@ NP_TYPE = np.float_ class Point: - """Represents a point of vector in arbitrary dimensions""" + """Represents a point or vector in arbitrary dimensions""" def __init__(self, coords): self.c = np.array(coords, dtype=NP_TYPE) @classmethod def c(cls, coords: TUnion[list, 'Point']) -> 'Point': - """Ensure coords is an instance of Point""" + """Ensure coords is an instance of Point (idempotent)""" if isinstance(coords, Point): return coords return Point(coords) @@ -177,10 +177,12 @@ class Object: return Difference(subject=self, tool=tool) def union(self, objects: TUnion[list, 'Object']) -> 'Object': + """Form the union of self and an object or list of objects""" return Union(child=Collection.c(objects)._add(self)) class Header: + """Render a header (setting global values) of an OpensCAD file""" def __init__(self, quality: str = 'draft'): self.quality = quality @@ -451,13 +453,14 @@ class Polygon(Object): class Collection(Object): + """Represents a collection of objects""" def __init__(self, coll: list): self.collection = coll @classmethod def c(cls, coll: TUnion[list, Object]) -> Object: - """Cast lists to collections""" + """Ensure the list of objects is a Collection (idempotent)""" if isinstance(coll, Object): return coll return cls(coll) @@ -494,8 +497,8 @@ class Rotate(Object): class Scale(Object): - def __init__(self, v: TUnion[list, Point, float], child: Object): - if isinstance(v, float): + def __init__(self, v: TUnion[list, Point, float, int], child: Object): + if isinstance(v, float) or isinstance(v, int): v = [v, v, v] self.v = Point.c(v) self.child = child