OpenSCAD 教程/第 7 章
在上一章中,您使用了 if 語句來控制設計中的某些部分是否應該被建立。在本節中,您將瞭解如何建立形成特定模式的多個部分或物件。
以以下汽車模型為例,您將學習如何建立此類模式。
|
single_car.scad use <vehicle_parts.scad>
$fa=1;
$fs=0.4;
// Variables
track = 30;
wheelbase=40;
// Body
body();
// Front left wheel
translate([-wheelbase/2,-track/2,0])
rotate([0,0,0])
simple_wheel();
// Front right wheel
translate([-wheelbase/2,track/2,0])
rotate([0,0,0])
simple_wheel();
// Rear left wheel
translate([wheelbase/2,-track/2,0])
rotate([0,0,0])
simple_wheel();
// Rear right wheel
translate([wheelbase/2,track/2,0])
rotate([0,0,0])
simple_wheel();
// Front axle
translate([-wheelbase/2,0,0])
axle(track=track);
// Rear axle
translate([wheelbase/2,0,0])
axle(track=track);
|
| 使用上述汽車示例,對其進行修改以建立另一輛汽車。為了避免重複建立汽車的程式碼,您應該建立一個汽車模組。該模組應該有兩個輸入引數,汽車的軌距和軸距。軌距和軸距的預設值分別為 30 和 40 個單位。第一輛車應該位於原點,如上例所示,第二輛車應該沿著 Y 軸的正方向平移 50 個單位。 |
|
two_cars.scad use <vehicle_parts.scad>
$fa=1;
$fs=0.4;
module car(track=30, wheelbase=40) {
// Body
body();
// Front left wheel
translate([-wheelbase/2,-track/2,0])
rotate([0,0,0])
simple_wheel();
// Front right wheel
translate([-wheelbase/2,track/2,0])
rotate([0,0,0])
simple_wheel();
// Rear left wheel
translate([wheelbase/2,-track/2,0])
rotate([0,0,0])
simple_wheel();
// Rear right wheel
translate([wheelbase/2,track/2,0])
rotate([0,0,0])
simple_wheel();
// Front axle
translate([-wheelbase/2,0,0])
axle(track=track);
// Rear axle
translate([wheelbase/2,0,0])
axle(track=track);
}
car();
translate([0,50,0])
car();
|
| 沿著 Y 軸的正方向建立另外八輛汽車,使總共十輛汽車。與前一輛車相比,每輛下一輛車應該沿著 Y 軸的正方向平移 50 個單位。 |
|
row_of_ten_cars_along_y_axis.scad …
car();
translate([0,50,0])
car();
translate([0,100,0])
car();
translate([0,150,0])
car();
translate([0,200,0])
car();
translate([0,250,0])
car();
translate([0,300,0])
car();
translate([0,350,0])
car();
translate([0,400,0])
car();
translate([0,450,0])
car();
…
|
在完成上一個練習後,您可能已經意識到以這種方式建立汽車模式效率不高;每輛車都需要編寫一個新的語句,這會導致指令碼中大量程式碼重複。通常,您可以使用迴圈來更輕鬆地實現相同的結果。迴圈提供了一種方法,可以重複執行相同的一組語句一定次數,每次執行時都應用小的、可預測的變化。請看下面的例子。
|
row_of_ten_cars_along_y_axis_with_for_loop.scad …
for (dy=[0:50:450]) {
translate([0,dy,0])
car();
}
…
|
關於迴圈語法,您應該注意一些事項。首先,鍵入關鍵字 for,然後跟一對圓括號。在圓括號內,定義迴圈的變數。建議在適用時為迴圈變數賦予描述性的名稱。在本例中,變數名為 dy,因為它代表每輛車沿 Y 軸平移的單位數。變數的定義表明它的第一個值為 0 個單位,所有後續值都將每次遞增 50 個單位,直到達到值 450。這意味著變數 dy 在迴圈執行的整個過程中將總共取十個不同的值。這些值形成一個向量,與單個值不同,它是一個值的序列。在第一次重複中,變數將取向量的第一個值,即 0。在第二次重複中,取第二個值,即 50。依此類推。迴圈變數在迴圈重複過程中取的不同連續值是迴圈適合於建立多個零件或模型模式的關鍵概念。最後,在閉合圓括號之後,是一對花括號。在花括號內,存在將重複執行的語句,執行次數與迴圈變數的值數量相同。在本例中,花括號內的單個語句將執行 10 次,這是 dy 變數將取的值的數量。為了避免建立 10 輛完全重疊的汽車,迴圈每次重複時沿 Y 軸的平移量使用 dy 變數進行引數化。dy 變數在迴圈的每次重複時都有不同的值,從而建立了所需的模式。
| 使用迴圈建立一個汽車模式。第一輛車應該以原點為中心。每輛下一輛車應該放在前一輛車的後面。具體而言,每輛下一輛車應該與前一輛車相比沿著 X 軸的正方向平移 70 個單位。該模式應該由總共 8 輛汽車組成。迴圈變數應該命名為 dx。 |
|
row_of_eight_cars_along_x_axis.scad …
for (dx=[0:70:490]) {
translate([dx,0,0])
car();
}
…
|
在前面的示例中,迴圈變數 dy 被直接用於修改構成模式的每個單獨模型的某些方面。唯一被修改的方面是每個模型沿 Y 軸或 X 軸的平移。在每次重複中,dy 變數的值都等於每個模型所需的平移量。
當需要修改模型的多個方面時,最好讓迴圈變數取整數 0、1、2、3 等。然後從迴圈變數取的這些整數值生成修改模型不同方面所需的值(例如,沿某個軸的平移、某個部分的縮放)。在下面的示例中,這個概念被用來同時沿著 Y 軸和 X 軸的正方向將每輛車分別平移 50 個單位和 70 個單位。
|
diagonal_row_of_five_cars.scad …
for (i=[0:1:4]) {
translate([i*70,i*50,0])
car();
}
…
|
您應該注意一些事項。迴圈變數現在命名為 i。當迴圈變數以這種方式使用時,它通常被稱為索引,並被賦予名稱 i。由於迴圈變數取整數值,因此您需要將其乘以適當的數字以生成所需的平移量。具體來說,所需的沿 Y 軸的平移量是透過將迴圈變數乘以 50 生成的。同樣,所需的沿 X 軸的平移量是透過將迴圈變數乘以 70 生成的。
| 在前面的示例中新增旋轉變換,以便圍繞 Z 軸旋轉汽車。第一輛車不應旋轉。每輛下一輛車應該與前一輛車相比圍繞 Z 軸的正旋轉方向旋轉 90 度。Z 軸的正旋轉方向是將 X 軸旋轉到 Y 軸的方向。為了保持汽車處於相同位置,旋轉變換需要在平移變換之前還是之後應用? |
|
diagonal_row_of_five_twisted_cars.scad …
for (i=[0:1:4]) {
translate([i*70,i*50,0])
rotate([0,0,i*90])
car();
}
…
|
您正在建立的模式已經變得越來越酷,這是一個有趣的模式。
|
circular_pattern_of_ten_cars.scad …
r = 200;
for (i=[0:36:359]) {
translate([r*cos(i),r*sin(i),0])
car();
}
…
|
在上面的模式中,汽車被放置在半徑為 200 個單位的完美圓周上等間距的點上。如果您希望建立此類模式,則應該注意一些要點。
首先,為了建立一個圓形模式,您需要使用極座標。根據您的背景,您可能只看一眼程式碼就能注意到極座標的使用,或者您可能不知道它是什麼。在後一種情況下,您只需要知道極座標是一種方法,可以根據圓的半徑和對應於該點的角度生成圓上給定點的 X 和 Y 座標。角度 0 對應於圓上屬於 X 軸正方向的點。角度的正計數方向是從 X 軸到 Y 軸。這意味著正 Y 軸對應於 90 度,負 X 軸對應於 180 度,負 Y 軸對應於 270 度,如果您完成圓,則正 X 軸對應於 360 度。根據極座標,X 座標可以透過將圓的半徑乘以角度的餘弦來計算,而 Y 座標可以透過將圓的半徑乘以角度的正弦來計算。這就是生成所需的沿 X 軸和 Y 軸的平移量的方式。
您應該注意到的第二件事是 for 迴圈變數 i 的取值。變數 i 從 0 開始,每次迴圈遞增 36,以便在圓周上等距放置 10 輛汽車(360/10 = 36)。第一輛汽車在 0 度角處建立,與 360 度對應的汽車將完全重疊。為了避免這種情況,您需要指示 for 迴圈變數在達到 360 之前停止遞增。如果您懶得計算 360 - 36 = 324,您可以直接將限制設定為 359。這將正常工作,因為 for 迴圈變數將只取值 0、36、72、108、144、180、216、252、288 和 324,因為再遞增 36 個單位將導致 360,超過 359。
透過使用額外的變數併為其命名,您可以使指令碼更具描述性和可用性,以便任何人(甚至您在以後的時間點)都能更容易地理解它們的作用以及如何使用它們。例如,之前的指令碼可以採用以下形式。
…
r = 200; // pattern radius
n = 10; // 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])
car();
}
…
|
在上面的指令碼中,for 迴圈變數 i 對應於角度,這是不言自明的。平移沿每個軸的量也更加清晰。此外,透過更改半徑和/或汽車數量,可以輕鬆地自定義此模式。
| 修改上述指令碼中的適當值,以建立半徑為 160 個單位的圓周上等距放置的 14 輛汽車的圖案。 |
|
parametric_circular_pattern_of_fourteen_cars.scad …
r = 160; // pattern radius
n = 14; // 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])
car();
}
…
|
| 如果您不熟悉極座標,請使用以下指令碼進行嘗試。嘗試將不同的值分配給半徑和角度變數,並檢視汽車的最終位置。 |
|
car_positioned_with_polar_coordinates.scad …
radius = 100;
angle = 30; // degrees
dx = radius*cos(angle);
dy = radius*sin(angle);
translate([dx,dy,0])
car();
…
|
|
parametric_circular_pattern_of_fourteen_cars.scad …
r = 160; // pattern radius
n = 14; // 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])
car();
}
…
|
| 上面的指令碼用於建立圓形汽車圖案。透過新增旋轉變換修改上面的指令碼,使所有汽車都面向原點。使用您修改後的指令碼建立一個圖案,該圖案具有 12 輛汽車和 140 個單位的半徑。 |
|
cars_facing_at_the_origin.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();
}
…
|
| 對上述指令碼進行適當的更改以建立:i) 所有汽車都背離原點的圖案 ii) 所有汽車都以切線方向定向,就像在圓周上逆時針行駛一樣 iii) 以及像在圓周上順時針行駛一樣。 |
- 背離原點
|
cars_facing_away_from_the_origin.scad …
translate([dx,dy,0])
rotate([0,0,angle - 180])
car();
…
|
- 逆時針行駛
|
cars_driving_counter_clockwise.scad …
translate([dx,dy,0])
rotate([0,0,angle - 90])
car();
…
|
- 順時針行駛
|
cars_driving_clockwise.scad …
translate([dx,dy,0])
rotate([0,0,angle - 270])
car();
…
|
現在您已經掌握了使用 for 迴圈建立圖案的方法,是時候將您的新技能用於開發更復雜的輪轂設計了!
| 如果您對 OpenSCAD 技能感到自信,或者想進行更多實驗,請嘗試構建一個名為 spoked_wheel 的新模組,以建立以下輪轂設計。如果您想獲得更多關於建立此模組的指導,請完成以下練習。 |
| 如果您對一些額外的指導感到更舒服,那就很好。建立一個名為 spoked_wheel 的新模組,該模組具有 5 個輸入引數。輸入引數應分別命名為 radius、width、thickness、number_of_spokes 和 spoke_radius。分別為這些變數指定 12、5、5、7 和 1.5 的預設值。使用 cylinder 和 difference 命令透過從較大的圓柱體中減去較小的圓柱體來建立輪轂的環形。您需要建立的模型可以在以下影像中看到。在此步驟中,您將只使用 radius、width 和 thickness 變數。請記住,當您從另一個物體中減去一個物體時,它需要清除另一個物體的表面以避免任何錯誤。在定義小圓柱體的高度時請牢記這一點。您還需要從 radius 和 thickness 變數中計算出小圓柱體的半徑。您可以使用名為 inner_radius 的變數來儲存相應計算的結果,然後使用它來定義較小圓柱體的半徑。 |
|
ring_of_spoked_wheel.scad …
module spoked_wheel(radius=12, width=5, thickness=5, number_of_spokes=7, spoke_radius=1.5) {
// Ring
inner_radius = radius - thickness/2;
difference() {
cylinder(h=width,r=radius,center=true);
cylinder(h=width + 1,r=inner_radius,center=true);
}
}
spoked_wheel();
…
|
| 擴充套件之前的模組以另外建立輪轂的輻條,如以下影像所示。輪轂的輻條需要是圓柱形的。輻條的長度需要適當,以便每根輻條從環形的中心延伸到其半厚度。您將不得不使用 for 迴圈以圖案形式建立輻條。隨意檢視之前的 for 迴圈示例,這些示例可以幫助您完成此操作。 |
|
spoked_wheel_horizontal.scad …
module spoked_wheel(radius=12, width=5, thickness=5, number_of_spokes=7, spoke_radius=1.5) {
// Ring
inner_radius = radius - thickness/2;
difference() {
cylinder(h=width,r=radius,center=true);
cylinder(h=width + 1,r=inner_radius,center=true);
}
// Spokes
spoke_length = radius - thickness/4;
step = 360/number_of_spokes;
for (i=[0:step:359]) {
angle = i;
rotate([0,90,angle])
cylinder(h=spoke_length,r=spoke_radius);
}
}
spoked_wheel();
…
|
| 為了使新的輪轂設計與您在整個教程中建立的現有輪轂設計和模組相容,它需要旋轉以直立,如以下影像所示。新增適當的旋轉變換以執行此操作。 |
|
spoked_wheel.scad …
module spoked_wheel(radius=12, width=5, thickness=5, number_of_spokes=7, spoke_radius=1.5) {
rotate([90,0,0]) {
// Ring
inner_radius = radius - thickness/2;
difference() {
cylinder(h=width,r=radius,center=true);
cylinder(h=width + 1,r=inner_radius,center=true);
}
// Spokes
spoke_length = radius - thickness/4;
step = 360/number_of_spokes;
for (i=[0:step:359]) {
angle = i;
rotate([0,90,angle])
cylinder(h=spoke_length,r=spoke_radius);
}
}
}
…
spoked_wheel();
…
|
| 在 vehicle_parts.scad 指令碼中新增 spoked_wheel 模組。在您的汽車模型之一中使用新的輪轂設計。如果您沒有想法,可以嘗試複製以下模型。 |
|
car_with_spoked_wheels.scad use <vehicle_parts.scad>
$fa = 1;
$fs = 0.4;
front_track = 24;
rear_track = 34;
wheelbase = 60;
front_wheels_radius = 10;
front_wheels_width = 4;
front_wheels_thickness = 3;
front_spoke_radius = 1;
front_axle_radius = 1.5;
// Round car body
resize([90,20,12])
sphere(r=10);
translate([10,0,5])
resize([50,15,15])sphere(r=10);
// Wheels
translate([-wheelbase/2,-front_track/2,0])
spoked_wheel(radius=front_wheels_radius, width=front_wheels_width, thickness=front_wheels_thickness, spoke_radius=front_spoke_radius);
translate([-wheelbase/2,front_track/2,0])
spoked_wheel(radius=front_wheels_radius, width=front_wheels_width, thickness=front_wheels_thickness, spoke_radius=front_spoke_radius);
translate([wheelbase/2,-rear_track/2,0])
spoked_wheel();
translate([wheelbase/2,rear_track/2,0])
spoked_wheel();
// Axles
translate([-wheelbase/2,0,0])
axle(track=front_track, radius=front_axle_radius);
translate([wheelbase/2,0,0])
axle(track=rear_track);
|
以下指令碼沿 Y 軸建立一行汽車。
|
row_of_six_cars_along_y_axis.scad …
n = 6; // number of cars
y_spacing = 50;
for (dy=[0:y_spacing:n*y_spacing-1]) {
translate([0,dy,0])
car();
}
…
|
| 修改上面的指令碼以建立 4 行額外的汽車。與前一行相比,每一行都應該沿 X 軸的正方向平移 70 個單位。 |
|
five_rows_of_six_cars.scad …
n = 6; // number of cars
y_spacing = 50;
for (dy=[0:y_spacing:n*y_spacing-1]) {
translate([0,dy,0])
car();
}
for (dy=[0:y_spacing:n*y_spacing-1]) {
translate([70,dy,0])
car();
}
for (dy=[0:y_spacing:n*y_spacing-1]) {
translate([140,dy,0])
car();
}
for (dy=[0:y_spacing:n*y_spacing-1]) {
translate([210,dy,0])
car();
}
for (dy=[0:y_spacing:n*y_spacing-1]) {
translate([280,dy,0])
car();
}
…
|
如果您一直在密切關注本教程,您可能已經注意到上面的指令碼效率不高。它有很多程式碼重複,行數不容易修改。您在本章開頭想要建立一行汽車時就遇到了類似的情況。為了解決這個問題,您將建立圖案一部分(一輛汽車)的語句包裝在一個 for 迴圈中。這生成了整個圖案(一行汽車),而無需為每輛單獨的汽車鍵入語句。同樣的原則可以應用在這裡。在本例中,重複的圖案將是汽車行,它本身是單個汽車的重複圖案。遵循與之前相同的過程,建立汽車行的語句將被放置在一個 for 迴圈中,以建立汽車行的圖案。結果是,一個 for 迴圈被放置在另一個 for 迴圈中。以這種方式使用的 for 迴圈稱為巢狀 for 迴圈。以下示例演示了此概念。
|
five_rows_of_six_cars_with_nested_for_loops.scad …
n_cars = 6;
y_spacing = 50;
n_rows = 5;
x_spacing = 70;
for (dx=[0:x_spacing:n_rows*x_spacing-1]) {
for (dy=[0:y_spacing:n_cars*y_spacing-1]) {
translate([dx,dy,0])
car();
}
}
…
|
您應該注意以下概念。在外層 for 迴圈的第一次迴圈中,內層 for 迴圈的所有迴圈都會被執行,從而建立第一行汽車。在外層 for 迴圈的第二次迴圈中,內層 for 迴圈的所有迴圈都會被執行,從而建立第二行汽車。依此類推。每一行都由 dx 變數定位,dx 變數儲存沿 X 軸的參量化平移。在外層迴圈的每次迴圈中,都會使用新的 dx 值。然後,此值保持不變,而內層迴圈執行並修改 dy 值。這樣,在每個 dx 值處都會生成一行汽車。
| 使用巢狀 for 迴圈建立三個類似於下圖的圓形汽車圖案。外層迴圈的 for 迴圈變數應用於引數化每個圖案的半徑。圓形圖案的半徑應分別為 140、210 和 280 個單位。每個圖案應由 12 輛汽車組成。 |
|
three_circular_patterns.scad …
n = 12; // number of cars
step = 360/n;
for (r=[140:70:280]) {
for (angle=[0:step:359]) {
dx = r*cos(angle);
dy = r*sin(angle);
translate([dx,dy,0])
rotate(angle)
car();
}
}
…
|
| 修改之前練習的指令碼,使每個圖案不僅半徑不同,而且汽車數量也不同。為此,使用索引變數 i 作為外層迴圈的變數,而不是對應於半徑的變數 r。應根據公式 r = 70 + i*70 在外層 for 迴圈的每次迴圈中計算變數 r。此外,在外層 for 迴圈的每次迴圈中,變數 n 應根據公式 n = 12 + i*2 取不同的值。步長變數也需要在外層 for 迴圈的每次迴圈中更新。變數 i 應取值 1、2 和 3。 |
|
three_circular_patterns_with_increasing_number_of_cars.scad …
for (i=[1:1:3]) {
r = 70 + i*70;
n = 12 + i*2;
step = 360/n;
for (angle=[0:step:359]) {
dx = r*cos(angle);
dy = r*sin(angle);
translate([dx,dy,0])
rotate(angle)
car();
}
}
…
|