How do I pass a function as a parameter to an OpenSCAD module? - stl

Over the last few days I have become interested with the idea of using programming-language based software to create 3D models. One the languages I have been playing with is OpenSCAD, which has proven tremendously helpful in creating interesting shapes.
I am currently trying to create a flower with OpenSCAD, and I have encountered an issue that I have not been able to circumvent using the documentation or other resources I found online.
Here is the short form of the question:
Can I pass a function as a parameter to an OpenSCAD module?
If so, how? If not, why not and what can I do instead?
This brings me to the long form of the question with specifics to my situation:
I am trying to create petals using a linear extrusion of a 2D polar function, and intersecting that with a 3D function.
To do this, I am starting out with two very nice modules I found on http://spolearninglab.com/curriculum/lessonPlans/hacking/resources/software/3d/openscad/openscad_math.html. I do not claim to have written them in the first place.
First - 3D plotter by Dan Newman
/* 3Dplot.scad */
// 3dplot -- the 3d surface generator
// x_range -- 2-tuple [x_min, x_max], the minimum and maximum x values
// y_range -- 2-tuple [y_min, y_max], the minimum and maximum y values
// grid -- 2-tuple [grid_x, grid_y] indicating the number of grid cells along the x and y axes
// z_min -- Minimum expected z-value; used to bound the underside of the surface
// dims -- 2-tuple [x_length, y_length], the physical dimensions in millimeters
//Want to pass in z(x,y) as parameter
module 3dplot(x_range=[-10, +10], y_range=[-10,10], grid=[50,50], z_min=-5, dims=[80,80]){
dx = ( x_range[1] - x_range[0] ) / grid[0];
dy = ( y_range[1] - y_range[0] ) / grid[1];
// The translation moves the object so that its center is at (x,y)=(0,0)
// and the underside rests on the plane z=0
scale([dims[0]/(max(x_range[1],x_range[0])-min(x_range[0],x_range[1])),
dims[1]/(max(y_range[1],y_range[0])-min(y_range[0],y_range[1])),1])
translate([-(x_range[0]+x_range[1])/2, -(y_range[0]+y_range[1])/2, -z_min])
union()
{
for ( x = [x_range[0] : dx : x_range[1]] )
{
for ( y = [y_range[0] : dy : y_range[1]] )
{
polyhedron(points=[[x,y,z_min], [x+dx,y,z_min], [x,y,z(x,y)], [x+dx,y,z(x+dx,y)],
[x+dx,y+dy,z_min], [x+dx,y+dy,z(x+dx,y+dy)]],
faces=prism_faces_1);
polyhedron(points=[[x,y,z_min], [x,y,z(x,y)], [x,y+dy,z_min], [x+dx,y+dy,z_min],
[x,y+dy,z(x,y+dy)], [x+dx,y+dy,z(x+dx,y+dy)]],
faces=prism_faces_2);
}
}
}
}
Second - 2D Grapher
/* 2dgraphing.scad */
// function to convert degrees to radians
function d2r(theta) = theta*360/(2*pi);
// These functions are here to help get the slope of each segment, and use that to find points for a correctly oriented polygon
function diffx(x1, y1, x2, y2, th) = cos(atan((y2-y1)/(x2-x1)) + 90)*(th/2);
function diffy(x1, y1, x2, y2, th) = sin(atan((y2-y1)/(x2-x1)) + 90)*(th/2);
function point1(x1, y1, x2, y2, th) = [x1-diffx(x1, y1, x2, y2, th), y1-diffy(x1, y1, x2, y2, th)];
function point2(x1, y1, x2, y2, th) = [x2-diffx(x1, y1, x2, y2, th), y2-diffy(x1, y1, x2, y2, th)];
function point3(x1, y1, x2, y2, th) = [x2+diffx(x1, y1, x2, y2, th), y2+diffy(x1, y1, x2, y2, th)];
function point4(x1, y1, x2, y2, th) = [x1+diffx(x1, y1, x2, y2, th), y1+diffy(x1, y1, x2, y2, th)];
function polarX(theta) = cos(theta)*r(theta);
function polarY(theta) = sin(theta)*r(theta);
module nextPolygon(x1, y1, x2, y2, x3, y3, th) {
if((x2 > x1 && x2-diffx(x2, y2, x3, y3, th) < x2-diffx(x1, y1, x2, y2, th) || (x2 <= x1 && x2-diffx(x2, y2, x3, y3, th) > x2-diffx(x1, y1, x2, y2, th)))) {
polygon(
points = [
point1(x1, y1, x2, y2, th),
point2(x1, y1, x2, y2, th),
// This point connects this segment to the next
point4(x2, y2, x3, y3, th),
point3(x1, y1, x2, y2, th),
point4(x1, y1, x2, y2, th)
],
paths = [[0,1,2,3,4]]
);
}
else if((x2 > x1 && x2-diffx(x2, y2, x3, y3, th) > x2-diffx(x1, y1, x2, y2, th) || (x2 <= x1 && x2-diffx(x2, y2, x3, y3, th) < x2-diffx(x1, y1, x2, y2, th)))) {
polygon(
points = [
point1(x1, y1, x2, y2, th),
point2(x1, y1, x2, y2, th),
// This point connects this segment to the next
point1(x2, y2, x3, y3, th),
point3(x1, y1, x2, y2, th),
point4(x1, y1, x2, y2, th)
],
paths = [[0,1,2,3,4]]
);
}
else {
polygon(
points = [
point1(x1, y1, x2, y2, th),
point2(x1, y1, x2, y2, th),
point3(x1, y1, x2, y2, th),
point4(x1, y1, x2, y2, th)
],
paths = [[0,1,2,3]]
);
}
}
module 2dgraph(bounds=[-10,10], th=2, steps=10, polar=false, parametric=false) {
step = (bounds[1]-bounds[0])/steps;
union() {
for(i = [bounds[0]:step:bounds[1]-step]) {
if(polar) {
nextPolygon(polarX(i), polarY(i), polarX(i+step), polarY(i+step), polarX(i+2*step), polarY(i+2*step), th);
}
else if(parametric) {
nextPolygon(x(i), y(i), x(i+step), y(i+step), x(i+2*step), y(i+2*step), th);
}
else {
nextPolygon(i, f(i), i+step, f(i+step), i+2*step, f(i+2*step), th);
}
}
}
}
My wrapper code:
include <2dgraphing.scad>;
include <3dplot.scad>;
function z(x,y) = pow(x,2)+pow(y,2); //function used in 3dplot
function r(theta) = cos(4*theta); //function used in 2dgraph
module Petals () {
difference () {
union () { //everything to add
intersection () {
3dplot([-4,4],[-4,4],[50,50],-2.5);
scale([20, 20, 20]) linear_extrude(height=0.35)
2dgraph([0, 720], 0.1, steps=160, polar=true);
}
}
union () { //everything to subtract
}
}
}
Petals();
And all is well and dandy with the world when I render the world's most computationally expensive petals.
[Here I would post an image but since this is my first post I do not have the pre-requisite 10 reputation points]
However, now I want to subtract the excess from the bottom of the petals. So I could use a 3D plot with a steeper function and a lower starting point and subtract that from the original 3D plot.
So in the same program I want to use two different functions for two different uses of the 3Dplot module.
I tried modifying 3dplot and my code to do so:
Modified 3dplot:
module 3dplot(x_range=[-10, +10], y_range=[-10,10], grid=[50,50], z_min=-5, dims=[80,80], input_function)
{
dx = ( x_range[1] - x_range[0] ) / grid[0];
dy = ( y_range[1] - y_range[0] ) / grid[1];
// The translation moves the object so that its center is at (x,y)=(0,0)
// and the underside rests on the plane z=0
scale([dims[0]/(max(x_range[1],x_range[0])-min(x_range[0],x_range[1])),
dims[1]/(max(y_range[1],y_range[0])-min(y_range[0],y_range[1])),1])
translate([-(x_range[0]+x_range[1])/2, -(y_range[0]+y_range[1])/2, -z_min])
union()
{
for ( x = [x_range[0] : dx : x_range[1]] )
{
for ( y = [y_range[0] : dy : y_range[1]] )
{
polyhedron(points=[[x,y,z_min], [x+dx,y,z_min], [x,y,input_function(x,y)], [x+dx,y,input_function(x+dx,y)],
[x+dx,y+dy,z_min], [x+dx,y+dy,input_function(x+dx,y+dy)]],
faces=prism_faces_1);
polyhedron(points=[[x,y,z_min], [x,y,input_function(x,y)], [x,y+dy,z_min], [x+dx,y+dy,z_min],
[x,y+dy,input_function(x,y+dy)], [x+dx,y+dy,input_function(x+dx,y+dy)]],
faces=prism_faces_2);
}
}
}
}
Modified my code:
include <2dgraphing.scad>;
include <3dplot.scad>;
function z1(x,y) = pow(x,2)+pow(y,2); //function used in 3dplot
function z2(x,y) = pow(pow(x,2)+pow(y,2),1.5)-1; //function to be subtracted out
function r(theta) = cos(4*theta); //function used in 2dgraph
module Petals () {
difference () {
union () { //everything to add
intersection () {
3dplot([-4,4],[-4,4],[50,50],-2.5);
scale([20, 20, 20]) linear_extrude(height=0.35)
2dgraph([0, 720], 0.1, steps=160, polar=true, input_function=z1);
}
}
union () { //everything to subtract
3dplot([-4,4],[-4,4],[50,50],-2.5,input_function=z2);
}
}
}
Petals();
I received the following error:
WARNING: Ignoring unknown function 'input_function'.
So how do I go about making passing in the function as a parameter?
I have not written in any functional language before this, but it is my understanding from the OpenSCAD User Manual that "Since version 2015.03, Variables can now be assigned in any scope." So I should be able to change the value of input_function for each run of 3dplot, just like the variables within 3dplot. Am I interpreting this incorrectly?
And as an optional side question: is there a clear way with OpenSCAD to achieve my current objectives without creating a massive computational load during the rendering process?
I have spent a decent enough amount of time trying to solve this problem that I am posting this lengthy question, and I apologize if I have glazed over a simple existing solution. I very much appreciate anyone willing to help.

