├── LICENSE.txt
├── README.md
├── demo_plot_function.scad
├── demo_plot_function.stl
├── images
└── demo_plot_function.png
└── plot_function.scad
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Creative Commons Legal Code
2 |
3 | CC0 1.0 Universal
4 |
5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
12 | HEREUNDER.
13 |
14 | Statement of Purpose
15 |
16 | The laws of most jurisdictions throughout the world automatically confer
17 | exclusive Copyright and Related Rights (defined below) upon the creator
18 | and subsequent owner(s) (each and all, an "owner") of an original work of
19 | authorship and/or a database (each, a "Work").
20 |
21 | Certain owners wish to permanently relinquish those rights to a Work for
22 | the purpose of contributing to a commons of creative, cultural and
23 | scientific works ("Commons") that the public can reliably and without fear
24 | of later claims of infringement build upon, modify, incorporate in other
25 | works, reuse and redistribute as freely as possible in any form whatsoever
26 | and for any purposes, including without limitation commercial purposes.
27 | These owners may contribute to the Commons to promote the ideal of a free
28 | culture and the further production of creative, cultural and scientific
29 | works, or to gain reputation or greater distribution for their Work in
30 | part through the use and efforts of others.
31 |
32 | For these and/or other purposes and motivations, and without any
33 | expectation of additional consideration or compensation, the person
34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she
35 | is an owner of Copyright and Related Rights in the Work, voluntarily
36 | elects to apply CC0 to the Work and publicly distribute the Work under its
37 | terms, with knowledge of his or her Copyright and Related Rights in the
38 | Work and the meaning and intended legal effect of CC0 on those rights.
39 |
40 | 1. Copyright and Related Rights. A Work made available under CC0 may be
41 | protected by copyright and related or neighboring rights ("Copyright and
42 | Related Rights"). Copyright and Related Rights include, but are not
43 | limited to, the following:
44 |
45 | i. the right to reproduce, adapt, distribute, perform, display,
46 | communicate, and translate a Work;
47 | ii. moral rights retained by the original author(s) and/or performer(s);
48 | iii. publicity and privacy rights pertaining to a person's image or
49 | likeness depicted in a Work;
50 | iv. rights protecting against unfair competition in regards to a Work,
51 | subject to the limitations in paragraph 4(a), below;
52 | v. rights protecting the extraction, dissemination, use and reuse of data
53 | in a Work;
54 | vi. database rights (such as those arising under Directive 96/9/EC of the
55 | European Parliament and of the Council of 11 March 1996 on the legal
56 | protection of databases, and under any national implementation
57 | thereof, including any amended or successor version of such
58 | directive); and
59 | vii. other similar, equivalent or corresponding rights throughout the
60 | world based on applicable law or treaty, and any national
61 | implementations thereof.
62 |
63 | 2. Waiver. To the greatest extent permitted by, but not in contravention
64 | of, applicable law, Affirmer hereby overtly, fully, permanently,
65 | irrevocably and unconditionally waives, abandons, and surrenders all of
66 | Affirmer's Copyright and Related Rights and associated claims and causes
67 | of action, whether now known or unknown (including existing as well as
68 | future claims and causes of action), in the Work (i) in all territories
69 | worldwide, (ii) for the maximum duration provided by applicable law or
70 | treaty (including future time extensions), (iii) in any current or future
71 | medium and for any number of copies, and (iv) for any purpose whatsoever,
72 | including without limitation commercial, advertising or promotional
73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
74 | member of the public at large and to the detriment of Affirmer's heirs and
75 | successors, fully intending that such Waiver shall not be subject to
76 | revocation, rescission, cancellation, termination, or any other legal or
77 | equitable action to disrupt the quiet enjoyment of the Work by the public
78 | as contemplated by Affirmer's express Statement of Purpose.
79 |
80 | 3. Public License Fallback. Should any part of the Waiver for any reason
81 | be judged legally invalid or ineffective under applicable law, then the
82 | Waiver shall be preserved to the maximum extent permitted taking into
83 | account Affirmer's express Statement of Purpose. In addition, to the
84 | extent the Waiver is so judged Affirmer hereby grants to each affected
85 | person a royalty-free, non transferable, non sublicensable, non exclusive,
86 | irrevocable and unconditional license to exercise Affirmer's Copyright and
87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the
88 | maximum duration provided by applicable law or treaty (including future
89 | time extensions), (iii) in any current or future medium and for any number
90 | of copies, and (iv) for any purpose whatsoever, including without
91 | limitation commercial, advertising or promotional purposes (the
92 | "License"). The License shall be deemed effective as of the date CC0 was
93 | applied by Affirmer to the Work. Should any part of the License for any
94 | reason be judged legally invalid or ineffective under applicable law, such
95 | partial invalidity or ineffectiveness shall not invalidate the remainder
96 | of the License, and in such case Affirmer hereby affirms that he or she
97 | will not (i) exercise any of his or her remaining Copyright and Related
98 | Rights in the Work or (ii) assert any associated claims and causes of
99 | action with respect to the Work, in either case contrary to Affirmer's
100 | express Statement of Purpose.
101 |
102 | 4. Limitations and Disclaimers.
103 |
104 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
105 | surrendered, licensed or otherwise affected by this document.
106 | b. Affirmer offers the Work as-is and makes no representations or
107 | warranties of any kind concerning the Work, express, implied,
108 | statutory or otherwise, including without limitation warranties of
109 | title, merchantability, fitness for a particular purpose, non
110 | infringement, or the absence of latent or other defects, accuracy, or
111 | the present or absence of errors, whether or not discoverable, all to
112 | the greatest extent permissible under applicable law.
113 | c. Affirmer disclaims responsibility for clearing rights of other persons
114 | that may apply to the Work or any use thereof, including without
115 | limitation any person's Copyright and Related Rights in the Work.
116 | Further, Affirmer disclaims responsibility for obtaining any necessary
117 | consents, permissions or other rights required for any use of the
118 | Work.
119 | d. Affirmer understands and acknowledges that Creative Commons is not a
120 | party to this document and has no duty or obligation with respect to
121 | this CC0 or use of the Work.
122 |
123 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OpenSCAD Function Plotting Library
2 |
3 |

4 |
5 | This is a general purpose function plotting library for OpenSCAD which will
6 | render functions with Cartesian coordinates (x & y input, z output),
7 | polar/cylindrical coordinates (r & angle input, z output), or axial coordinates
8 | (z & angle input, r output). This library is sufficiently flexible that it can
9 | be used for more than just plotting functions. As demonstrated in the included
10 | demo files, this can efficiently render ordinary objects with surfaces defined
11 | by mathematical functions.
12 |
13 | While there are a few other function plotting libraries out there for OpenSCAD,
14 | this one is particularly robust, fast, and flexible. It uses list comprehension
15 | to generate each plot as a single polyhedron, supports multiple user-defined
16 | functions of each type in one design, consistently creates properly manifold
17 | renders, and executes as quickly as any other rendered object of comparable
18 | size.
19 |
20 | # Usage
21 |
22 | Since OpenSCAD does not support passing functions as parameters, this library
23 | resolves it by allowing the user to declare functions Func1, Func2, etc,
24 | PolarFunc1, PolarFunc2, etc, and AxialFunc1, AxialFunc2, etc. Then the number
25 | 1, 2, etc is passed to the corresponding plot routine. For example, the
26 | following code will create a Cartesian coordinate plot of the following
27 | function #1 over the domain -40 through 40 in both x and y with a step size of
28 | 0.4:
29 |
30 | ```
31 | include
32 | function Func1(x, y) = 2*(1.5 + cos(x*x + y*y/4));
33 | PlotFunction(1, [-40, 0.4, 40], [-40, 0.4, 40]);
34 | ```
35 |
36 | The module call PlotFunction can occur anywhere a normal polyhedron could be
37 | generated, but the function definition Func1 must be declared at the top-level
38 | of the code so that it can be accessed from within the included plotting
39 | library. Note that it must "include" plot_function.scad rather than "use" it so
40 | that Func1 and others are accessible. A variety of usage demonstrations are in
41 | the demo_plot_function.scad file, and the API for the three plotting modules is
42 | as follows:
43 |
44 | ```
45 | // Plots the numbered function Func1 through Func9, where FuncN is 1 through 9.
46 | // Each function is a function of x and y.
47 | // minx_stepx_maxx should be [minx, stepx, maxx], and likewise for y,
48 | // specifying the domain to be plotted.
49 | // To guarantee a properly manifold shape, the routine will only render
50 | // strictly positive values (z>0) of the defined function. Add an offset if
51 | // needed to achieve this.
52 | module PlotFunction(FuncN, minx_stepx_maxx, miny_stepy_maxy)
53 |
54 | // Plots the numbered function PolarFunc1 through PolarFunc9, where
55 | // PolarFuncN is 1 through 9. Each function is a function of radius and
56 | // angle.
57 | // max_r is the outer radius, and min_step is the smallest step size between
58 | // points.
59 | // To guarantee a properly manifold shape, the routine will only render
60 | // strictly positive values (z>0) of the defined function. Add an offset if
61 | // needed to achieve this.
62 | module PlotPolarFunction(PolarFuncN, max_r, min_step=-1)
63 |
64 | // Plots the numbered function AxialFunc1 through AxialFunc9, where
65 | // AxialFuncN is 1 through 9. Each function is a function of z-height and
66 | // angle, and returns the radius outward in the xy-plane.
67 | // max_r is the outer radius, and min_step is the smallest step size between
68 | // points.
69 | // minz_stepz_maxz should be [minz, stepz, maxz], and likewise for y,
70 | // specifying the domain to be plotted.
71 | // To guarantee a properly manifold shape, the routine will only render
72 | // strictly positive values (r>0) of the defined function. Add an offset if
73 | // needed to achieve this.
74 | module PlotAxialFunction(AxialFuncN, minz_stepz_maxz, num_circle_steps=360)
75 | ```
76 |
77 | # How it works internally
78 |
79 | This section does not need to be read or understood to use the library. The key
80 | step in the design relies on the PlotClosePoints module which generates an
81 | arbitrary polyhedron given a two-dimensional array of points defining a series
82 | of consecutive loops outlining the polyhedron. This provides clear guarantees
83 | of producing a manifold shape as long as the outlining points are kept properly
84 | ordered by the Plot modules. The axial plots are then defined by loops from z=0
85 | to the max height, with the radial distance outward of each point given by the
86 | provided function. The Cartesian plots are done similarly, but starting from
87 | the minimum y value, including 2 coordinate values for the bottom corners at
88 | the x max and x min, and looping across the x values. The polar plots start
89 | with the bottom outer ring and loop inward with consecutive rings for each
90 | radial value of the polar part, with the loops adopting whatever z value the
91 | provided function gives.
92 |
93 |
--------------------------------------------------------------------------------
/demo_plot_function.scad:
--------------------------------------------------------------------------------
1 | // Created in 2017 by Ryan A. Colyer.
2 | // This work is released with CC0 into the public domain.
3 | // https://creativecommons.org/publicdomain/zero/1.0/
4 | //
5 | // https://www.thingiverse.com/thing:2391851
6 |
7 |
8 | include
9 |
10 |
11 | Demo(0);
12 |
13 | // 1 -- Gravity well
14 | function Func1(x, y) =
15 | let ( z = 50 - 50/sqrt(x*x+y*y) )
16 | z < 1 ? 1 : z;
17 |
18 | // 2 -- A bowl
19 | function PolarFunc2(r, a) = let(z = 23-sqrt(23*23-r*r)) (z < 2 ? 2 : z);
20 |
21 | // 3 -- A rose
22 | function PolarFunc3(r, a) = (15+5*sin(r*10))*exp(-pow(r*cos(a)*cos(r*8)+r*sin(a)*sin(r*35),2)/300) + 1;
23 |
24 | // 4 -- A simple chalice
25 | function AxialFunc1(z, ang) = 5*(cos(log(z/5+1)*360) + 2);
26 | function AxialFunc2(z, ang) = AxialFunc1(z, ang) - 2;
27 |
28 | // 5 --
29 | // Plane wave
30 | function Func4(x, y) = 2.5*(1+cos(y*36))+2;
31 | // Two-slit interference
32 | slit_pos = 10;
33 | function Func5(x, y) = 1.25*(1+cos(sqrt(y*y+(x-slit_pos)*(x-slit_pos))*36)) +
34 | 1.25*(1+cos(sqrt(y*y+(x+slit_pos)*(x+slit_pos))*36))+2;
35 |
36 | // One-slit
37 | function Func6(x, y) = 2.5*(1+cos(sqrt(y*y+(x-slit_pos)*(x-slit_pos))*36))+2;
38 |
39 | module DemoNumber(n) {
40 | SelectFrom(n-1) {
41 |
42 | // 1 -- Gravity well
43 | translate([10, 0, 0])
44 | PlotFunction(1, [-10, 0.2, 0], [-10, 0.2, 10]);
45 |
46 | // 2 -- A bowl
47 | difference() {
48 | PlotPolarFunction(2, 20, 0.8);
49 | translate([0, 0, -2]) PlotPolarFunction(2, 20.1, 0.8);
50 | }
51 |
52 | // 3 -- A rose
53 | PlotPolarFunction(3, 22, 0.4);
54 |
55 | // 4 -- A simple chalice
56 | difference() {
57 | PlotAxialFunction(1, [0, 0.4, 50], 180);
58 | PlotAxialFunction(2, [2, 0.4, 51], 180);
59 | }
60 |
61 | // 5 -- Two-slit interference
62 | union() {
63 | PlotFunction(4, [-25, 0.4, 25], [-25, 0.4, 0]);
64 | difference() {
65 | translate([-25, -1, 0]) cube([50, 2, 16]);
66 | translate([slit_pos, 0, 16/2]) cube([2, 3, 16+2], center=true);
67 | translate([-slit_pos, 0, 16/2]) cube([2, 3, 16+2], center=true);
68 | }
69 | PlotFunction(5, [-25, 0.4, 25], [0, 0.4, 30]);
70 | }
71 |
72 | // 6 -- One-slit wave
73 | union() {
74 | PlotFunction(4, [-25, 0.4, 25], [-25, 0.4, 0]);
75 | difference() {
76 | translate([-25, -1, 0]) cube([50, 2, 16]);
77 | translate([slit_pos, 0, 16/2]) cube([2, 3, 16+2], center=true);
78 | }
79 | PlotFunction(6, [-25, 0.4, 25], [0, 0.4, 30]);
80 | }
81 |
82 | }
83 | }
84 |
85 |
86 | module SelectFrom(n) { children(n); }
87 |
88 | module Demo(n=0) {
89 | demo_cnt = 5;
90 | demo_cnt_sqrt = floor(sqrt(demo_cnt));
91 | demo_order = [2, 3, 4, 1, 5];
92 | rotate_demos = 270;
93 | //rotate_demos = 180;
94 | if (n == 0) {
95 | for (i=[1:demo_cnt])
96 | translate([((i-1)%demo_cnt_sqrt)*60, floor((i-1)/demo_cnt_sqrt)*60, 0])
97 | rotate([0, 0, rotate_demos])
98 | if (i-1 < len(demo_order)) {
99 | DemoNumber(demo_order[i-1]);
100 | }
101 | else{
102 | DemoNumber(i);
103 | }
104 | }
105 | else {
106 | DemoNumber(n);
107 | }
108 | }
109 |
110 |
111 |
--------------------------------------------------------------------------------
/demo_plot_function.stl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rcolyer/plot-function/804086e16086548eb58a167fee7b45e63a4bdeb6/demo_plot_function.stl
--------------------------------------------------------------------------------
/images/demo_plot_function.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rcolyer/plot-function/804086e16086548eb58a167fee7b45e63a4bdeb6/images/demo_plot_function.png
--------------------------------------------------------------------------------
/plot_function.scad:
--------------------------------------------------------------------------------
1 | // Created in 2017 by Ryan A. Colyer.
2 | // This work is released with CC0 into the public domain.
3 | // https://creativecommons.org/publicdomain/zero/1.0/
4 | //
5 | // https://www.thingiverse.com/thing:2391851
6 |
7 |
8 | // Note: For use as a library this file must be included with "include",
9 | // not "use", so that it can access the functions defined in the including
10 | // scad file.
11 |
12 |
13 | //// Uncomment to try the usage examples:
14 |
15 | //// Gravity well
16 | // function Func1(x, y) =
17 | // let ( z = 30 - 10*10/(x*x+y*y) )
18 | // z < 1 ? 1 : z;
19 | //
20 | // translate([10, 0, 0])
21 | // PlotFunction(1, [-10, 0.4, 0], [-10, 0.4, 10]);
22 |
23 |
24 | //// A bowl
25 | // function PolarFunc2(r, a) = let(z = 23-sqrt(23*23-r*r)) (z < 2 ? 2 : z);
26 | //
27 | // difference() {
28 | // PlotPolarFunction(2, 20, 0.8);
29 | // translate([0, 0, -2]) PlotPolarFunction(2, 20.1, 0.8);
30 | // }
31 |
32 |
33 | //// A rose
34 | // function PolarFunc3(r, a) = (15+5*sin(r*10))*exp(-pow(r*cos(a)*cos(r*8)+r*sin(a)*sin(r*35),2)/300) + 1;
35 | //
36 | // PlotPolarFunction(3, 22, 0.4);
37 |
38 |
39 | //// A simple chalice
40 | // function AxialFunc1(z, ang) = 5*(cos(log(z/5+1)*360) + 2);
41 | // function AxialFunc2(z, ang) = AxialFunc1(z, ang) - 2;
42 | //
43 | // difference() {
44 | // PlotAxialFunction(1, [0, 0.4, 50], 180);
45 | // PlotAxialFunction(2, [2, 0.4, 51], 180);
46 | // }
47 |
48 |
49 | // Plots the numbered function Func1 through Func9, where FuncN is 1 through 9.
50 | // Each function is a function of x and y.
51 | // minx_stepx_maxx should be [minx, stepx, maxx], and likewise for y,
52 | // specifying the domain to be plotted.
53 | // To guarantee a properly manifold shape, the routine will only render
54 | // strictly positive values (z>0) of the defined function. Add an offset if
55 | // needed to achieve this.
56 | module PlotFunction(FuncN, minx_stepx_maxx, miny_stepy_maxy) {
57 | minx = minx_stepx_maxx[0];
58 | stepx = minx_stepx_maxx[1];
59 | maxx = minx_stepx_maxx[2] + 0.001*stepx;
60 | miny = miny_stepy_maxy[0];
61 | stepy = miny_stepy_maxy[1];
62 | maxy = miny_stepy_maxy[2] + 0.001*stepy;
63 | minplot = 0.0005*(stepx+stepy);
64 |
65 | pointarrays = concat(
66 | [concat( // Close miny edge of plot.
67 | [[maxx, miny-0.001*stepy, 0]],
68 | [[minx, miny-0.001*stepy, 0]],
69 | [
70 | for (x = [minx:stepx:maxx])
71 | [x, miny-0.001*stepy, 0.0001]
72 | ]
73 | )],
74 |
75 | [ for (y = [miny:stepy:maxy])
76 | concat(
77 | [[maxx, y, 0]],
78 | [[minx, y, 0]],
79 | [
80 | for (x = [minx:stepx:maxx]) let(
81 | z = CallFunc(x, y, FuncN),
82 | zchecked = z < minplot ? minplot : z
83 | )
84 | [x, y, zchecked]
85 | ]
86 | )
87 | ],
88 |
89 | [concat( // Close maxy edge of plot.
90 | [[maxx, maxy+0.001*stepy, 0]],
91 | [[minx, maxy+0.001*stepy, 0]],
92 | [
93 | for (x = [minx:stepx:maxx])
94 | [x, maxy+0.001*stepy, 0.0001]
95 | ]
96 | )]
97 | );
98 |
99 | PlotClosePoints(pointarrays);
100 | }
101 |
102 |
103 | // Plots the numbered function PolarFunc1 through PolarFunc9, where
104 | // PolarFuncN is 1 through 9. Each function is a function of radius and
105 | // angle.
106 | // max_r is the outer radius, and min_step is the smallest step size between
107 | // points.
108 | // To guarantee a properly manifold shape, the routine will only render
109 | // strictly positive values (z>0) of the defined function. Add an offset if
110 | // needed to achieve this.
111 | module PlotPolarFunction(PolarFuncN, max_r, min_step=-1) {
112 | num_circle_steps = (min_step <= 0) ? 360 :
113 | ceil((max_r * 2*PI / min_step) / 8)*8;
114 | ang_step = 360 / num_circle_steps;
115 | eff_minstep = (min_step <= 0) ? 2*PI*max_r/num_circle_steps : min_step;
116 | num_r_steps = ceil(max_r / eff_minstep);
117 | r_step = (max_r - 0.001*eff_minstep) / num_r_steps;
118 | minplot = 0.001*r_step;
119 |
120 | pointarrays = concat(
121 | [
122 | [ for (a = [0:ang_step:359.9999])
123 | [max_r * cos(a), max_r * sin(a), 0]
124 | ]
125 | ],
126 |
127 | [ for (r = [max_r:-r_step:0.000001*r_step])
128 | [ for (a = [0:ang_step:359.9999]) let(
129 | z = CallPolarFunc(r, a, PolarFuncN),
130 | zchecked = z < minplot ? minplot : z
131 | )
132 | [r * cos(a), r * sin(a), zchecked]
133 | ]
134 | ]
135 | );
136 |
137 | PlotClosePoints(pointarrays);
138 | }
139 |
140 |
141 | // Plots the numbered function AxialFunc1 through AxialFunc9, where
142 | // AxialFuncN is 1 through 9. Each function is a function of z-height and
143 | // angle, and returns the radius outward in the xy-plane.
144 | // max_r is the outer radius, and min_step is the smallest step size between
145 | // points.
146 | // minz_stepz_maxz should be [minz, stepz, maxz], and likewise for y,
147 | // specifying the domain to be plotted.
148 | // To guarantee a properly manifold shape, the routine will only render
149 | // strictly positive values (r>0) of the defined function. Add an offset if
150 | // needed to achieve this.
151 | module PlotAxialFunction(AxialFuncN, minz_stepz_maxz, num_circle_steps=360) {
152 | ang_step = 360 / num_circle_steps;
153 | minz = minz_stepz_maxz[0];
154 | stepz = minz_stepz_maxz[1];
155 | maxz = minz_stepz_maxz[2] + 0.001*stepz;
156 | minplot = 0.001*stepz;
157 |
158 | pointarrays = [
159 | for (z = [minz:stepz:maxz])
160 | [ for (ai = [0:num_circle_steps-1]) let(
161 | a = ai * ang_step,
162 | r = CallAxialFunc(z, a, AxialFuncN),
163 | rchecked = r < minplot ? minplot : r
164 | )
165 | [rchecked * cos(a), rchecked * sin(a), z]
166 | ]
167 |
168 | ];
169 |
170 | PlotClosePoints(pointarrays);
171 | }
172 |
173 |
174 | // Relays function calls to Func1 through Func9
175 | function CallFunc(x, y, n) =
176 | (n == 1) ? Func1(x, y) :
177 | (n == 2) ? Func2(x, y) :
178 | (n == 3) ? Func3(x, y) :
179 | (n == 4) ? Func4(x, y) :
180 | (n == 5) ? Func5(x, y) :
181 | (n == 6) ? Func6(x, y) :
182 | (n == 7) ? Func7(x, y) :
183 | (n == 8) ? Func8(x, y) :
184 | (n == 9) ? Func9(x, y) :
185 | FunctionNumberOutOfRange;
186 |
187 |
188 | // Relays function calls to PolarFunc1 through PolarFunc9
189 | function CallPolarFunc(r, ang, n) =
190 | (n == 1) ? PolarFunc1(r, ang) :
191 | (n == 2) ? PolarFunc2(r, ang) :
192 | (n == 3) ? PolarFunc3(r, ang) :
193 | (n == 4) ? PolarFunc4(r, ang) :
194 | (n == 5) ? PolarFunc5(r, ang) :
195 | (n == 6) ? PolarFunc6(r, ang) :
196 | (n == 7) ? PolarFunc7(r, ang) :
197 | (n == 8) ? PolarFunc8(r, ang) :
198 | (n == 9) ? PolarFunc9(r, ang) :
199 | PolarFunctionNumberOutOfRange;
200 |
201 |
202 | // Relays function calls to AxialFunc1 through AxialFunc9
203 | function CallAxialFunc(z, ang, n) =
204 | (n == 1) ? AxialFunc1(z, ang) :
205 | (n == 2) ? AxialFunc2(z, ang) :
206 | (n == 3) ? AxialFunc3(z, ang) :
207 | (n == 4) ? AxialFunc4(z, ang) :
208 | (n == 5) ? AxialFunc5(z, ang) :
209 | (n == 6) ? AxialFunc6(z, ang) :
210 | (n == 7) ? AxialFunc7(z, ang) :
211 | (n == 8) ? AxialFunc8(z, ang) :
212 | (n == 9) ? AxialFunc9(z, ang) :
213 | AxialFunctionNumberOutOfRange;
214 |
215 |
216 | function isfinite(x) = (!(x!=x)) && (x<(1/0)) && (x>(-1/0));
217 |
218 |
219 | // This generates a closed polyhedron from an array of arrays of points,
220 | // with each inner array tracing out one loop outlining the polyhedron.
221 | // pointarrays should contain an array of N arrays each of size P outlining a
222 | // closed manifold. The points must obey the right-hand rule. For example,
223 | // looking down, the P points in the inner arrays are counter-clockwise in a
224 | // loop, while the N point arrays increase in height. Points in each inner
225 | // array do not need to be equal height, but they usually should not meet or
226 | // cross the line segments from the adjacent points in the other arrays.
227 | // (N>=2, P>=3)
228 | // Core triangles:
229 | // [j][i], [j+1][i], [j+1][(i+1)%P]
230 | // [j][i], [j+1][(i+1)%P], [j][(i+1)%P]
231 | // Then triangles are formed in a loop with the middle point of the first
232 | // and last array.
233 | module PlotClosePoints(pointarrays) {
234 | function recurse_avg(arr, n=0, p=[0,0,0]) = (n>=len(arr)) ? p :
235 | recurse_avg(arr, n+1, p+(arr[n]-p)/(n+1));
236 |
237 | N = len(pointarrays);
238 | P = len(pointarrays[0]);
239 | NP = N*P;
240 | lastarr = pointarrays[N-1];
241 | midbot = recurse_avg(pointarrays[0]);
242 | midtop = recurse_avg(pointarrays[N-1]);
243 |
244 | faces_bot = [
245 | for (i=[0:P-1])
246 | [0,i+1,1+(i+1)%len(pointarrays[0])]
247 | ];
248 |
249 | loop_offset = 1;
250 | bot_len = loop_offset + P;
251 |
252 | faces_loop = [
253 | for (j=[0:N-2], i=[0:P-1], t=[0:1])
254 | [loop_offset, loop_offset, loop_offset] + (t==0 ?
255 | [j*P+i, (j+1)*P+i, (j+1)*P+(i+1)%P] :
256 | [j*P+i, (j+1)*P+(i+1)%P, j*P+(i+1)%P])
257 | ];
258 |
259 | top_offset = loop_offset + NP - P;
260 | midtop_offset = top_offset + P;
261 |
262 | faces_top = [
263 | for (i=[0:P-1])
264 | [midtop_offset,top_offset+(i+1)%P,top_offset+i]
265 | ];
266 |
267 | points = [
268 | for (i=[-1:NP])
269 | (i<0) ? midbot :
270 | ((i==NP) ? midtop :
271 | pointarrays[floor(i/P)][i%P])
272 | ];
273 | faces = concat(faces_bot, faces_loop, faces_top);
274 |
275 | polyhedron(points=points, faces=faces, convexity=8);
276 | }
277 |
278 |
279 |
--------------------------------------------------------------------------------