OpenSCAD 使用数学公式和基本几何创建模型

在 OpenSCAD 中进行数学计算

到目前为止,您已经了解到 OpenSCAD 变量在整个脚本执行过程中只能保存一个值,即分配给它们的最后一个值。您还了解到 OpenSCAD 变量的常见用途是提供模型的参数化。在这种情况下,每个参数化模型都会有一些自变量,您可以更改其值以调整该模型。这些变量通常直接赋值,如下例所示。


示例

…
wheel_diameter = 12;
…
body_length = 70;
…
wheelbase = 40;
…
// etc.
…

您已经看过几次但没有明确提及的另一件事是使用脚本中的变量和硬编码值执行数学运算的能力。这方面的一个例子是实现汽车的轴距。回想一下,汽车的车轴和车轮沿 X 轴平移并远离原点轴距值的一半。由于在这种情况下,轴距是一个已经在脚本中定义的变量,您可以通过将轴距变量除以 2 来计算单位数量。对轨道变量进行了类似的操作,以放置汽车的左右轮。回想一下,左轮和右轮沿 Y 轴平移并远离原点,距离轨道值的一半。


axle_with_wheelset.scad

use <vehicle_parts.scad>
$fa = 1;
$fs = 0.4;

wheelbase = 40;
track = 35;

translate([-wheelbase/2, track/2])
    simple_wheel();
translate([-wheelbase/2, -track/2])
    simple_wheel();
translate([-wheelbase/2, 0, 0])
    axle(track=track);

Axle with wheelset.jpg

加法、减法、乘法和除法在 OpenSCAD 中用符号 +、-、* 和 / 表示。除了这些基本运算之外,还有一些额外的数学运算在构建更复杂的模型时很有用。这方面的两个示例是用于定义汽车圆形图案的余弦和正弦函数。具体来说,您使用 cosine 和 sine 函数将每辆车的极坐标转换为笛卡尔坐标,以便将其平移到适当的位置。cheatsheet里面找到其他函数[1]


circular_pattern_of_cars.scad

…
r = 140; // pattern radius
n = 12; // number of cars
step = 360/n;
for (i=[0:step:359]) {
    angle = i;
    dx = r*cos(angle);
    dy = r*sin(angle);
    translate([dx,dy,0])
        rotate([0,0,angle])
        car();
}
…

Circular pattern of cars.jpg 在上述情况下,您不仅在脚本中使用了可用的数学运算,而且还定义了两个附加变量 dx 和 dy 来存储计算结果,以提高脚本的可读性。这也可以在您的汽车模型中完成。以以下车型为例。

car.scad

use <vehicle_parts.scad>
$fa = 1;
$fs = 0.4;

wheelbase = 40;
track = 35;

// Body
body();
// Front left wheel 
translate([-wheelbase/2,-track/2,0])
    simple_wheel();
 // Front right wheel 
translate([-wheelbase/2,track/2,0])
    simple_wheel();
// Rear left wheel 
translate([wheelbase/2,-track/2,0])
    simple_wheel();
// Rear right wheel 
translate([wheelbase/2,track/2,0])
    simple_wheel();
// Front axle 
translate([-wheelbase/2,0,0])
    axle(track=track); 
// Rear axle 
translate([wheelbase/2,0,0])
    axle(track=track);

在上述模型中,数学运算用于计算每个车轮和车轴沿 X 和 Y 轴所需的平移量。

Car model.jpg


使用多边形图元创建任何 2D 对象

除了圆形和方形 2D 基元之外,还有另一个基元可以让您设计几乎任何 2D 对象。这是多边形图元,它允许您通过提供包含其点坐标的列表来定义 2D 对象。假设您要设计以下 2D 零件。

Profile 1 drawing.jpg

在不使用多边形图元的情况下设计该零件的一种方法是从与该零件的外部尺寸相对应的正方形开始,然后从其右上角减去正确旋转和平移的正方形。计算适当的旋转角度和平移量将是一项耗时的任务。此外,不可能对更复杂的对象遵循这种策略。相反,您可以通过以下方式使用多边形图元创建此对象。