Passing a function as parameter is currently not possible. Also generating a huge number of small objects (e.g. the polyhedrons in the 3dplot module) will make the model rendering very slow. For this
specific use-case there are other options to generate the model.
The new list generation features available with the latest OpenSCAD versions allows to generate a single polyhedron based on a function.
See the 3d-functions.scad in the demo repository. This plots the
function f(x, y).

You can use my function-parsing library and pass relatively simple functions either as text strings or as pre-compiled optimized representations. The library supports most of the built-in OpenSCAD mathematical and other functions, but you cannot call other user-defined functions or do recursion or list generation inside the function.

Related

What is the correct way to use DolphinDB function tmbeta?

I want to run the script in DolphinDB:
T = 1..25
X = 2..26
Y1 = 3..27
Y2 = 4..28
y_matrix = matrix(table(Y1 as y1, Y2 as y2))
y_matrix.rename!(1..25,`y1`y2).setIndexedMatrix!()
x_series = indexedSeries(1..25,X)
tmbeta(T=1..25, Y=y_matrix[0], X=x_series[0],window=10)
It returns an error: tmbeta(1 .. 25, y_matrix[0], x_series[0], 10) => Usage: tmbeta(T, X, Y, window). X must be a vector with the same length as T.
I’d like to calculate the coefficient estimate of an ordinary-least-squares regression of Y on X. What is the correct usage of tmbeta?
For function tmbeta, the parameter X (y_matrix[0] in the script) must be a vector. The error is caused by the incorrect data type of y_matrix[0].
typestr(y_matrix[0])
FAST INT MATRIX
As the y_matrix is an indexed matrix in DolphinDB, you can use mbeta instead of tmbeta as no index (T) is needed.
mbeta(X=x_series,Y=y_matrix,window=10)
The correct way to apply function tmbeta is:
y1_matrix=matrix(table(Y1 as y1, Y2 as y2))
tmbeta(T,X=x_series[0],Y=y1_matrix[0],window=10)
Here y1_matrix[0] is a vector:
typestr(y1_matrix[0])
FAST INT VECTOR

