<jupyter_start><jupyter_text>CSG CAD in Python  SetupTo setup this environment, you need to install:* [OpenSCAD](https://www.openscad.org/downloads.html)Then you need to open your anaconda command prompt and power shell. * install [SolidPython](https://github.com/SolidCode/SolidPythonusing-solidpython) using the command `pip install solidpython`* install [viewscad](https://github.com/nickc92/ViewSCAD) by running the command `pip install viewscad`<jupyter_code>from solid import *
from solid.utils import *
import viewscad

r = viewscad.Renderer(openscad_exec='C:\Program Files\OpenSCAD\openscad.exe')<jupyter_output><empty_output><jupyter_text>Above we setup the environment. It includes importing the relivant functions and telling the rendered where openscad is. You may need to change the line `openscad_exec='C:\Program Files\OpenSCAD\openscad.exe'`**Next lets generate a simple cylinder**<jupyter_code>c = cylinder(r=10,h=5)
r.render(c)<jupyter_output><empty_output><jupyter_text>The code is converting the cylinder to a very basic form of an equation into a triangulated surfce. But as we can see the resolution isnt great. This is the same problem we have when we convert from a nice geoemtry in Solidworks/Autodesk to STL files. How can we fix it? We increase the discritization resolution<jupyter_code>c = cylinder(r=10,h=5,segments=1000)
r.render(c)<jupyter_output><empty_output><jupyter_text>Ok, so openSCAD aaaaaaaaaaalways converts from CSG into a mesh to be viewed. We can see this when we zoom into the objet and all of the sudden there isnt anything inside! And we can set the resolution of this conversion.   What are the primitives? sphere<jupyter_code># Sphere with radius 10
s1 = sphere(r=10)
r.render(s1)
# Set diameter to 5
s2 = sphere(d=5, segments = 100)
r.render(s2)<jupyter_output><empty_output><jupyter_text>Cuboids<jupyter_code>c1 = cube(10)
r.render(c1)<jupyter_output><empty_output><jupyter_text>The basic function puts a corner at the origin with the side length you set. We can also tell it to automatically put it with the center at the origin<jupyter_code>c2 = cube(10,True)
r.render(c2)<jupyter_output><empty_output><jupyter_text>We can also use it to make a rectangular prisim.<jupyter_code>c3 = cube([10,20,30])
r.render(c3)<jupyter_output><empty_output><jupyter_text>You can note that there is no segments argument to cube because a triangulated mesh is a perfect representation of a cube.   Cylinderwe saw the basics above, but we can also make Cones using cylinder<jupyter_code>c2 = cylinder(r1 = 10, r2=5,h=5)
r.render(c2)<jupyter_output><empty_output><jupyter_text>It is also possible to define cone sections using diameters and to shift the center to the center of the height axis. The height axis will be in the Z direction<jupyter_code>c3 = cylinder(d1 = 10, d2=5,h=5,center=True)
r.render(c3)<jupyter_output><empty_output><jupyter_text>PolyhedronWhat if those primitives are not what you need? What if I need to make a pyramids?  or Tetra hedra, or any of the platonic solids besides a cube?<jupyter_code>points = [[0,0,0],[0,1,0],[1,1,0],[1,0,0],[0.5,0.5,1]]
faces = [[0,1,2,3], # Base
         [0,1,4],[1,2,4],[2,3,4],[3,0,4]]
p = polyhedron(points = points,faces = faces)
r.render(p)
points=[ [10,10,0],[10,-10,0],[-10,-10,0],[-10,10,0], # the four points at base
           [0,0,10]  ]                               # the apex point 
faces=[ [0,1,4],[1,2,4],[2,3,4],[3,0,4],              # each triangle side
              [1,0,3],[2,1,3] ]                       # two triangles for square base
p2 = polyhedron(points = points,faces = faces)
r.render(p2)
def pyramid(side,height):
    s = side/2.0
    points=[ [s,s,0],[s,-s,0],[-s,-s,0],[-s,s,0], # the four points at base
           [0,0,height]  ]                               # the apex point 
    faces=[ [0,1,4],[1,2,4],[2,3,4],[3,0,4],              # each triangle side
              [1,0,3],[2,1,3] ]                       # two triangles for square base
    return polyhedron(points = points,faces = faces)
r.render(pyramid(10,10))<jupyter_output><empty_output><jupyter_text>If your polygon is having issues with faces not showing up. look at the segment on debugging your faces [here](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Primitive_Solidspolyhedron)  Well what about those basic CSG operations?* Union* Difference* IntersectionArent they just the opperations of a **SET??**<jupyter_code>c3 = c1 + c2
r.render(c3)
c3 = c1 - c2 
r.render(c3)
c3 = c2 - c1 
r.render(c3)
c3 = c1*c2 
r.render(c3)<jupyter_output><empty_output><jupyter_text>Hole<jupyter_code>pipe_od = 10
pipe_id = 3
seg_length = 10
outer = cylinder(r=pipe_od, h=seg_length)
inner = cylinder(r=pipe_id, h=seg_length)
pipe_a = outer - hole()(inner)
r.render(pipe_a)
pipe_a = outer + hole()(inner)
r.render(pipe_a)<jupyter_output><empty_output><jupyter_text>Homogenous Transformations * Scale     * scale a     * scale [a1,a2,a3]    * resize [x,y,z]* Rotate    * axis angle    * [dx,dy,dz] euler* Translate    * tx ty tz* mirror    * plane normal n1 n2 n3      Scaling<jupyter_code>x = scale(1.5)(cube(10))-cube(10)
r.render(x)
x = scale([1,2,3])(sphere(10))
r.render(x)
x = resize([20,5,10])(sphere(10))
r.render(x)<jupyter_output><empty_output><jupyter_text>RotationAs we discussed in class there are several ways to do rotations. namely Axis-angle, Euler Angles, and Quaternions. Natively OpenSCAD only does the first two. Lets look at Axis-angle<jupyter_code>angle = 45
axis = [0,1,0]
x = rotate(angle,axis)(pyramid(10,10))
r.render(x)
angle = 45
axis = [1,1,0]
x = rotate(angle,axis)(pyramid(10,10))
r.render(x)
angles = [20,10,5]
x = rotate(angles)(pyramid(10,10))
r.render(x)
angles = [20,10,5]
x2 = rotate(angles[0],[1,0,0])(pyramid(10,10))
r.render(x2)
x2 = rotate(angles[1],[0,1,0])(x2)
r.render(x2)
x2 = rotate(angles[2],[0,0,1])(x2)
r.render(x2)
r.render(x-x2+sphere(1))<jupyter_output><empty_output><jupyter_text>This means the system uses the XYZ euler angles. NOT any of the implicit XY'Z'' or the standard ZXZ system. So be aware when someone quotes you euler angles! We can also use quaterions, but we need to insert our own transformation. To do that we can convert to Matrix form using the equation below$$ R= \left(\begin{matrix}1-2(q_j^2-q_k^2)&2(q_iq_j-q_kq_r)&2(q_iq_k+q_jq_r)\\2(q_iq_j+q_kq_r)&1-2(q_i^2-q_k^2)&2(q_jq_k-q_iq_r)\\2(q_iq_k-q_jq_r)&2(q_jq_k+q_iq_r)&1-2(q_i^2-q_j^2)\end{matrix}\right) $$We should note that the naming here is carefully chosen. some people list quaternions as $$q = [q_r,q_i,q_j,q_k]$$ others use $$q = [q_i,q_j,q_k,q_r]$$ so be careful when storing quaternion as a list or connecting two bits of quaternion code from different sources together. Scipy and numpy use the second convention<jupyter_code>import numpy as np
from scipy.spatial.transform import Rotation as RM
rm = RM.from_quat([np.sin(np.pi/4),0, 0, np.cos(np.pi/4)])
rot = rm.as_dcm()
print(rot)
y = multmatrix(rot)(pyramid(10,10))
r.render(y)<jupyter_output>[[ 1.  0.  0.]
 [ 0.  0. -1.]
 [ 0.  1.  0.]]<jupyter_text>Translate<jupyter_code>y = translate([0,10,0])(pyramid(10,10))+pyramid(10,10)
r.render(y)
c = cube([30,20,10])
c1 = translate([20,0,0])(rotate(45,[0,0,1])(c))
c2 = mirror([1,0,0])(c1)
r.render(c1+c2)<jupyter_output><empty_output><jupyter_text>Other Operations* [minkowski sum](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations)* [Convex Hull](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Transformations)* Shear transforms using m Matrix* [Affine Transforms](https://www.wikiwand.com/en/Affine_transformation)<jupyter_code>x = minkowski()(cube([10,10,1]),cylinder(1,True))
r.render(x)
q = sphere(1)
x = minkowski()(pyramid(10,10),translate([5,5,0])(q))
r.render(x)<jupyter_output><empty_output><jupyter_text>Convex Hull<jupyter_code>c1 = cylinder(1,1)
c2 = translate([10,10,0])(c1)
y = c1+c2
r.render(y)
c1 = cylinder(1,1)
c2 = translate([10,10,0])(c1)
y = hull()(c1,c2)
r.render(y)
c1 = cylinder(1,1)
c2 = translate([10,10,0])(c1)
c3 = translate([0,10,0])(c1)
y = hull()(c1,c2,c3)
r.render(y)<jupyter_output><empty_output><jupyter_text>2D operations* square* circle* polygon* text<jupyter_code>s1 = square(size = [1,10],center = True)
s2 = square(size = 1.5, center = False)
y = s1-s2
scad_render(y)
c1 = circle(r=10)
c2 = circle(d=10)
y = c2-c1
scad_render(y)
xs = [np.cos(2*np.pi/5*i)for i in range(0,5)]
ys = [np.sin(2*np.pi/5*i)for i in range(0,5)]
pts = list(zip(xs,ys))
y = polygon(points=pts)
scad_render(y)
y= text('ME480')
scad_render(y)<jupyter_output><empty_output><jupyter_text>2D to 3D* linear extrude    * text to use to make labels    * twisting* rotation extrude    * [solids of rotations ](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/2D_to_3D_ExtrusionLinear_Extrude)<jupyter_code>y = linear_extrude(height =10,center=True)(text('ME480'))
r.render(y)
y = linear_extrude(height=10, twist=90)(text('ME480'))
r.render(y)
y = linear_extrude(height=10, twist=90,slices = 100)(text('ME480'))
r.render(y)
xs = [np.cos(2*np.pi/5*i)for i in range(0,5)]
ys = [np.sin(2*np.pi/5*i)for i in range(0,5)]
pts = list(zip(xs,ys))
y = polygon(points=pts)
z = linear_extrude(height=10, twist=4*360,slices = 100)(y)
r.render(z)
z = linear_extrude(height=10, twist=4*360,slices = 100,scale = 0.5)(y)
r.render(z)
z = linear_extrude(height=10, twist=4*360,slices = 100, scale=[1,2])(y)
r.render(z)
z = rotate_extrude(angle = 360)(translate([2,0,0])(y))
scad_render(z)
r.render(z)
z = rotate_extrude(angle = 360,segments=100)(translate([2,0,0])(y))
scad_render(z)
r.render(z)<jupyter_output><empty_output><jupyter_text>Imports* stl import* import SCAD    * lego Brick Model<jupyter_code>q = import_stl("C://Users//jil26//Documents//GitHub//ME480//JupyterNotebooks//darthVader.stl")
w = translate([100,0,0])(q)
scad_render(q+w).replace('\n','').replace('\t','')<jupyter_output><empty_output><jupyter_text>Sometimes the renderer isnt perfect, so you have to copy and paste into openscad  [OpenSCAD MCAD Library](https://github.com/openscad/MCAD)<jupyter_code>scadfile = import_scad('C://Users//jil26//Documents//GitHub//ME480//JupyterNotebooks//MCAD-master//lego_compatibility.scad')
#use('C://Users//jil26//Documents//GitHub//ME480//JupyterNotebooks//MCAD-master//lego_compatibility.scad')

width = 10
length = 5
height = 2
b = scadfile.block(width,length,height)
r.render(b)
x = minkowski()(cylinder(r=5,h=5),sphere(1))
cn =cylinder(r=10,h=5)- hole()(cylinder(r=5,h=5))-translate([0,0,5])(x)
r.render(cn)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1559011544699933, 'fraction_numerical': 0.051330979868333175, 'mean_word_length': 5.575909661229611, 'pattern_counts': {'":': 0, '<': 88, '<?xml version=': 0, '>': 88, 'https://': 9, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '22', 'dir': 'jupyter-structured-clean-dedup', 'id': '469018', 'license': 'MIT', 'n_tokens_mistral': 4465, 'n_tokens_neox': 4153, 'n_words': 911, 'path': 'JupyterNotebooks/2019/SolidPython.ipynb', 'repo_name': 'JeffreyILipton/ME480', 'size': '10481'}