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 库 (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 功能定义一个或多个模块,以便灵活地组合您创建的各个部分。 |