profile_1_polygon.scad

p0 = [0, 0];
p1 = [0, 30];
p2 = [15, 30];
p3 = [35, 20];
p4 = [35, 0];
points = [p0, p1, p2, p3, p4];
polygon(points);

Profile 1 polygon.jpg 关于polygon的使用,您应该注意一些事项。polygon使用点列表作为输入。点或顶点使用 X 和 Y 坐标对表示,并按顺序提供。定义列表时,您可以从您喜欢的任何顶点开始,您可以按顺时针或逆时针顺序遍历它们。在上面的示例中,第一个顶点位于原点 (0,0),而其余顶点按顺时针方向列出。所有顶点(X 和 Y 坐标对)p0、p1、...、p4 都放置在名为 points 的列表中。然后将此列表传递给多边形命令以创建相应的对象。

无论变量只有一个值还是值列表,您都可以使用 echo 命令在控制台上打印其内容。 控制台中的输出为:[[0, 0], [0, 30], [15, 30], [35, 20], [35, 0]]

不需要单独命名每个点(p0、p1、...),但建议更好地跟踪您的设计。您也可以直接定义要传递给多边形命令的点列表。

示例

…
points = [[0, 0], [0, 30], [15, 30], [35, 20], [35, 0]];
…

您还可以根据给定的尺寸参数化点坐标的定义,这将使您能够快速修改对象的尺寸。这可以通过为每个给定维度引入一个变量并使用适当的数学表达式定义点的坐标来实现。


profile_1_polygon_parametric.scad

// Given dimensions
d1 = 15;
d2 = 20;
h1 = 20;
h2 = 10;
// Points
p0 = [0, 0];
p1 = [0, h1 + h2];
p2 = [d1, h1 + h2];
p3 = [d1 + d2, h1];
p4 = [d1 + d2, 0];
points = [p0, p1, p2, p3, p4];
// Polygon
polygon(points);

新的小车

是时候使用你的新技能来创造赛车的车身了!

Racing car.jpg

使用多边形命令创建上述车身。为此,您必须定义设计的每个点,将它们全部添加到列表中并将此列表传递给多边形命令。每个点的坐标的定义应该是相对于给定尺寸的参数化。请记住,为了做到这一点,您必须为每个给定尺寸定义一个变量,并使用适当的数学表达式从这些变量中计算每个点的坐标。您应该将创建的 2D 轮廓拉伸到 14 个单位的高度。 1360px-Racing car body drawing.jpg

racing_car_body.scad

// model parameters
d1=30;
d2=20;
d3=20;
d4=10;
d5=20;

w1=15;
w2=45;
w3=25;

h=14;

// right side points
p0 = [0, w1/2];
p1 = [d1, w1/2];
p2 = [d1 + d2, w2/2];
p3 = [d1 + d2 + d3, w2/2];
p4 = [d1 + d2 + d3 + d4, w3/2];
p5 = [d1 + d2 + d3 + d4 + d5, w3/2];

// left side points
p6 = [d1 + d2 + d3 + d4 + d5, -w3/2];
p7 = [d1 + d2 + d3 + d4, -w3/2];
p8 = [d1 + d2 + d3, -w2/2];
p9 = [d1 + d2, -w2/2];
p10 = [d1, -w1/2];
p11 = [0, -w1/2];

// all points
points = [p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11];

// extruded body profile
linear_extrude(height=h)
    polygon(points);


示例

尝试通过将剩余的对象添加到上述车身来完成赛车设计。

use <vehicle_parts.scad>

$fa = 1;
$fs = 0.4;

// model parameters
d1=30;
d2=20;
d3=20;
d4=10;
d5=20;

w1=15;
w2=45;
w3=25;

h=14;
track=40;

// distances to lengths
l1 = d1;
l2 = d1 + d2;
l3 = d1 + d2 + d3;
l4 = d1 + d2 + d3 + d4;
l5 = d1 + d2 + d3 + d4 + d5;

