跳轉到內容

OpenSCAD 教程/第 5 章

來自華夏公益教科書,開放的世界,開放的書籍

建立和利用模組作為單獨的指令碼

[編輯 | 編輯原始碼]

在上一章中,你學習了 OpenSCAD 最強大的功能之一——模組,以及它如何用於引數化設計。你還有機會將汽車分成不同的模組,然後將它們重新組合來建立不同型別的車輛。使用模組也可以被視為組織你的創作和構建你自己的物件庫的一種方式。輪子模組可以潛在地用於大量的設計中,所以能夠在需要時輕鬆地獲取它,而無需在當前設計的指令碼中重新定義它,將是一件很棒的事情。為此,你需要定義輪子模組並將其儲存為單獨的指令碼。

練習
在單獨的指令碼檔案中定義以下 simple_wheel 模組。在同一個指令碼中,呼叫 simple_wheel 模組,以便你直觀地看到該模組建立了哪個物件。將指令碼檔案儲存為名為 simple_wheel.scad 的 scad 檔案。
程式碼

simple_wheel.scad

$fa = 1;
$fs = 0.4;
module simple_wheel(wheel_radius=10, wheel_width=6) {
    rotate([90,0,0])
        cylinder(h=wheel_width,r=wheel_radius,center=true);
}
simple_wheel();

現在是時候在另一個設計中使用這個儲存的模組了。首先你需要建立一個新的設計。

練習
建立一個新的指令碼,其中包含以下汽車設計。給指令碼起任何你喜歡的名字,但將指令碼儲存在與 simple_wheel 模組相同的目錄中。
程式碼

car_with_simple_wheels.scad

$fa = 1;
$fs = 0.4;
wheel_radius = 8; 
base_height = 10; 
top_height = 10; 
track = 30; 
// Car body base 
cube([60,20,base_height],center=true); 
// Car body top 
translate([5,0,base_height/2+top_height/2 - 0.001])
    cube([30,20,top_height],center=true); 
// Front left wheel 
translate([-20,-track/2,0])
    rotate([90,0,0])
    cylinder(h=3,r=wheel_radius,center=true); 
// Front right wheel 
translate([-20,track/2,0])
    rotate([90,0,0])
    cylinder(h=3,r=wheel_radius,center=true); 
// Rear left wheel 
translate([20,-track/2,0])
    rotate([90,0,0])
    cylinder(h=3,r=wheel_radius,center=true); 
// Rear right wheel 
translate([20,track/2,0])
    rotate([90,0,0])
    cylinder(h=3,r=wheel_radius,center=true); 
