import fmglee
import gleam/bool
import gleam/list
import gleam/string
pub type RadiusOrDiameter {
Radius
Diameter
}
pub type CenteredOrNot {
Centered
NotCentered
}
/// builder pattern for our OpenSCAD expressions:
pub type OpenSCADExpr {
//Boolean Operations:
Union(expressions: List(OpenSCADExpr))
Difference(expressions: List(OpenSCADExpr))
Intersection(expressions: List(OpenSCADExpr))
//Transformations:
Translate(expr: OpenSCADExpr, vector: #(Float, Float, Float))
Rotate(expr: OpenSCADExpr, angle: #(Float, Float, Float))
Scale(expr: OpenSCADExpr, vector: #(Float, Float, Float))
//resize([x,y,z],auto,convexity)
Mirror(expr: OpenSCADExpr, vector: #(Float, Float, Float))
//multmatrix(m)
//color("colorname",alpha)
//color("#hexvalue")
//color([r,g,b,a])
//offset(r|delta,chamfer)
//hull()
//minkowski(convexity)
//2D Objects:
Circle(value: Float, rod: RadiusOrDiameter)
Square(width: Float, height: Float, center: CenteredOrNot)
Polygon(List(#(Float, Float)))
//polygon([points],[paths])
//text(text,size,font,direction,language,script, halign,valign,spacing)
//import("….ext", convexity)
//projection(cut)
//3D Objects:
Cube(size: #(Float, Float, Float), center: CenteredOrNot)
Sphere(value: Float, rod: RadiusOrDiameter)
Cylinder(
height: Float,
value: Float,
rod: RadiusOrDiameter,
center: CenteredOrNot,
)
Cylinder2(
height: Float,
value1: Float,
value2: Float,
rod: RadiusOrDiameter,
center: CenteredOrNot,
)
//polyhedron(points, faces, convexity)
//import("….ext", convexity)
LinearExtrude(
expr: OpenSCADExpr,
height: Float,
center: CenteredOrNot,
convexity: Float,
twist: Float,
slices: Float,
)
//rotate_extrude(angle,convexity)
//surface(file = "….ext",center,convexity)
//Custom OpenSCAD code:
CustomCode(code: String)
//Special variables...
}
/// Create a union of shapes
pub fn union(expressions: List(OpenSCADExpr)) -> OpenSCADExpr {
Union(expressions)
}
/// Create a difference of shapes
pub fn difference(expressions: List(OpenSCADExpr)) -> OpenSCADExpr {
Difference(expressions)
}
/// Creates an intersection of shapes
pub fn intersection(expressions: List(OpenSCADExpr)) -> OpenSCADExpr {
Intersection(expressions)
}
/// Translate (move) a shape
pub fn translate(
expr: OpenSCADExpr,
x: Float,
y: Float,
z: Float,
) -> OpenSCADExpr {
Translate(expr, #(x, y, z))
}
/// Rotate a shape
pub fn rotate(
expr: OpenSCADExpr,
x: Float,
y: Float,
z: Float,
) -> OpenSCADExpr {
Rotate(expr, #(x, y, z))
}
/// Scales a shape
pub fn scale(expr: OpenSCADExpr, x: Float, y: Float, z: Float) -> OpenSCADExpr {
Scale(expr, #(x, y, z))
}
/// Mirrors a shape
pub fn mirror(
expr: OpenSCADExpr,
x: Float,
y: Float,
z: Float,
) -> OpenSCADExpr {
Mirror(expr, #(x, y, z))
}
/// Draws a 2d circle
pub fn circle(value: Float, rod: RadiusOrDiameter) -> OpenSCADExpr {
Circle(value, rod)
}
/// Draws a 2d square
pub fn square(
width: Float,
height: Float,
center: CenteredOrNot,
) -> OpenSCADExpr {
Square(width, height, center)
}
/// Draws a 2d polygon
pub fn polygon(polygons: List(#(Float, Float))) -> OpenSCADExpr {
Polygon(polygons)
}
/// Creates a 3d cube with all sides of equal length
pub fn cube(size: Float, center: CenteredOrNot) -> OpenSCADExpr {
Cube(#(size, size, size), center)
}
/// Creates a 3d cube
pub fn cube3(
width: Float,
depth: Float,
height: Float,
center: CenteredOrNot,
) -> OpenSCADExpr {
Cube(#(width, depth, height), center)
}
/// Creates a 3d sphere
pub fn sphere(value: Float, rod: RadiusOrDiameter) -> OpenSCADExpr {
Sphere(value, rod)
}
/// Creates a 3d cylinder
pub fn cylinder(
height: Float,
value: Float,
rod: RadiusOrDiameter,
center: CenteredOrNot,
) -> OpenSCADExpr {
Cylinder(height, value, rod, center)
}
/// Creates a 3d cylinder. Enables you to pass different diameters/radii for top and bottom
pub fn cylinder2(
height: Float,
value1: Float,
value2: Float,
rod: RadiusOrDiameter,
center: CenteredOrNot,
) -> OpenSCADExpr {
Cylinder2(height, value1, value2, rod, center)
}
/// Allows for adding custom code
pub fn custom_code(code: String) -> OpenSCADExpr {
CustomCode(code)
}
pub fn linear_extrude(
expr: OpenSCADExpr,
height: Float,
center: CenteredOrNot,
convexity: Float,
twist: Float,
slices: Float,
) -> OpenSCADExpr {
LinearExtrude(expr, height, center, convexity, twist, slices)
}
/// Helper function, for creating some of the strings
fn build_command(
expr: OpenSCADExpr,
command: String,
vector: #(Float, Float, Float),
) -> String {
fmglee.new("%s([%f, %f, %f]){\n%s\n}")
|> fmglee.s(command)
|> fmglee.f(vector.0)
|> fmglee.f(vector.1)
|> fmglee.f(vector.2)
|> fmglee.s(to_openscad(expr))
|> fmglee.build
}
fn centered_or_not_to_string(value: CenteredOrNot) {
case value {
Centered -> True
NotCentered -> False
}
|> bool.to_string
|> string.lowercase
}
/// Exports the data to a string, containing OpenSCAD code
pub fn to_openscad(expr: OpenSCADExpr) -> String {
case expr {
Union(expressions) ->
fmglee.new("union(){\n%s\n}")
|> fmglee.s(expressions |> list.map(to_openscad) |> string.join("\n"))
|> fmglee.build
Difference(expressions) ->
fmglee.new("difference(){\n%s\n}")
|> fmglee.s(expressions |> list.map(to_openscad) |> string.join("\n"))
|> fmglee.build
Intersection(expressions) ->
fmglee.new("intersection(){\n%s\n}")
|> fmglee.s(expressions |> list.map(to_openscad) |> string.join("\n"))
|> fmglee.build
Translate(expr, vector) -> build_command(expr, "translate", vector)
Rotate(expr, angle) -> build_command(expr, "rotate", angle)
Scale(expr, vector) -> build_command(expr, "scale", vector)
Mirror(expr, vector) -> build_command(expr, "mirror", vector)
Circle(value, rod) ->
case rod {
Radius ->
fmglee.new("circle(r=%f);")
|> fmglee.f(value)
|> fmglee.build
Diameter ->
fmglee.new("circle(d=%f);")
|> fmglee.f(value)
|> fmglee.build
}
Square(width, height, center) ->
fmglee.new("square(width=%f,height=%f,center=%s);")
|> fmglee.f(width)
|> fmglee.f(height)
|> fmglee.s(centered_or_not_to_string(center))
|> fmglee.build
Polygon(polygons) ->
fmglee.new("polygon(points=[%s]);")
|> fmglee.s(
polygons
|> list.map(fn(point) {
fmglee.new("[%f,%f]")
|> fmglee.f(point.0)
|> fmglee.f(point.1)
|> fmglee.build
})
|> string.join(","),
)
|> fmglee.build
Cube(size, center) -> {
let #(w, d, h) = size
fmglee.new("cube(size=[%f, %f, %f], center=%s);")
|> fmglee.f(w)
|> fmglee.f(d)
|> fmglee.f(h)
|> fmglee.s(centered_or_not_to_string(center))
|> fmglee.build
}
Sphere(value, rod) ->
case rod {
Radius ->
fmglee.new("sphere(r=%f);")
|> fmglee.f(value)
|> fmglee.build
Diameter ->
fmglee.new("sphere(d=%f);")
|> fmglee.f(value)
|> fmglee.build
}
Cylinder(height, value, rod, center) ->
case rod {
Radius ->
fmglee.new("cylinder(h=%f,r=%f,center=%s);")
|> fmglee.f(height)
|> fmglee.f(value)
|> fmglee.s(centered_or_not_to_string(center))
|> fmglee.build
Diameter ->
fmglee.new("cylinder(h=%f,d=%f,center=%s);")
|> fmglee.f(height)
|> fmglee.f(value)
|> fmglee.s(centered_or_not_to_string(center))
|> fmglee.build
}
Cylinder2(height, value1, value2, rod, center) ->
case rod {
Radius ->
fmglee.new("cylinder(h=%f,r1=%f,r2=%f,center=%s);")
|> fmglee.f(height)
|> fmglee.f(value1)
|> fmglee.f(value2)
|> fmglee.s(centered_or_not_to_string(center))
|> fmglee.build
Diameter ->
fmglee.new("cylinder(h=%f,d1=%f,d2=%f,center=%s);")
|> fmglee.f(height)
|> fmglee.f(value1)
|> fmglee.f(value2)
|> fmglee.s(centered_or_not_to_string(center))
|> fmglee.build
}
LinearExtrude(expr, height, center, convexity, twist, slices) ->
fmglee.new(
"linear_extrude(height=%f,center=%s,convexity=%f,twist=%f,slices=%f){\n%s\n}",
)
|> fmglee.f(height)
|> fmglee.s(centered_or_not_to_string(center))
|> fmglee.f(convexity)
|> fmglee.f(twist)
|> fmglee.f(slices)
|> fmglee.s(to_openscad(expr))
|> fmglee.build
CustomCode(code) -> code
}
}