// right side points
p0 = [0, w1/2];
p1 = [l1, w1/2];
p2 = [l2, w2/2];
p3 = [l3, w2/2];
p4 = [l4, w3/2];
p5 = [l5, w3/2];

// left side points
p6 = [l5, -w3/2];
p7 = [l4, -w3/2];
p8 = [l3, -w2/2];
p9 = [l2, -w2/2];
p10 = [l1, -w1/2];
p11 = [0, -w1/2];

// all points
points = [p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11];

// extruded body profile
linear_extrude(height=h)
    polygon(points);

// canopy
translate([d1+d2+d3/2,0,h])
    resize([d2+d3+d4,w2/2,w2/2])
    sphere(d=w2/2);

// axles
l_front_axle = d1/2;
l_rear_axle = d1 + d2 + d3 + d4 + d5/2;
half_track = track/2;

translate([l_front_axle,0,h/2])
    axle(track=track);
translate([l_rear_axle,0,h/2])
    axle(track=track);

// wheels
translate([l_front_axle,half_track,h/2])
    simple_wheel(wheel_width=10);
translate([l_front_axle,-half_track,h/2])
    simple_wheel(wheel_width=10);

translate([l_rear_axle,half_track,h/2])
    simple_wheel(wheel_width=10);
translate([l_rear_axle,-half_track,h/2])
    simple_wheel(wheel_width=10);

Racing car.jpg

使用多边形和数学函数创建更复杂的对象

从上面的例子很明显,多边形基元打开了创建对象的可能性,而不是仅使用基本的 2D 或 3D 基元几乎不可能。在这些示例中,您通过根据给定设计定义其点的坐标来创建自定义 2D 轮廓。但是,要释放多边形命令的真正威力,并创建更复杂和令人印象深刻的设计,您必须使用数学以编程方式定义轮廓的点。这是因为单独定义每个点不能扩展到设计平滑非方形轮廓所需的数百个点。这方面的一个例子是下面的心脏。您可以手动定义创建它所需的点吗?不可能。

Heart model.jpg


该模型不是手动定义每个点,这实际上是不可能的,而是使用以下参数方程以编程方式定义。


x = 16⋅sin⁡(t)3
y = 13⋅cos⁡(t) − 5⋅cos⁡(2⋅t) − 2⋅cos⁡(3⋅t) − cos⁡(4⋅t)

当 t 变量的范围涵盖从 0 到 360 度的值时,上述等式给出了心脏轮廓的 X 和 Y 坐标,从顶部中间点开始并沿顺时针方向移动。使用上述等式,可以通过以下方式生成包含每个点坐标的列表。

示例

points = [ for (t=[0:step:359.999]) [16*pow(sin(t),3), 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)]];

关于列表生成的语法,您应该注意一些事项。首先输入将存储列表的变量名称。然后是等号(就像在每个变量赋值中一样)和一对方括号。在这对方括号内,首先输入的是关键字 for。在关键字 for 后面跟着一对括号,其中定义了相应变量将采用的连续值。此变量类似于 for 循环中遇到的 for 循环变量。生成的列表将具有的元素数量等于该变量将采用的值的数量。对于此变量采用的每个值,都定义了列表的一个元素。列表的每个元素将是什么,在右括号之后指定。在这种情况下,生成列表的每个元素本身就是一个具有两个元素的列表,每个元素对应对应点的每个坐标。t 变量从 0 到 360,这是产生整个心脏轮廓所需的范围。由于多边形不应包含重复点,因此使用 359.999 而不是 360。通过为 step 变量选择更小或更大的值,可以控制将创建的点的数量。具体来说,为了创建 n 个点,需要按以下方式定义步长变量。由于多边形不应包含重复点,因此使用 359.999 而不是 360。通过为 step 变量选择更小或更大的值,可以控制将创建的点的数量。具体来说,为了创建 n 个点,需要按以下方式定义步长变量。由于多边形不应包含重复点,因此使用 359.999 而不是 360。通过为 step 变量选择更小或更大的值,可以控制将创建的点的数量。具体来说,为了创建 n 个点,需要按以下方式定义步长变量。


示例