Convert Yolo output to a real-world coordinate system

We have detected objects on UAV data using Yolo v5 and obtained bounding box coordinates (x1,y1,x2,y2) in the format relative to the origin of the satellite data. The data looks like this and is returned as a tab-delimited text file.
[ 7953 11025 7978 11052]
[16777 10928 16817 10970]
[15670 10591 15685 10607]
The results are accompanied by a PNG and the PGW (world file) reads like this:
0.1617903116883119
0
0
-0.1617903116883119
655854.20159515587147325
2716038.70000312989577651
How can the bounding boxes be converted into real-world global projection EPSG:4328 usable in GIS? Any hints towards a python script are much appreciated.
go in Detect.py and set gn = 1 and it'll get you the un-normalized coordinates. Attaching the screenshot below for you reference
I wrote this short function to convert the yolo detections to real-world polygons. The yolo detections.txt needs to be read without the [].
# function to return polygon
def bbox(x1, y1, x2, y2):
# world file content
# Line 1: A: x-component of the pixel width (x-scale)
xscale = 0.1617903116883119
# Line 2: D: y-component of the pixel width (y-skew)
yskew = 0
# Line 3: B: x-component of the pixel height (x-skew)
xskew = 0
# Line 4: E: y-component of the pixel height (y-scale), typically negative
yscale = -0.1617903116883119
# Line 5: C: x-coordinate of the center of the original image's upper left pixel transformed to the map
xpos = 655854.20159515587147325
# Line 6: F: y-coordinate of the center of the original image's upper left pixel transformed to the map
ypos = 2716038.70000312989577651
X_proj = xpos + (xscale * x1) + (xskew * y1)
Y_proj = ypos + (yscale * y1) + (yskew * x1)
X1_proj = xpos + (xscale * x2) + (xskew * y2)
Y1_proj = ypos + (yscale * y2) + (yskew * x2)
return Polygon([[X_proj, Y_proj],
[X1_proj, Y_proj],
[X1_proj, Y1_proj],
[X_proj, Y1_proj]])
outGDF = gpd.GeoDataFrame(geometry = dataset.apply(lambda g: bbox(int(g[0]),int(g[1]),int(g[2]),int(g[3])),axis=1),crs = {'init':'epsg:32638'})

