跳轉到內容

OpenSCAD 教程/第 9 章

來自華夏公益教科書,為開放世界提供開放書籍

在 OpenSCAD 中進行數學計算

[編輯 | 編輯原始碼]

到目前為止,您已經瞭解到 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);

使用 polygon 原語建立任何 2D 物件

[編輯 | 編輯原始碼]

除了圓形和方形 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=[開始:步長:結束]) … ];)來生成以下列表
  1. [1, 2, 3, 4, 5, 6]
  2. [10, 8, 6, 4, 2, 0, -2]
  3. [[3, 30], [4, 40], [5, 50], [6, 60]]

練習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();
華夏公益教科書