跳轉到內容

OpenSCAD 教程/第 4 章

來自華夏公益教科書

定義和使用模組

[編輯 | 編輯原始碼]

上一章最後一個示例的指令碼變得相當長。這是因為用更復雜的輪子設計(需要建立許多語句)替換了簡單的圓柱形輪子(需要一個語句來建立)。要將輪子從簡單的設計更改為複雜的設計,您必須識別所有定義簡單輪子的圓柱體命令,並將它們替換為定義複雜輪子的命令。此過程聽起來類似於您必須經歷的更改輪子直徑的步驟。當沒有使用變數時,您必須識別指令碼中的對應值,並將它們一一替換為新值。這個重複且耗時的過程透過使用 wheel_radius 變數得到了改進,該變數使您能夠快速輕鬆地更改輪子的直徑。但是,當您想完全更改輪子的設計時,您能做些什麼來改進相應的容易出錯的過程呢?答案是肯定的!您可以使用模組,它類似於應用於整個部件/模型的變數。您可以將設計的一部分或甚至整個模型定義為模組。

首先,請回憶一下複雜輪子的設計。

程式碼

wheel_with_spherical_sides_and_holes.scad

$fa = 1;
$fs = 0.4;
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); 
}

您可以透過以下方式將上述輪子定義為模組。

程式碼

blank_model.scad

$fa = 1;
$fs = 0.4;
module 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);
    }
}

您需要做幾件事。您應該注意的第一件事是,為了定義模組,您必須鍵入 module 關鍵字,後跟要賦予該模組的名稱。在本例中,模組名為 wheel。模組名稱後面跟著一對括號。目前括號內沒有任何內容,因為還沒有為該模組定義任何引數。最後,括號後面跟著一對大括號。所有定義相應物件的命令都放在大括號內。末尾不需要分號。

您應該注意的第二件事是,OpenSCAD 還沒有建立任何輪子。這是因為您只是定義了 wheel 模組,但還沒有使用它。為了建立一個輪子,您需要新增一個建立輪子的語句,類似於新增建立任何基本物件(立方體、球體等)的語句。

程式碼

wheel_created_by_module.scad

$fa = 1;
$fs = 0.4;
module 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);
    }
}
wheel();

您可以將定義模組視為擴充套件 OpenSCAD 指令碼語言。當您定義了一個 wheel 模組時,就像擁有一個額外的可用基本物件。在本例中,新物件是您定義的輪子。然後,您可以像使用任何其他可用基本物件一樣使用此模組。

練習
嘗試在汽車的指令碼中定義上述輪子模組。嘗試使用定義的 wheel 模組建立汽車的輪子。
程式碼

car_with_wheels_created_by_module.scad

module 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);
    }
}
$fa = 1;
$fs = 0.4;
base_height = 10; 
top_height = 14; 
track = 35; 
body_roll = 0; 
wheels_turn = 0; 
rotate([body_roll,0,0]) {
    // 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([0,0,wheels_turn])
    wheel();
 // Front right wheel 
translate([-20,track/2,0])
    rotate([0,0,wheels_turn])
    wheel(); 
// Rear left wheel 
translate([20,-track/2,0])
    rotate([0,0,0])
    wheel(); 
// Rear right wheel 
translate([20,track/2,0])
    rotate([0,0,0])
    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);

引數化模組

[編輯 | 編輯原始碼]

在 wheel 模組中指定的輪子設計有許多可以用來自定義它的變數。這些變數是在 wheel 模組定義的大括號內定義的。因此,雖然 wheel 模組的輸出可以定製,但 wheel 模組本身只能建立輪子的一個版本,它對應於定義的變數的值。這意味著 wheel 模組不能用來為前後軸建立不同的輪子。如果您一直在瞭解引數化設計的良好實踐,您應該意識到這種情況是不希望的。如果 wheel 模組可以用來建立不同版本的輪子,那會好得多。為此,在 wheel 模組內定義和使用的變數,需要作為 wheel 模組的引數來定義,而不是直接在模組內部定義。這可以透過以下方式完成。

程式碼

wheel_created_by_parameterized_module.scad

$fa = 1;
$fs = 0.4;
module wheel(wheel_radius, side_spheres_radius, hub_thickness, cylinder_radius) {
    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);
    }
}
wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2);

您應該注意模組引數的定義在括號內。您還應該注意,每個引數的值不再在模組定義的大括號內分配。相反,引數的值是在每次呼叫模組時定義的。因此,該模組現在可以用來建立不同版本的輪子。