Calling Octave interpolation function within function body

I'm trying to wrap Octave interpolation function in a function body,
function FUN = inter(p);
FUN = interpn (x1, x2, x3, x4, x5, A, p(1), p(2), p(3), p(4), p(5), "spline");
end
The reason why I'm doing this is because I'm using a package which function needs a string name function which would be in this case packageFunction("inter", argument1);
The issue is calling now for instance like,
disp("value = "), inter([10 2 4 3 4])
doesn't work; Doesn't see the vectors error: 'x1' undefined ,
Of course the vectors xi and matrix A are defined above the function body. Would appreciate advice on this,
thanks, Damir
------------- in file example1.m
[a b c] = fminuit('gaussian','mnplot',[10 166 33],[x;y;dy])
------------- in file gaussian.m
function f = gaussian(par,data);
%theoretical function
f = par(1)/(sqrt(2*pi)*par(3)) * exp(-.5*((data(1,:)-
par(2))./par(3)).^2);
if (size(data,1)==2), %chi-square, error = 1
f = sum((data(2,:) - f).^2);
elseif (size(data,1)>2), %chi-square, error = 3rd row of data
f = sum(((data(2,:) - f)./data(3,:)).^2);
end
Given you are using an old function that requires a string as the function, the first solution below will not work. This is, however, the right way to do it. Changing the old function to use function handles instead of strings would be my preferred solution. However, you can also use an alternative solution further down below, which uses global variables. This is not the recommended approach (we should strive to avoid globals), but will solve your near-term problems.
Correct approach: use an anonymous function
You should use an anonymous function, these can capture variables when they're defined:
inter = #(p)interpn (x1, x2, x3, x4, x5, A, p(1), p(2), p(3), p(4), p(5), "spline");
Now inter(p) works just as if inter had been declared as a normal function. But the values of x1, x2, etc as they were defined when inter was defined will be stored inside inter.
As stated, the function you pass inter to must be written to accept function handles.
Bad, quick solution: use global variables
First, create a file inter.m with the following contents:
function FUN = inter(p);
global x1 x2 x3 x4 x5 A
FUN = interpn (x1, x2, x3, x4, x5, A, p(1), p(2), p(3), p(4), p(5), "spline");
end
Next, in your function of script that calls inter, again declare the global variables (currently MATLAB warns that you should declare them as globals before giving them a value, in future versions this will be required):
global x1 x2 x3 x4 x5 A
x1 = ...
x2 = ...
% etc
inter([10 2 4 3 4])
% or:
fminuit('inter',...)

Plotting points using symbolic Octave

I have a symbolic function in Octave (with symbolic package), e.g.:
syms x;
syms y;
f = x.^2 + y.^2 - sqrt(12);
Which function is used to plot this ? Also, is it possible to plot only specific points, like x,y(2,2) ? Ty !
I don't have that package, so I cannot test, but according to the Internet the basic principle should be:
syms x;
syms y;
f = x.^2 + y.^2 - sqrt(12);
x1=-2:.0001:2;
y1=-2:.0001:2;
# plot3(x1, y1, f(x1,y1)); ## apparently errors out
scatter3(x1, y1, subs(f, {x, y}, {x1, y1}));

(SAS 9.4) Is any functions in SAS which can extract residual from regression equation?

I need help to know a function can extract residual from regression equation.
I need that function to make 2-stage credit model. I want to extract a residual from first stage model(regression) and apply the residual to second stage model(y value).
It will be very helpful if there is proper function in SAS 9.4.
thank you
Look at the documentation around PROC REG.
proc reg data=inData;
model y = x1 x2 x3;
output out=ouData r=resid;
run;
quit;
This takes data from the INDATA data set, regresses Y on X1, X2, and X3, and outputs the residuals in OUTDATA.
If you want to get fancy, you can do it 2-stage least squares with proc model.
proc model data=have;
exo x1 x2 x3;
endo y1 y2;
y1 = b1 + b2*y2 + b3*x1 + b4*x2;
y2 = b5 + b6*y1 + b7*x3;
fit y1 y2 / 2sls;
instruments _exog_;
run;