diff --git a/README.md b/README.md index e17dc2ee..b6a22f30 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,8 @@ Some modules may depend on other modules. For example, the `polyline2d` module d - [line3d](https://openhome.cc/eGossip/OpenSCAD/lib-line3d.html) - [polyline3d](https://openhome.cc/eGossip/OpenSCAD/lib-polyline3d.html) - [hull_polyline3d](https://openhome.cc/eGossip/OpenSCAD/lib-hull_polyline3d.html) - + - [function_grapher](https://openhome.cc/eGossip/OpenSCAD/lib-function_grapher.html) + - Transformation - [hollow_out](https://openhome.cc/eGossip/OpenSCAD/lib-hollow_out.html) - [bend](https://openhome.cc/eGossip/OpenSCAD/lib-bend.html) diff --git a/docs/images/lib-function_grapher-1.JPG b/docs/images/lib-function_grapher-1.JPG new file mode 100644 index 00000000..8b4f183d Binary files /dev/null and b/docs/images/lib-function_grapher-1.JPG differ diff --git a/docs/images/lib-function_grapher-2.JPG b/docs/images/lib-function_grapher-2.JPG new file mode 100644 index 00000000..20a25abe Binary files /dev/null and b/docs/images/lib-function_grapher-2.JPG differ diff --git a/docs/images/lib-function_grapher-3.JPG b/docs/images/lib-function_grapher-3.JPG new file mode 100644 index 00000000..7252b060 Binary files /dev/null and b/docs/images/lib-function_grapher-3.JPG differ diff --git a/docs/images/lib-function_grapher-4.JPG b/docs/images/lib-function_grapher-4.JPG new file mode 100644 index 00000000..39c5b585 Binary files /dev/null and b/docs/images/lib-function_grapher-4.JPG differ diff --git a/docs/lib-function_grapher.md b/docs/lib-function_grapher.md new file mode 100644 index 00000000..57c385d0 --- /dev/null +++ b/docs/lib-function_grapher.md @@ -0,0 +1,116 @@ +# function_grapher + +Given a set of points `[x, y, f(x, y)]` where `f(x, y)` is a mathematics function, the `function_grapher` module can create the graph of `f(x, y)`. + +It depends on the `line3d` and `polyline3d` modules so you have to include "line3d.scad" and "polyline3d.scad". + +## Parameters + +- `points` : A set of points `[x, y, f(x, y)]`. See examples below. +- `thickness` : The face or line thickness. +- `style` : The style of the graph. It accepts `"FACES"`, `"LINES"` and `"HULL_FACES"`. The default value is `"FACES"` which simply takes `f(x, y) - thickness` for each point to build a bottom. It may cause thickness problems when slopes is high. The `"HULL_FACES"` value can solve the problem but is slow. When assigning `"LINES"`, it uses lines to connect points. +- `slicing` : Given a rectangle, we have two ways to slice it into two triangles. Using this parameter to determine the way you want. It accepts `"SLASH"` (default) and `"BACK_SLASH"`. +- `$fa`, `$fs`, `$fn` : Used by the `circle` or `sphere` module internally. It affects the speed of rending. For example, a large `$fn` may be very slow when rending. Check [the circle module](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Using_the_2D_Subsystem#circle) or [the sphere module](https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Primitive_Solids#sphere) for more details. + +## Examples + + include ; + include ; + include ; + + points = [ + [[0, 0, 1], [1, 0, 2], [2, 0, 2], [3, 0, 3]], + [[0, 1, 1], [1, 1, 4], [2, 1, 0], [3, 1, 3]], + [[0, 2, 1], [1, 2, 3], [2, 2, 1], [3, 2, 3]], + [[0, 3, 1], [1, 3, 3], [2, 3, 1], [3, 3, 3]] + ]; + + thickness = 0.5; + + function_grapher(points, thickness); + +![function_grapher](images/lib-function_grapher-1.JPG) + + include ; + include ; + include ; + + function f(x, y) = + 30 * ( + cos(sqrt(pow(x, 2) + pow(y, 2))) + + cos(3 * sqrt(pow(x, 2) + pow(y, 2))) + ); + + thickness = 2; + min_value = -200; + max_value = 200; + resolution = 10; + + points = [ + for(y = [min_value:resolution:max_value]) + [ + for(x = [min_value:resolution:max_value]) + [x, y, f(x, y)] + ] + ]; + + function_grapher(points, thickness); + +![function_grapher](images/lib-function_grapher-2.JPG) + + include ; + include ; + include ; + + function f(x, y) = + 30 * ( + cos(sqrt(pow(x, 2) + pow(y, 2))) + + cos(3 * sqrt(pow(x, 2) + pow(y, 2))) + ); + + thickness = 2; + min_value = -200; + max_value = 200; + resolution = 10; + style = "LINES"; + + points = [ + for(y = [min_value:resolution:max_value]) + [ + for(x = [min_value:resolution:max_value]) + [x, y, f(x, y)] + ] + ]; + + function_grapher(points, thickness, style); + +![function_grapher](images/lib-function_grapher-3.JPG) + + include ; + include ; + include ; + + function f(x, y) = + 30 * ( + cos(sqrt(pow(x, 2) + pow(y, 2))) + + cos(3 * sqrt(pow(x, 2) + pow(y, 2))) + ); + + thickness = 2; + min_value = -200; + max_value = 200; + resolution = 10; + style = "LINES"; + slicing = "BACK_SLASH"; + + points = [ + for(y = [min_value:resolution:max_value]) + [ + for(x = [min_value:resolution:max_value]) + [x, y, f(x, y)] + ] + ]; + + function_grapher(points, thickness, style, slicing); + +![function_grapher](images/lib-function_grapher-4.JPG) \ No newline at end of file diff --git a/src/function_grapher.scad b/src/function_grapher.scad new file mode 100644 index 00000000..183b7305 --- /dev/null +++ b/src/function_grapher.scad @@ -0,0 +1,113 @@ +/** +* function_grapher.scad +* +* +* Given a set of points `[x, y, f(x, y)]` where `f(x, y)` is a +* mathematics function, the `function_grapher` module can +* create the graph of `f(x, y)`. +* It depends on the line3d and polyline3d modules so you have +* to include "line3d.scad" and "polyline3d.scad". +* +* @copyright Justin Lin, 2017 +* @license https://opensource.org/licenses/lgpl-3.0.html +* +* @see https://openhome.cc/eGossip/OpenSCAD/lib-function_grapher.html +* +**/ + +module function_grapher(points, thickness, style = "FACES", slicing = "SLASH") { + // Increasing $fn will be slow when you use "LINES" or "HULL_FACES". + + function tri_shell_points(top) = + let( + z_offset = [0, 0, -thickness], + bottom = [ + top[0] + z_offset, + top[1] + z_offset, + top[2] + z_offset + ], + faces = [ + [0, 1, 2], + [3, 4, 5], + [0, 1, 4, 3], + [1, 2, 5, 4], + [2, 0, 3, 5] + ] + ) + [ + concat(top, bottom), + faces + ]; + + + module tri_to_faces(top_tri1, top_tri2) { + pts_faces1 = tri_shell_points(top_tri1); + pts_faces2 = tri_shell_points(top_tri2); + + hull() { + polyhedron( + points = pts_faces1[0], + faces = pts_faces1[1] + ); + polyhedron( + points = pts_faces2[0], + faces = pts_faces2[1] + ); + } + } + + module tri_to_lines(tri1, tri2) { + polyline3d(concat(tri1, [tri1[0]]), thickness); + polyline3d(concat(tri2, [tri2[0]]), thickness); + } + + module hull_pts(tri) { + half_thickness = thickness / 2; + hull() { + translate(tri[0]) sphere(half_thickness); + translate(tri[1]) sphere(half_thickness); + translate(tri[2]) sphere(half_thickness); + } + } + + module tri_to_hull_faces(tri1, tri2) { + hull_pts(tri1); + hull_pts(tri2); + } + + module tri_to_graph(tri1, tri2) { + if(style == "FACES") { + tri_to_faces(tri1, tri2); + } else if(style == "LINES") { + tri_to_lines(tri1, tri2); + } else { // Warning: May be very slow!! + tri_to_hull_faces(tri1, tri2); + } + } + + for(yi = [0:len(points) - 2]) { + for(xi = [0:len(points[yi]) - 2]) { + if(slicing == "SLASH") { + tri_to_graph([ + points[yi][xi], + points[yi][xi + 1], + points[yi + 1][xi + 1] + ], [ + points[yi][xi], + points[yi + 1][xi + 1], + points[yi + 1][xi] + ]); + } else { + tri_to_graph([ + points[yi][xi], + points[yi][xi + 1], + points[yi + 1][xi] + ], [ + points[yi + 1][xi], + points[yi][xi + 1], + points[yi + 1][xi + 1] + ]); + } + } + } +} \ No newline at end of file