練習
嘗試在汽車的指令碼中定義上述 wheel 模組。嘗試使用 wheel 模組建立汽車的輪子。在呼叫 wheel 模組時,將 10、50、4 和 2 的值傳遞給相應的 wheel_radius、side_spheres_radius、hub_thickness 和 cylinder_radius 引數。
程式碼

car_with_wheels_created_by_parameterized_module.scad

module wheel(wheel_radius, side_spheres_radius, hub_thickness, cylinder_radius) {
    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);
    }
}
$fa = 1;
$fs = 0.4;
base_height = 10; 
top_height = 14; 
track = 35; 
body_roll = 0; 
wheels_turn = 0; 
rotate([body_roll,0,0]) {
    // 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([0,0,wheels_turn])
    wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2);
 // Front right wheel 
translate([-20,track/2,0])
    rotate([0,0,wheels_turn])
    wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2);
// Rear left wheel 
translate([20,-track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2);
// Rear right wheel 
translate([20,track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=10, side_spheres_radius=50, hub_thickness=4, cylinder_radius=2);
// 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);

練習
嘗試在汽車的指令碼中定義 wheel_radius、side_spheres_radius、hub_thickness 和 cylinder_radius 變數,並分別為它們分配 10、50、4 和 2 的值。嘗試使用這些變數在呼叫 wheel 模組時定義 wheel_radius、side_spheres_radius、hub_thickness 和 cylinder_radius 引數的值。
程式碼
wheel_radius=10;
side_spheres_radius=50;
hub_thickness=4;
cylinder_radius=2;
wheel(wheel_radius=wheel_radius, side_spheres_radius=side_spheres_radius, hub_thickness=hub_thickness, cylinder_radius=cylinder_radius);
練習
嘗試為前後軸定義不同的 wheel_radius、side_spheres_radius、hub_thickness 和 cylinder_radius 變數。嘗試為這些變數分配您喜歡的值的組合。請記住,還要在 wheel 模組的相應呼叫中編輯變數的名稱。
程式碼

car_with_different_wheels.scad

module wheel(wheel_radius, side_spheres_radius, hub_thickness, cylinder_radius) {
    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);
    }
}
$fa = 1;
$fs = 0.4;
base_height = 10;
top_height = 14;
track = 35;
body_roll = 0;
wheels_turn = 0;

wheel_radius_front=10;
side_spheres_radius_front=50;
hub_thickness_front=4;
cylinder_radius_front=2;

wheel_radius_rear=12;
side_spheres_radius_rear=30;
hub_thickness_rear=8;
cylinder_radius_rear=3;

rotate([body_roll,0,0]) {
    // 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([0,0,wheels_turn])
    wheel(wheel_radius=wheel_radius_front, side_spheres_radius=side_spheres_radius_front, 
    hub_thickness=hub_thickness_front, cylinder_radius=cylinder_radius_front);
 // Front right wheel 
translate([-20,track/2,0])
    rotate([0,0,wheels_turn])
    wheel(wheel_radius=wheel_radius_front, side_spheres_radius=side_spheres_radius_front, 
    hub_thickness=hub_thickness_front, cylinder_radius=cylinder_radius_front);
// Rear left wheel 
translate([20,-track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius_rear, side_spheres_radius=side_spheres_radius_rear, 
    hub_thickness=hub_thickness_rear, cylinder_radius=cylinder_radius_rear);
// Rear right wheel 
translate([20,track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius_rear, side_spheres_radius=side_spheres_radius_rear, 
    hub_thickness=hub_thickness_rear, cylinder_radius=cylinder_radius_rear);
// 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);

定義模組引數的預設值

[編輯 | 編輯原始碼]

您可以為 wheel 模組的引數設定特定的值組合作為預設值。這可以透過以下方式實現。

程式碼
$fa = 1;
$fs = 0.4;
module 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);
    }
}

您應該注意,預設值是在模組定義的括號內分配的。透過為模組的引數定義預設值,您在使用 wheel 模組的方式上有了更大的靈活性。例如,使用該模組最簡單的方法是在呼叫它時不指定任何引數。

程式碼

wheel_created_by_default_parameters.scad

…
wheel();
…

如果在呼叫 wheel 模組時沒有指定引數的值,則使用該引數的預設值。預設值可以設定為與輪子的最常用版本相同。預設值可以透過在呼叫 wheel 模組時為相應引數分配新值來覆蓋。可以覆蓋零個或任何數量的預設值。因此,透過指定預設值,wheel 模組可以使用以下任何方式以及更多方式使用。

程式碼

wheel_created_by_default_parameters.scad

…
wheel();
…

程式碼

