OpenSCAD 教程/第 9 章
到目前为止,您已经了解到 OpenSCAD 变量在整个脚本执行过程中只能保存一个值,即最后分配给它的值。您还了解到 OpenSCAD 变量的常见用途是为模型提供参数化。在这种情况下,每个参数化模型都将具有一些独立变量,您可以更改它们的值来调整该模型。这些变量通常直接分配一个值,如以下示例所示。
… wheel_diameter = 12; … body_length = 70; … wheelbase = 40; … // etc. … |
您已经见过几次,但没有明确提及的另一件事是在您的脚本中使用变量和硬编码值执行数学运算的能力。一个例子是在实现汽车的轴距。回想一下,汽车的轴和轮子沿 X 轴平移,并从原点移动了轴距值的一半。由于在这种情况下,轴距是一个已经在您的脚本中定义的变量,因此您可以通过将轴距变量除以二来计算单位数量。类似的事情也发生在轨距变量上,用于放置汽车的左右车轮。回想一下,左轮和右轮沿 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); |
加法、减法、乘法和除法在 OpenSCAD 中分别用 +、-、* 和 / 表示。除了这些基本运算之外,还有许多额外的数学运算在构建更复杂的模型时可能会有用。两个例子是您用来定义汽车圆形图案的余弦和正弦函数。具体来说,您使用余弦和正弦函数将每辆车的极坐标转换为直角坐标,以便将其平移到正确的位置。您可以在备忘单中找到所有可用的数学函数的简要列表。
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(); } … |
在上面的例子中,您不仅在您的脚本中使用了可用的数学运算,而且还定义了两个额外的变量 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 轴所需的平移量。
修改上面的脚本,以提高其可读性,避免重复相同的数学运算多次。为此,您应该引入两个名为 half_wheelbase 和 half_track 的新变量。使用相应的数学计算将这些变量分别设置为轴距和轨距变量值的一半。将平移命令中重复的数学运算替换为这两个变量的使用。 |
car_with_additional_variables.scad use <vehicle_parts.scad> $fa = 1; $fs = 0.4; wheelbase = 40; track = 35; half_wheelbase = wheelbase/2; half_track = track/2; // Body body(); // Front left wheel translate([-half_wheelbase,-half_track,0]) simple_wheel(); // Front right wheel translate([-half_wheelbase,half_track,0]) simple_wheel(); // Rear left wheel translate([half_wheelbase,-half_track,0]) simple_wheel(); // Rear right wheel translate([half_wheelbase,half_track,0]) simple_wheel(); // Front axle translate([-half_wheelbase,0,0]) axle(track=track); // Rear axle translate([half_wheelbase,0,0]) axle(track=track); |
除了圆形和方形 2D 原语之外,还有另一个原语可以让您设计几乎任何 2D 对象。这就是 polygon 原语,它允许您通过提供一个包含其点坐标的列表来定义 2D 对象。假设您想设计以下 2D 部件。
在不使用 polygon 原语的情况下设计此部件的一种方法是从一个对应于此部件外围尺寸的正方形开始,然后从其右上角减去一个适当旋转和平移的正方形。计算适当的旋转角度和平移量将是一项耗时的任务。此外,对于更复杂的对象来说,遵循这种策略是不可能的。相反,您可以使用以下方式使用 polygon 原语创建此对象。
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); |
关于 polygon 原语的使用,您应该注意一些事项。polygon 原语使用点列表作为输入。点或顶点使用 X 和 Y 坐标对表示,并按顺序提供。在定义列表时,您可以从您喜欢的任何顶点开始,并且可以以顺时针或逆时针方向遍历它们。在上面的示例中,第一个顶点位于原点 (0,0),而其余顶点按顺时针方向列出。所有顶点(X 和 Y 坐标对)p0、p1、…、p4 都放置在一个名为 points 的列表中。然后将此列表传递给 polygon 命令以创建相应对象。
无论变量只有一个值还是一个值列表,您都可以使用 echo 命令在控制台中打印其内容。
… echo(points); … |
控制台中的输出为:[[0, 0], [0, 30], [15, 30], [35, 20], [35, 0]]
分别命名每个点(p0、p1、…)不是必需的,但建议您更好地跟踪设计。您也可以直接定义要传递给 polygon 命令的点列表。
… points = [[0, 0], [0, 30], [15, 30], [35, 20], [35, 0]]; … |
此外,您甚至不必定义一个变量来存储点列表。您可以在调用 polygon 命令时直接定义点列表。
… polygon([[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); |
使用 polygon 原语创建以下 2D 对象。为此,您需要定义一个包含对象点的 X 和 Y 坐标对的列表。请记住,应该按适当的顺序定义点。您应该首先将每个点的坐标存储在名为 p0、p1、p2、… 的单独变量中,然后定义所有点的列表并将其存储在一个名为 points 的变量中。此列表将传递给 polygon 命令。每个点的坐标定义应该与给定的尺寸相关联地参数化,这可以通过使用适当的数学表达式来实现。为此,您还需要为每个给定尺寸定义一个变量。 |
profile_2_polygon.scad // Given dimensions h1 = 23; h2 = 10; h3 = 8; d1 = 25; d2 = 12; d3 = 15; // Points p0 = [0, 0]; p1 = [0, h1 + h2]; p2 = [d3, h1]; p3 = [d1 + d2, h1 + h2]; p4 = [d1 + d2, h3]; p5 = [d1, 0]; points = [p0, p1, p2, p3, p4, p5]; // Polygon polygon(points); |
使用 linear_extrude 和 rotate_extrude 命令分别创建一个管子和一个环,它们具有上述轮廓。该管子应具有 100 个单位的高度。该环应具有 100 个单位的内径。您需要将 2d 轮廓沿 X 轴的正方向平移多少个单位才能实现这一点? |
linearly_extruded_polygon.scad … linear_extrude(height=100) polygon(points); … |
rotationaly_extruded_polygon.scad … rotate_extrude(angle=360) translate([50,0,0]) polygon(points); … |
是时候使用您的新技能来创建一辆赛车的车身了!
使用 polygon 命令创建上述赛车车身。为此,您必须定义设计的每个点,将它们全部添加到一个列表中,并将此列表传递给 polygon 命令。每个点的坐标定义应该与给定的尺寸相关联地参数化。请记住,要做到这一点,您必须为每个给定尺寸定义一个变量,并使用适当的数学表达式从这些变量中计算每个点的坐标。您应该将创建的 2D 轮廓挤压到 14 个单位的高度。 |
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); |
如前所述,您可以使用额外的变量来提高脚本的可读性,并避免重复数学运算。您能在上面的脚本中找到一种方法来做到这一点吗? |
racing_car_body_with_extra_variables.scad // model parameters d1=30; d2=20; d3=20; d4=10; d5=20; w1=15; w2=45; w3=25; h=14; // 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); |
尝试通过在上面的车身上添加剩余的物体来完成赛车的设计。 |
racing_car.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); |
从上面的例子可以很明显地看出,多边形图元为创建物体打开了可能性,这些物体仅仅使用基本二维或三维图元是很难实现的。在这些例子中,您通过根据给定的设计定义其点的坐标来创建自定义的二维轮廓。然而,要释放polygon命令的真正力量,并创建更复杂和更令人印象深刻的设计,您必须使用数学以编程方式定义轮廓的点。这是因为分别定义每个点对于设计平滑的非方形轮廓所需的数百个点来说是不可扩展的。以下心脏就是一个例子。您能手动定义创建它所需的点吗?没有办法。
该模型不是通过手动定义每个点,因为这实际上是不可能的,而是通过以下参数方程以编程方式定义的。
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。通过为步长变量选择较小或较大的值,可以控制将创建的点数。具体而言,为了创建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个点,轮廓的分辨率非常好。
修改上面的脚本,使心脏的轮廓由20个点组成。 |
heart_low_poly.scad … n = 20; … |
使用更多或更少的点取决于您。如果您想创建一个与底层数学方程非常相似的物体,您应该增加点数。相反,如果您选择低多边形风格,您应该减少点数。
在继续之前,进行一些快速的列表生成练习。使用新引入的语法(变量 = [ for (i=[开始:步长:结束]) … ];)来生成以下列表
|
练习i的解决方案,[1, 2, 3, 4, 5, 6]
x = [ for (i=[1:6]) i]; |
练习ii的解决方案,[10, 8, 6, 4, 2, 0, -2]
x = [ for (i=[10:-2:-2]) i]; |
练习iii的解决方案,[[3, 30], [4, 40], [5, 50], [6, 60]]
x = [ for (i=[3:6]) [i, i*10]]; |
请注意,当没有提供步长时,默认值为1。
OpenSCAD还允许您定义自己的数学函数,当数学表达式特别长或您想多次使用它时,这将很有用。这些函数与模块的工作方式类似,不同之处在于它们不是定义一个设计,而是定义一个数学过程。定义函数后,您可以通过调用其名称并提供必要的输入参数来使用它。例如,您可以定义一个接受参数t并返回心脏轮廓相应点的X和Y坐标的函数。
function heart_coordinates(t) = [16*pow(sin(t),3), 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)]; |
在这种情况下,创建心脏的脚本将采用以下形式。
n = 500; h = 10; step = 360/n; function heart_coordinates(t) = [16*pow(sin(t),3), 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)]; points = [ for (t=[0:step:359.999]) heart_coordinates(t)]; linear_extrude(height=h) polygon(points); |
您应该注意到函数定义中的一些事项。首先键入函数一词。然后是您要给函数起的名字。在本例中,函数名为heart_coordinates。在函数名之后是一对圆括号,其中包含函数的输入参数。与模块参数类似,函数中的输入参数可以有默认值。在本例中,唯一的输入参数是当前步长的极角t,并且没有给它默认值。在闭合括号之后是等号和定义心脏轮廓的X和Y坐标对的命令。
生成点列表也可以变成一个函数。这可以通过以下类似的方式完成。
function heart_points(n=50) = [ for (t=[0:360/n:359.999]) heart_coordinates(t)]; |
在这种情况下,创建心脏的脚本将采用以下形式。
n=20; h = 10; function heart_coordinates(t) = [16*pow(sin(t),3), 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)]; function heart_points(n=50) = [ for (t=[0:360/n:359.999]) heart_coordinates(t)]; points = heart_points(n=n); linear_extrude(height=h) polygon(points); |
简而言之,您应该记住,任何返回单个值或值列表的命令都可以变成函数。与模块一样,函数应该用于组织您的设计并使其可重用。
由于您已经定义了生成创建心脏所需的点列表的函数,因此最好也定义一个创建心脏的模块。创建一个名为heart的模块。该模块应具有两个输入参数h和n,分别对应于心脏的高度和使用的点数。该模块应调用heart_points函数来创建所需的点列表,然后将该列表传递给polygon命令以创建心脏的二维轮廓。此轮廓应挤出到指定高度。 |
heart.scad … module heart(h=10, n=50) { points = heart_points(n=n); linear_extrude(height=h) polygon(points); } … |
您可以将heart_coordinates和heart_points函数以及heart模块保存在名为heart.scad的脚本中,并将其添加到您的库中。每当您想要在正在处理的设计中包含心脏时,都可以使用use命令使此脚本的函数和模块对您可用。 |
您将把新技能付诸实践,为您的赛车创建一个空气动力学扰流板!
您将使用对称的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函数。 |
naca_airfoil_module.scad … 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 函数生成的列表连接时,翼型的所有点都以从前缘开始的顺时针方向定义,从而使生成的列表适合用于多边形命令。 |
naca_airfoil_module.scad … function naca_bottom_coordinates(t,n) = [ for (x=[1:-1/(n-1):0]) [x, - naca_half_thickness(x,t)]]; … |
创建一个名为 naca_coordinates 的函数,它将两个点列表连接在一起。您可以使用 OpenSCAD 的内置函数“concat”将列表连接在一起。将两个列表作为输入传递给 concat 以将它们连接在一起。 |
naca_airfoil_module.scad … function naca_coordinates(t,n) = concat(naca_top_coordinates(t,n), naca_bottom_coordinates(t,n)); … |
尝试使用 naca_coordinates 函数创建一个包含翼型点的列表,该翼型的最大厚度为 0.12,每半部分有 300 个点。该列表应存储在一个名为 points 的变量中。points 变量应传递给多边形命令以创建翼型的二维轮廓。 |
small_airfoil_polygon.scad … points = naca_coordinates(t,n); polygon(points); … |
上述翼型的弦长为 1 个单位。您可以使用适当的缩放命令来放大翼型吗?所需的弦长应定义在一个名为 chord 的变量中,并且缩放命令应与该变量相关定义。创建一个弦长为 20 个单位的翼型。 |
scaled_airfoil_polygon.scad … chord = 20; points = naca_coordinates(t=0.12,n=300); scale([chord,chord,1]) polygon(points); … |
将上述脚本变成一个名为 naca_airfoil 的模块。该模块应具有三个输入参数,chord、t 和 n。任何输入参数都不应具有默认值。 |
naca_airfoil_module.scad module naca_airfoil(chord,t,n) { points = naca_coordinates(t,n); scale([chord,chord,1]) polygon(points); } |
现在要做的就是对二维翼型轮廓应用 linear_extrude 命令来创建机翼。创建一个名为 naca_wing 的模块来执行此操作。与 naca_airfoil 模块相比,naca_wing 模块应具有两个额外的输入参数,span 和 center。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); … |
使用 naca_wing 模块在前面的示例中添加两个较小的垂直机翼,以创建汽车的扰流板。较小的机翼应具有 15 个单位的翼展和 15 个单位的弦长。 |
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.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(); |