step = 360/n;

将所有这些放在一起,可以使用以下脚本创建心脏。


heart.scad

n = 500;
h = 10;
step = 360/n;
points = [ for (t=[0:step:359.999]) [16*pow(sin(t),3), 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)]];
linear_extrude(height=h)
    polygon(points);

可以看到,使用 500 点轮廓的分辨率非常好。

制作赛车尾翼

您将把您的新技能付诸实践,为您的赛车制造空气动力学扰流板!

Racing car with spoiler.jpg

您将为扰流板使用对称的 4 位数 NACA 00xx 翼型。这种翼型在给定点 x 上的半厚度由以下公式给出: 上式中,x 是沿弦的位置,0 对应前缘,1 对应后缘,而 t 是翼型的最大厚度,以弦的百分比表示。将 t 乘以 100 得到 NACA 4 位数面额的最后两位数。

创建一个名为 naca_half_thickness 的函数。该函数应该有两个输入参数:x 和 t。给定 x 和 t,该函数应返回相应 NACA 翼型的一半厚度。x 和 t 输入参数不应有任何默认值。

naca_airfoil_module.scad

…
function naca_half_thickness(x,t) = 5*t*(0.2969*sqrt(x) - 0.1260*x - 0.3516*pow(x,2) + 0.2843*pow(x,3) - 0.1015*pow(x,4));
…

示例

创建一个名为 naca_top_coordinates 的函数。此函数应返回翼型上半部分的 X 和 Y 坐标列表。第一个点应该对应于前沿,而最后一个点应该对应于后沿。 该函数应该有两个输入参数:t 和 n。参数 t 应该对应于翼型的最大厚度,而参数 n 应该对应于创建点的数量。点列表应使用适当的列表生成命令生成。您将需要调用 naca_half_thickness 函数。

…
function naca_top_coordinates(t,n) = [ for (x=[0:1/(n-1):1]) [x, naca_half_thickness(x,t)]];
…

示例

创建一个名为 naca_bottom_coordinates 的类似函数,该函数返回翼型下半部分的点列表。这次应该以相反的顺序给出分数。第一个点应对应于后缘,而最后一个点应对应于前缘。这样做是为了当连接从 naca_top_coordinates 和 naca_bottom_coordinates 函数生成的列表时,翼型的所有点都从前缘开始按顺时针方向定义,从而使结果列表适合与多边形命令一起使用。

…
function naca_bottom_coordinates(t,n) = [ for (x=[1:-1/(n-1):0]) [x, - naca_half_thickness(x,t)]];
…


naca_airfoil_module.scad

创建一个名为 naca_coordinates 的函数,用于连接两个点列表。您可以使用 OpenSCAD 的内置功能“concat”将列表连接在一起。将两个列表作为输入传递给 concat 以将它们连接在一起。

…
function naca_coordinates(t,n) = concat(naca_top_coordinates(t,n), naca_bottom_coordinates(t,n));
…

Small airfoil polygon.jpg


small_airfoil_polygon.scad

尝试使用 naca_coordinates 函数创建一个列表,其中包含最大厚度为 0.12 且每半边有 300 个点的翼型点。该列表应存储在名为 points 的变量中。点变量应传递给多边形命令以创建翼型 2D 轮廓。

…
points = naca_coordinates(t,n);
polygon(points);
…


示例

上述翼型的弦为1个单位。您可以使用适当的比例命令来放大机翼吗?所需的和弦应该在一个名为 chord 的变量上定义,并且缩放命令应该与这个变量相关。创建一个具有 20 个单位的弦的翼型。

…
chord = 20;
points = naca_coordinates(t=0.12,n=300);
scale([chord,chord,1])
    polygon(points);
…

Scaled airfoil polygon.jpg


naca_airfoil_module.scad

将上面的脚本变成一个名为 naca_airfoil 的模块。该模块应具有三个输入参数,chord、t 和 n。任何输入参数都不应该有默认值。

module naca_airfoil(chord,t,n) {
    points = naca_coordinates(t,n);
    scale([chord,chord,1])
        polygon(points);
}