wheel_with_thicker_hub.scad

…
wheel(hub_thickness=8);
…

程式碼

wheel_with_thicker_hub_and_larger_radius.scad

…
wheel(hub_thickness=8, wheel_radius=12);
…

練習
在 wheel 模組的定義中包含預設值。嘗試透過覆蓋任何數量的預設值來建立幾個輪子。您能製作出如下所示的輪子嗎?

程式碼

wheel_with_larger_side_radius.scad

…
wheel(side_spheres_radius=10);
…

將整個模型分成模組

[編輯 | 編輯原始碼]

使用模組是 OpenSCAD 的一個非常強大的功能。您應該開始將您的模型視為模組的組合。例如,汽車模型可以被認為是車身、輪子和軸模組的組合。這打開了進一步重用和重新組合模組以建立不同模型的可能性。

練習
嘗試定義一個車身模組和一個車軸模組。車身和車軸模組應該有哪些引數?嘗試使用車身、輪子和車軸模組重新建立汽車。為輪子模組的引數提供一組預設值,對應於前輪。透過在指令碼中定義適當的變數,在建立後輪時向輪子模組傳遞不同的值。為車身和車軸模組的引數也設定預設值。
程式碼

car_with_different_wheels_and_default_body_and_axle.scad

module 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);
    }
}

module body(base_height=10, top_height=14, base_length=60, top_length=30, width=20, top_offset=5) {
    // Car body base
    cube([base_length,width,base_height],center=true);
    // Car body top
    translate([top_offset,0,base_height/2+top_height/2 - 0.001])
        cube([top_length,width,top_height],center=true);
}

module axle(track=35, radius=2) {
    rotate([90,0,0])
        cylinder(h=track,r=radius,center=true);
}

$fa = 1;
$fs = 0.4;
wheelbase = 40;
track = 35; 
body_roll = 0; 
wheels_turn = 0; 
wheel_radius_rear=12;

// Body
rotate([body_roll,0,0]) {
    body();
}
// Front left wheel 
translate([-wheelbase/2,-track/2,0])
    rotate([0,0,wheels_turn])
    wheel();
 // Front right wheel 
translate([-wheelbase/2,track/2,0])
    rotate([0,0,wheels_turn])
    wheel();
// Rear left wheel 
translate([wheelbase/2,-track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius_rear);
// Rear right wheel 
translate([wheelbase/2,track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius_rear);
// Front axle 
translate([-wheelbase/2,0,0])
    axle(); 
// Rear axle 
translate([wheelbase/2,0,0])
    axle();

練習
嘗試重複使用車身、輪子和車軸模組來建立一個類似於以下的車輛。

程式碼

car_with_six_wheels.scad

module 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);
    }
}

module body(base_height=10, top_height=14, base_length=60, top_length=30, width=20, top_offset=5) {
    // Car body base
    cube([base_length,width,base_height],center=true);
    // Car body top
    translate([top_offset,0,base_height/2+top_height/2 - 0.001])
        cube([top_length,width,top_height],center=true);
}

module axle(track=35, radius=2) {
    rotate([90,0,0])
        cylinder(h=track,r=radius,center=true);
}

$fa = 1;
$fs = 0.4;
track = 35; 
body_roll = 0; 
wheels_turn = 0;
base_length = 100;
top_length = 75;
top_offset = 5;
front_axle_offset = 30;
rear_axle_1_offset = 10;
rear_axle_2_offset = 35;
wheel_radius = 12;

// Body
rotate([body_roll,0,0]) {
    body(base_length=base_length, top_length=top_length, top_offset=top_offset);
}
// Front left wheel 
translate([-front_axle_offset,-track/2,0])
    rotate([0,0,wheels_turn])
    wheel(wheel_radius=wheel_radius);
 // Front right wheel 
translate([-front_axle_offset,track/2,0])
    rotate([0,0,wheels_turn])
    wheel(wheel_radius=wheel_radius);
// Rear left wheel 1
translate([rear_axle_1_offset,-track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius);
// Rear right wheel 1
translate([rear_axle_1_offset,track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius);
// Rear left wheel 2
translate([rear_axle_2_offset,-track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius);
// Rear right wheel 2
translate([rear_axle_2_offset,track/2,0])
    rotate([0,0,0])
    wheel(wheel_radius=wheel_radius);
// Front axle 
translate([-front_axle_offset,0,0])
    axle(); 
// Rear axle 1
translate([rear_axle_1_offset,0,0])
    axle();
// Rear axle 2
translate([rear_axle_2_offset,0,0])
    axle();
華夏公益教科書