如何将函数作为参数传递给OpenSCAD模块?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何将函数作为参数传递给OpenSCAD模块?相关的知识,希望对你有一定的参考价值。
在过去的几天里,我对使用基于编程语言的软件创建3D模型的想法感兴趣。我所使用的语言之一是OpenSCAD,它已经证明在创建有趣的形状方面非常有用。
我目前正在尝试使用OpenSCAD创建一个花,我遇到了一个问题,我无法使用我在网上找到的文档或其他资源来规避这个问题。
以下是问题的简短形式:
我可以将函数作为参数传递给OpenSCAD模块吗?
如果是这样,怎么样?如果没有,为什么不呢,我该怎么做呢?
这让我想到了问题的长篇大论,并详细说明了我的情况:
我正在尝试使用2D极性函数的线性挤出创建花瓣,并将其与3D函数相交。
为此,我开始在http://spolearninglab.com/curriculum/lessonPlans/hacking/resources/software/3d/openscad/openscad_math.html找到两个非常好的模块。我没有声称首先写了它们。
第一 - Dan Newman的3D绘图仪/ * 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);
}
}
}
}
第二 - 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);
}
}
}
}
我的包装代码:
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();
当我渲染世界上计算量最大的花瓣时,一切都很好,对世界很好。
[这里我会张贴一张图片,但由于这是我的第一篇文章,我没有前提条件10点声望点]
但是,现在我想减去花瓣底部的多余部分。因此,我可以使用具有更陡峭功能和更低起点的3D绘图,并从原始3D绘图中减去该绘图。
所以在同一个程序中,我想为3Dplot模块的两种不同用途使用两种不同的函数。
我尝试修改3dplot和我的代码来执行此操作:
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);
}
}
}
}
修改了我的代码:
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();
我收到以下错误:警告:忽略未知函数'input_function'。
那么我该如何将函数作为参数传递呢?
在此之前我没有用任何函数式语言编写,但是我从OpenSCAD用户手册中了解到“自2015.03版以来,现在可以在任何范围内分配变量”。所以我应该能够为3dplot的每次运行更改input_function的值,就像3dplot中的变量一样。我不正确地解释这个吗?
作为一个可选的侧面问题:OpenSCAD是否有明确的方法来实现我当前的目标,而不会在渲染过程中产生大量的计算负荷?
我花了足够的时间试图解决这个问题,我发布了这个冗长的问题,如果我已经解决了一个简单的现有解决方案,我会道歉。我非常感谢愿意提供帮助的人。
目前无法将函数作为参数传递。同时生成大量小对象(例如3dplot模块中的多面体)将使模型渲染非常慢。对于此特定用例,还有其他选项可用于生成模型。
最新的OpenSCAD版本提供的新列表生成功能允许基于函数生成单个多面体。
请参阅演示存储库中的3d-functions.scad。这个plots函数f(x,y)。
您可以使用我的function-parsing library并将相对简单的函数作为文本字符串或预编译的优化表示形式传递。该库支持大多数内置的OpenSCAD数学和其他函数,但您不能调用其他用户定义的函数或在函数内部执行递归或列表生成。
以上是关于如何将函数作为参数传递给OpenSCAD模块?的主要内容,如果未能解决你的问题,请参考以下文章