// Front axle 
translate([-20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true); 
// Rear axle 
translate([20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true);

有兩種方法可以將 simple_wheel.scad 指令碼用於你的汽車設計中。它可以被包含或使用。要包含指令碼,你必須在汽車指令碼的頂部新增以下語句。

程式碼
include <simple_wheel.scad>

你應該注意到,發生了一些意想不到的事情。在原點處建立了一個輪子。這是 simple_wheel 指令碼的物件。當你使用 include 時,OpenSCAD 將你包含的整個外部指令碼視為你當前指令碼的一部分。在 simple_wheel.scad 指令碼中,除了 simple_wheel 模組定義之外,還呼叫了 simple_wheel 模組,它建立了一個輪子物件。由於使用了 include 命令,因此該物件也被建立在汽車模型中。這正是你將透過使用 use 而不是 include 命令來改變的事情,但暫時不要擔心它。

練習
汽車的輪子目前是用 cylinder 命令建立的。由於 simple_wheel.scad 指令碼已被包含在汽車指令碼中,因此 simple_wheel 模組應該可用。用對 simple_wheel 模組的呼叫替換 cylinder 命令。任何 rotate 命令是否變得不必要?對 simple_wheel 模組的呼叫不應包含任何引數定義。
程式碼

car_with_wheels_created_by_included_module.scad

include <simple_wheel.scad>
$fa = 1;
$fs = 0.4;
wheel_radius = 8; 
base_height = 10; 
top_height = 10; 
track = 30; 
// Car body base 
cube([60,20,base_height],center=true); 
// Car body top 
translate([5,0,base_height/2+top_height/2 - 0.001])
    cube([30,20,top_height],center=true); 
// Front left wheel 
translate([-20,-track/2,0])
    simple_wheel(); 
// Front right wheel 
translate([-20,track/2,0])
    simple_wheel();  
// Rear left wheel 
translate([20,-track/2,0])
    simple_wheel(); 
// Rear right wheel 
translate([20,track/2,0])
    simple_wheel(); 
// Front axle 
translate([-20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true); 
// Rear axle 
translate([20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true);

練習
在對 simple_wheel 模組的呼叫中定義 wheel_radius 和 wheel_width 引數。為此,使用現有的 wheel_radius 變數以及你將要定義的 wheel_width 變數。將變數設定為你喜歡的值。
程式碼

car_with_narrower_wheels_created_by_included_module.scad

include <simple_wheel.scad>
$fa = 1;
$fs = 0.4;
wheel_radius = 8;
wheel_width = 4;
base_height = 10; 
top_height = 10; 
track = 30; 
// Car body base 
cube([60,20,base_height],center=true); 
// Car body top 
translate([5,0,base_height/2+top_height/2 - 0.001])
    cube([30,20,top_height],center=true); 
// Front left wheel 
translate([-20,-track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Front right wheel 
translate([-20,track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width);  
// Rear left wheel 
translate([20,-track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Rear right wheel 
translate([20,track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Front axle 
translate([-20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true); 
// Rear axle 
translate([20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true);

從上面的例子中,你應該記住,當你將外部指令碼包含在當前指令碼中時,外部指令碼的模組將變為當前指令碼中的可用模組,但此外,在外部指令碼中建立的任何物件也將在當前指令碼中建立。由於原點處的輪子在這種情況下不需要,因此是時候使用 use 命令而不是 include 命令了。

練習
將最後一個例子的 include 命令替換為 use 命令。
程式碼

car_with_wheels_created_by_used_module.scad

…
use <simple_wheel.scad>
…

你應該注意到,在原點處不再建立輪子。你應該記住,use 命令的工作原理與 include 命令相同,唯一的區別是 use 命令不建立任何物件,而只是使外部指令碼的模組在當前指令碼中可用。

使用包含多個模組的指令碼

[編輯 | 編輯原始碼]

在前面的例子中,simple_wheel.scad 指令碼只有一個模組:simple_wheel 模組。情況並不總是如此。

練習
在 simple_wheel.scad 指令碼中新增以下模組。將 simple_wheel.scad 指令碼重新命名為 wheels.scad。
程式碼
module complex_wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2) {
    cylinder_height=2*wheel_radius; 
    difference() { 
        // Wheel sphere
        sphere(r=wheel_radius); 
        // Side sphere 1 
        translate([0,side_spheres_radius + hub_thickness/2,0])
            sphere(r=side_spheres_radius); 
        // Side sphere 2 
        translate([0,- (side_spheres_radius + hub_thickness/2),0])
            sphere(r=side_spheres_radius); 
        // Cylinder 1
        translate([wheel_radius/2,0,0])
            rotate([90,0,0])
            cylinder(h=cylinder_height,r=cylinder_radius,center=true); 
        // Cylinder 2 
        translate([0,0,wheel_radius/2])
            rotate([90,0,0])
            cylinder(h=cylinder_height,r=cylinder_radius,center=true); 
        // Cylinder 3 
        translate([-wheel_radius/2,0,0])
            rotate([90,0,0])
            cylinder(h=cylinder_height,r=cylinder_radius,center=true); 
        // Cylinder 4 
        translate([0,0,-wheel_radius/2])
            rotate([90,0,0])
            cylinder(h=cylinder_height,r=cylinder_radius,center=true); 
    }
}
練習
在你的汽車指令碼中使用 wheels.scad 指令碼。使用 simple_wheel 模組建立前輪,使用 complex_wheel 模組建立後輪。
程式碼

car_with_different_wheels_from_used_modules.scad

use <wheels.scad>
wheel_radius = 8;
wheel_width = 4;
base_height = 10; 
top_height = 10; 
track = 30; 
// Car body base 
cube([60,20,base_height],center=true); 
// Car body top 
translate([5,0,base_height/2+top_height/2 - 0.001])
    cube([30,20,top_height],center=true); 
// Front left wheel 
translate([-20,-track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Front right wheel 
translate([-20,track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width);  
// Rear left wheel 
translate([20,-track/2,0])
    complex_wheel(); 
// Rear right wheel 
translate([20,track/2,0])
    complex_wheel(); 
// Front axle 
translate([-20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true); 
// Rear axle 
translate([20,0,0])
    rotate([90,0,0])
    cylinder(h=track,r=2,center=true);

透過這個例子,應該清楚的是,指令碼的名稱不必與模組的名稱相同,並且指令碼可以包含多個模組。關於如何組織你的模組庫,沒有對錯之分。在最後一個例子中,使用了定義了不同輪子模組的 wheels.scad 指令碼。或者,你也可以將每個模組儲存為單獨的 *.scad 指令碼。

練習
建立一個 vehicle_parts.scad 指令碼。在這個指令碼中,定義 simple_wheel、complex_wheel、body 和 axle 模組。在另一個名為 vehicle_concept 的指令碼中使用這個指令碼,使相應的模組可用。使用這些模組來建立一個類似於以下內容的車輛概念。

程式碼

car_with_ten_wheels.scad

use <vehicle_parts.scad>
$fa = 1;
$fs = 0.4;
wheel_radius = 8;
wheel_width = 4;
base_length = 60;
top_length = 80;
track = 30;
wheelbase_1 = 38;
wheelbase_2 = 72;
z_offset = 10;
body(base_length=base_length, top_length=top_length, top_offset=0); 
// Front left wheel 
translate([-wheelbase_2/2,-track/2,z_offset])
    complex_wheel();  
// Front right wheel 
translate([-wheelbase_2/2,track/2,z_offset])
    complex_wheel();   
// Rear left wheel 
translate([wheelbase_2/2,-track/2,z_offset])
    complex_wheel(); 
// Rear right wheel 
translate([wheelbase_2/2,track/2,z_offset])
    complex_wheel(); 
// Front axle 
translate([-wheelbase_2/2,0,z_offset])
    axle(track=track); 
// Rear axle 
translate([wheelbase_2/2,0,z_offset])
    axle(track=track);

// Middle front left wheel 
translate([-wheelbase_1/2,-track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Middle front right wheel 
translate([-wheelbase_1/2,track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Middle left wheel 
translate([0,-track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Middle right wheel 
translate([0,track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width);
// Middle rear left wheel 
translate([wheelbase_1/2,-track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Middle rear right wheel 
translate([wheelbase_1/2,track/2,0])
    simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width); 
// Middle front axle 
translate([-wheelbase_1/2,0,0])
    axle(track=track); 
// Middle axle 
translate([0,0,0])
    axle(track=track); 
// Middle rear axle 
translate([wheelbase_1/2,0,0])
    axle(track=track);

使用 MCAD 庫

[編輯 | 編輯原始碼]

MCAD 庫 (https://github.com/openscad/MCAD) 是一個包含 OpenSCAD 中常用的機械設計元件庫。你可以透過使用相應的 OpenSCAD 指令碼並呼叫所需的模組來使用 MCAD 庫中的物件。例如,有一個 boxes.scad 指令碼,其中包含一個圓角盒子的模型。boxes.scad 指令碼包含一個模組,該模組可用於建立相應的盒子。你可以開啟這個指令碼以檢視該模組的引數是什麼,然後使用它在你的設計中新增圓角盒子。你可以使用以下指令碼建立一個邊長分別為 10、20 和 30 個單位,圓角半徑為 3 個單位的全圓角盒子。

程式碼

completely_rounded_box.scad

use <MCAD/boxes.scad>
$fa=1;
$fs=0.4;
roundedBox(size=[10,20,30],radius=3,sidesonly=false);

透過將 sidesonly 引數設定為 true,你可以建立一個具有相同尺寸但只有 4 個圓角邊的盒子。

程式碼

sides_only_rounded_box.scad

use <MCAD/boxes.scad>
$fa=1;
$fs=0.4;
roundedBox(size=[10,20,30],radius=3,sidesonly=true);

boxes.scad 指令碼位於 MCAD 目錄下,MCAD 目錄位於 libraries 目錄下。libraries 目錄位於 OpenSCAD 的安裝資料夾中。如果你希望你的任何庫從任何目錄都可用,你可以將其新增到 libraries 目錄中。你也可以在 https://www.openscad.org/libraries.html 上瀏覽其他可用的 OpenSCAD 庫。不過,你應該知道,在 GitHub 和 Thingiverse 上有大量可用的庫,遠遠超過了 OpenSCAD 庫頁面連結的那些庫。

練習
使用 MCAD 庫的 boxes.scad 指令碼建立一個全圓角盒子,邊長分別為 50、20 和 15 個單位,圓角半徑為 5 個單位。
程式碼

horizontal_completely_rounded_box.scad

use <MCAD/boxes.scad>
$fa=1;
$fs=0.4;
roundedBox(size=[50,20,15],radius=5,sidesonly=false);

練習
使用 MCAD 庫的 boxes.scad 指令碼建立一個只有 4 個圓角邊的圓角盒子,邊長分別為 50、50 和 15 個單位,圓角半徑為 20 個單位。
程式碼

short_sides_only_rounded_box.scad

use <MCAD/boxes.scad>
$fa=1;
$fs=0.4;
roundedBox(size=[50,50,15],radius=20,sidesonly=true);

建立更多可引數化的模組

[編輯 | 編輯原始碼]

到目前為止,對已建立模組的唯一輸入是透過為每種情況定義的模組的輸入引數。例如,complex_wheel 模組能夠根據所選的輸入引數(如 wheel_radius、hub_thickness 等)建立大量引數化的輪子。

在您的車輛設計中,您一直在使用車身、車輪和車軸模組,這些模組組合在一起可以生產各種型別的車輛。在所有車輛設計中,兩個車輪與一個車軸一起使用形成一套車輪。您可能已經考慮過需要一個 axle_wheelset 模組來使用單個語句同時定義所有三個物件。而您考慮這一點是正確的!但是,這個模組還沒有建立的原因,您現在將發現原因。

在前面的章節中,您建立了兩種不同的車輪設計(simple_wheel 和 complex_wheel)和一個車軸設計。您可以使用您現有的知識以以下方式組合 simple_wheel 和 axle 模組。

程式碼

axle_with_simple_wheelset_from_module.scad

use <vehicle_parts.scad>
$fa = 1;
$fs = 0.4;
module axle_wheelset(wheel_radius=10, wheel_width=6, track=35, radius=2) {
    translate([0,track/2,0])
        simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width);
    axle(track=track, radius=radius);
    translate([0,-track/2,0])
        simple_wheel(wheel_radius=wheel_radius, wheel_width=wheel_width);
}
axle_wheelset();

如果您只想使用上述簡單車輪集,這種方法就可以了。問題是,這個 axle_wheelset 模組實際上並不靈活,也無法按預期程度引數化。一方面,您可以自定義所有輸入引數,但另一方面,您可以將 simple_wheel 設計替換為 complex_wheel 設計嗎?事實是,使用上述方法,為了做到這一點,您將不得不定義一個全新的模組。

程式碼

axle_with_complex_wheelset_from_module.scad

use <vehicle_parts.scad>
$fa = 1;
$fs = 0.4;
module axle_wheelset_complex(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2, track=35, radius=2) {
    translate([0,track/2,0])
        complex_wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2);
    axle(track=track, radius=radius);
    translate([0,-track/2,0])
        complex_wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2);
}
axle_wheelset_complex();

如果您還沒有意識到這是一個問題,想象一下,您的庫中有六種不同的車輪設計和兩種不同的車軸設計。如果您想實現 axle_wheelset 模組,您將需要定義 12 個不同的模組來涵蓋所有車輪和車軸設計的組合。此外,如果您要在您的集合中新增新的車輪或車軸設計,您將需要定義一些額外的 axle_wheelset 模組,這將使維護您的庫非常困難。

好訊息是,上面的兩個模組看起來非常相似。如果您能夠保持模組的結構相同,但將車輪設計的特定選擇引數化,那麼問題就可以解決。幸運的是,OpenSCAD 支援此功能,並且可以透過以下方式引數化車輪設計的特定選擇。

程式碼

axle_with_simple_wheelset_from_parameterized_module.scad

use <vehicle_parts.scad>
$fa = 1;
$fs = 0.4;
module axle_wheelset(track=35, radius=2) {
    translate([0,track/2,0])
        children(0);
    axle(track=track, radius=radius);
    translate([0,-track/2,0])
        children(0);
}
axle_wheelset() {
    simple_wheel();
}

現在可以輕鬆地更改車輪設計,使此模組成為一個真正引數化的模組。

程式碼

axle_with_complex_wheelset_from_parameterized_module.scad

axle_wheelset() {
    complex_wheel();
}

程式碼

axle_with_large_complex_wheelset_from_parameterized_module.scad

axle_wheelset(radius=5) {
    complex_wheel(wheel_radius=20);
}

這裡有一個非常重要的概念您應該掌握。您首先應該注意的是這個新模組的定義。這個新模組類似於以前的模組,不同之處在於使用命令 children(0) 來代替對特定車輪模組的呼叫。您應該注意的第二件事是呼叫 axle_wheelset 模組。對 axle_wheelset 模組的呼叫包含一對花括號,每次定義模組要使用的特定車輪設計都在花括號內。OpenSCAD 保持一個已在花括號內定義的物件的有序列表,並從零開始對它們進行編號。然後可以透過 children 命令引用這些物件。傳遞到 children 命令中的數字對應於在花括號內定義的第一個、第二個、第三個等物件,從零開始計數。在上面的示例中,花括號內只定義了一個物件。它要麼是 simple_wheel,要麼是 complex_wheel 物件。每次使用 children(0) 命令時都會建立此物件。children 命令本質上是將物件作為輸入傳遞給模組的一種方式。

以下示例可以幫助您更具體地理解這個概念。在前面的示例中,無法使用 axle_wheelset 模組並最終建立一個兩側具有不同車輪的車軸。即使在呼叫 axle_wheelset 模組時在花括號內傳遞/定義兩個不同的物件,這種情況也不會發生,因為兩側都只引用第一個物件,即 children(0)。

程式碼

axle_with_same_wheels_from_module.scad

axle_wheelset() {
    complex_wheel();
    simple_wheel();
}

為了建立一個兩側具有不同車輪的車軸,需要修改 axle_wheelset 模組的定義。axle_wheelset 模組不需要為兩側都引用第一個物件,即 children(0),而是需要為一側引用第一個物件,即 children(0),為另一側引用第二個物件,即 children(1)。

程式碼
module axle_wheelset(track=35, radius=2) {
    translate([0,track/2,0])
        children(0);
    axle(track=track, radius=radius);
    translate([0,-track/2,0])
        children(1);
}

透過在花括號內定義兩個不同的車輪物件,將建立以下模型。

程式碼

axle_with_different_wheels_from_parameterized_module.scad

axle_wheelset() {
    complex_wheel();
    simple_wheel();
}

練習
嘗試在呼叫 axle_wheelset 模組時交換車輪在花括號內定義的順序。會發生什麼?
程式碼

axle_with_flipped_different_wheels_from_parameterized_module.scad

axle_wheelset() {
    simple_wheel();
    complex_wheel();
}

練習
嘗試在花括號內只定義一個車輪?您是否收到錯誤訊息?
程式碼

axle_with_missing_wheel_from_parameterized_module.scad

axle_wheelset() {
    simple_wheel();
}

練習
在 vehicle_parts.scad 指令碼中新增一個 axle_wheel 模組。利用 children 命令引數化車輪設計的特定選擇。將 vehicle_parts.scad 指令碼用於另一個指令碼,以建立您喜歡的任何車輛設計。

挑戰

[edit | edit source]

您在過去兩章中學習的材料為您提供了一套強大的工具,可以開始建立您自己的物件庫,這些物件可以靈活地組合和定製,以建立新的設計。

練習
考慮您想要建立的任何模型。將其分解成不同的部分。為每個部分提出替代設計,並定義建立它們的模組。每個模組的輸入引數應該是什麼?使用 children 功能定義一個或多個模組,以便靈活地組合您建立的各個部分。
華夏公益教科書