现在,要从翼型中创建机翼,您所要做的就是在 2D 翼型轮廓上应用 linear_extrude 命令。创建一个名为 naca_wing 的模块来执行此操作。与 naca_airfoil 模块相比,naca_wing 模块应该有两个额外的输入参数,跨度和中心。span 参数应对应于拉伸的高度,而 center 参数应指示拉伸是仅沿 Z 轴的正方向执行还是沿两个方向执行。span 参数不应有默认值,而 center 参数的默认值应为 false。您可以使用 naca_wing 模块创建以下机翼吗?下面的机翼跨度为50个单位,而机翼翼型的弦长为20个单位,最大厚度为0.12,每半边有500个点。

spoiler_wing.scad

…
module naca_wing(span,chord,t,n,center=false) {
    linear_extrude(height=span,center=center) {
        naca_airfoil(chord,t,n);
    }
}
…
rotate([90,0,0])
    naca_wing(span=50,chord=20,t=0.12,n=500,center=true);
…

Spoiler wing.jpg

使用 naca_wing 模块在前面的示例中添加两个较小的垂直机翼,以创建小车的扰流板。较小的机翼应该有 15 个单位的跨度和 15 个单位的弦长。 Spoiler model.jpg

spoiler.scad

…
rotate([90,0,0])
    naca_wing(span=50,chord=20,t=0.12,n=500,center=true);
translate([0,10,-15])
    naca_wing(span=15,chord=15,t=0.12,n=500);
translate([0,-10,-15])
    naca_wing(span=15,chord=15,t=0.12,n=500);
…

在赛车设计上添加上述扰流板即可完成。 Racing car with spoiler.jpg


racing_car_with_spoiler.scad

use <vehicle_parts.scad>

$fa = 1;
$fs = 0.4;
// model parameters
d1=30;
d2=20;
d3=20;
d4=10;
d5=20;

w1=15;
w2=45;
w3=25;

h=14;
track=40;

// distances to lengths
l1 = d1;
l2 = d1 + d2;
l3 = d1 + d2 + d3;
l4 = d1 + d2 + d3 + d4;
l5 = d1 + d2 + d3 + d4 + d5;

// right side points
p0 = [0, w1/2];
p1 = [l1, w1/2];
p2 = [l2, w2/2];
p3 = [l3, w2/2];
p4 = [l4, w3/2];
p5 = [l5, w3/2];

// left side points
p6 = [l5, -w3/2];
p7 = [l4, -w3/2];
p8 = [l3, -w2/2];
p9 = [l2, -w2/2];
p10 = [l1, -w1/2];
p11 = [0, -w1/2];

// all points
points = [p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11];

// extruded body profile
linear_extrude(height=h)
    polygon(points);

// canopy
translate([d1+d2+d3/2,0,h])
    resize([d2+d3+d4,w2/2,w2/2])
    sphere(d=w2/2);

// axles
l_front_axle = d1/2;
l_rear_axle = d1 + d2 + d3 + d4 + d5/2;
half_track = track/2;

translate([l_front_axle,0,h/2])
    axle(track=track);
translate([l_rear_axle,0,h/2])
    axle(track=track);

// wheels
translate([l_front_axle,half_track,h/2])
    simple_wheel(wheel_width=10);
translate([l_front_axle,-half_track,h/2])
    simple_wheel(wheel_width=10);

translate([l_rear_axle,half_track,h/2])
    simple_wheel(wheel_width=10);
translate([l_rear_axle,-half_track,h/2])
    simple_wheel(wheel_width=10);

// spoiler
use <naca.scad>

module car_spoiler() {
    rotate([90,0,0])
        naca_wing(span=50,chord=20,t=0.12,n=500,center=true);
    translate([0,10,-15])
        naca_wing(span=15,chord=15,t=0.12,n=500);
    translate([0,-10,-15])
        naca_wing(span=15,chord=15,t=0.12,n=500);
}

translate([l4+d5/2,0,25])
    car_spoiler();
此页面最后编辑于2022年9月26日 (星期一) 08:33。