Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lego Brick

Picture

This tutorial will introduce you to the basic concepts of the µCAD language.

Objective

The goal is to create a parametric Lego brick part and provide it as a reusable library.

Needed Skills

Basic programming and CAD knowledge are required to complete this tutorial.

How to construct a Lego brick

Before designing a fully customizable Lego brick, we will first construct a brick of fixed size and make it customizable afterwards.

We will design three components by extrusion of the following 2D sketches:

  • Base: A rectangular frame with struts (purple).
  • Cap: A rectangular top plate that closes the base structure (cyan).
  • Knobs: The knobs of a Lego brick placed on top of the cap (green).

Picture of all three

Next, we will use the sketches to create a 3D model of a Lego brick.

Finally, we will compile everything into a proper library.

Preparation

Before we can start, you may need to install µcad itself, as well as some other useful tools.

Install µcad

µcad can be installed with user privileges.

Install rust

First, you may need to install rust as described here rustup.rs:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Install µcad command line tool

Then, execute this command on the shell to install microcad:

cargo install microcad

Install Plugins (VSCode)

µcad brings a VSCode plugin with syntax coloring which you might want to install from our source code repository at github.com.

Other useful plugins

Install SVG Preview

You might want to install Svg Preview for VSCode.

Install STL Viewer extension

Another useful extension is the vscode-stl-viewer.

Create new µcad file

Before we design any geometry, we use the microcad command line tool to create a new µcad project:

microcad create lego_brick

This will create a file lego_brick.µcad.

Let's open this file in VSCode:

test

// microcad generated file

sketch YourSketch( /* your building plan */ ) {
    // your code
}

// create YourSketch
YourSketch();

We can export the file using the following command:

microcad export lego_brick

Nothing will be exported because the sketch does not contain any output geometry. Therefore, let's add some!

Sketch the base

The base sketch will consist of a frame and three rings that will become the struts.

Base

First, we will create the frame...

Creating the frame

The first geometry we want to construct is the frame of the brick's base. It consists of an outer and an inner rectangle.

Frame

The outer frame is a rectangle with a width = 31.8mm and a height = 15.8mm. From that we will derive the inner frame by using thickness = 1.2mm as parameter.

Creating a rectangle

To construct a rectangle in µcad, we use a sketch with the name std::geo2d::Rect.

Open the lego_brick.µcad file you have created before, delete all contents and replace it with the following single statement:

test

std::geo2d::Rect(width = 31.8mm, height = 15.8mm);

Picture

The above statement calls the built-in sketch std::geo2d::Rect with the parameters width and height set to our measures. Like every statement in µcad, it ends with a semicolon (;). Executing this statement will eventually construct the actual geometry.

As you can see, arguments in µcad are quite explicit. There are no positional parameters in µcad! Instead, arguments must be provided with an identifier or match unambiguously by type.

Also you can see that in µcad all values are attached to a unit (like mm in the above code). The unit implicitly defines the type (e.g. using mm will lead to a Length type). If you calculate with those values, units will be calculated too! So if you multiply a length with another you will get a value of type Area (e.g. mm²).

Creating a second rectangle

Like the outer frame, the inner frame is a std::geo2d::Rect too:

test

thickness = 1.2mm;
std::geo2d::Rect(width = 31.8mm - 2 * thickness, height = 15.8mm - 2 * thickness);

Picture

We have defined a new value thickness = 1.2mm to store the frame's wall thickness. Then, we construct a rectangle by taking the original width and height and subtracting twice the thickness from both.

We can now output the inner and outer geometry simultaneously. Similar to the thickness = 1.2mm, we also assign width and height their respective values to shorten the code:

test

thickness = 1.2mm;
width = 31.8mm;
height = 15.8mm;
std::geo2d::Rect(width, height);
std::geo2d::Rect(width = width - 2 * thickness, height = height - 2 * thickness);

Picture

Because the arguments we give to the first std::geo2d::Rect() match exactly the parameter names of it we do not need to write extra parameter names here. This is called auto-matching. It prevents us from having to write the argument names twice:

std::geo2d::Rect(width = width, height = height);

Now, we can execute the export command from the command line tool:

microcad export lego_brick.µcad

The export command will produce a Scalable Vector Graphic (SVG) file named lego_brick.svg next to the lego_brick.µcad file. By default, all 2D geometries are exported to SVG.

Congratulations, you have exported your first 2D geometry with µcad!

Although the measurements of these rectangles are correct, our intention was to create a frame in which the both rectangles define the boundary of the frame. To achieve this, we will use an operation to combine them.

Subtract

Until now, we have two rectangles which are generated within the same file but to build a frame, we have to combine them.

Grouping statements

The first step is to bundle the rectangles into a group with curly brackets {}. So let's put some around the frame's rectangles.

test

thickness = 1.2mm;
width = 31.8mm;
height = 15.8mm;
{
    std::geo2d::Rect(width, height);
    std::geo2d::Rect(width = width - 2 * thickness, height = height - 2 * thickness);
}

Picture

As you can see, there is no ; after the braces.

The visual output did not change by using the group braces. However, now we can combine both rectangles by using an operation.

Manipulate geometry with Operations

In µcad, the operation to subtract a geometry from one another is called subtract. In our case, we want to subtract the outer part by the inner part in our frame group:

test

thickness = 1.2mm;
width = 31.8mm;
height = 15.8mm;
{
    std::geo2d::Rect(width, height);
    std::geo2d::Rect(width = width - 2 * thickness, height = height - 2 * thickness);
}.std::ops::subtract();   // Apply the operation.

Picture

Now the semicolon is back, because we added the operation.

Use Statement

You might be wondering why we always have to write std::geo2d:: and std::ops:: in front of Rect and subtract. This is because builtin sketches (and parts) in µcad are organized within modules in a standard library. std is the name of the top module of this library and geo2d is a submodule of std and contains all built-in sketches.

Writing std::ops and std::geo2d in front of each element seems redundant and cumbersome. Luckily, µcad has syntax elements called use statements. Apart from the shorter code, another useful feature of the statement is that it allows you to explicitly specify which parts of a module you want to use throughout the source file. This means instead of the previous code, we can simply write:

test

use std::geo2d::Rect;
use std::ops::subtract;

thickness = 1.2mm;
width = 31.8mm;
height = 15.8mm;
{
    Rect(width, height);
    Rect(width = width - 2 * thickness, height = height - 2 * thickness);
}.subtract();

Picture

As you can see, this makes the code much simpler and clearer.

Naming models

During the design process, we will add more geometry to our design. Therefore, it is useful to identify each sub-geometry by a name. In the next we want to give the rectangle a name outer, so we can identify it more easily:

test

outer = std::geo2d::Rect(width = 31.8mm, height = 15.8mm);

By adding outer = to the call std::geo2d::Rect(..), we have created an assignment. Now, the output rectangle will be stored in the value outer. However, when we export the file again via microcad export lego_brick.µcad, you will notice that nothing is exported.

Why? Because in µcad, assignments are not part of the output geometry. A second statement outer; is needed to output the geometry stored in the outer value.

test

outer = std::geo2d::Rect(width = 31.8mm, height = 15.8mm);
outer;

Picture

Naming the rectangles leads to some better readability but the code will get quite a bit longer, because for each rectangle we now need a second statement to render them.

This makes more sense, if you use an Operator (-) instead of an Operation (subtract()) to combine them.

test

use std::geo2d::Rect;

thickness = 1.2mm;
width = 31.8mm;
height = 15.8mm;

// name both rectangles
outer = Rect(width, height);
inner = Rect(width = width - 2 * thickness, height = height - 2 * thickness);

// what was { .. }.subtract() before:
outer - inner;

Picture

Now any reader can easily understand what's going on.

As you might have mentioned, we do not need the line use std::ops::subtract; anymore. This is because µcad brings some builtin binary operators which are hard-linked to builtin operations:

OperatorBuiltin OperationDescription
-__builtin::subtractGeometrical difference
|__builtin::unionGeometrical union
&__builtin::intersectGeometrical intersection

Those builtin operations are from the builtin library which can be found within the global module __builtin.

Usually there is no need to use the builtin library directly, because all builtin functionalities are also accessible via the standard library and with a more convenient interface.

Finish the frame

Now we have successfully created a model which describes the frame by the difference of two rectangles.

At first glance, it might seem a bit cumbersome to go through multiple steps just to create a simple frame. However, this example was intentionally designed to introduce you to the fundamental concepts of µcad - such as workbenches, operations and groups. Those foundational steps give you a clearer understanding of how µcad works under the hood.

Fortunately, µcad has a ready-to-go sketch in the std library to construct a frame geometry like we have constructed: the Frame sketch. Using it, we can achieve the same result as before but with a much simpler expression:

test

// Include all from std::geo2d using * (including Frame)
use std::geo2d::*;

thickness = 1.2mm;
width = 31.8mm;
height = 15.8mm;

// Construct a frame
Frame(width, height, thickness);

Picture

It looks fine, so let's continue with the struts...

Creating the struts

Now after we have finished the frame, we can start with the struts.

Struts

Looking at it, we find three inner and three outer circles with the measures of 4.8 mm and 6.51 mm. Quite similar to the rectangles we have used in Frame, but now there are three times more elements.

Creating circles

We now can easily construct a single strut by using std::geo2d::Circle() and repeat the lesson we had before, but our Lego brick needs three of them.

The shape of each strut remains the same - they’re simply offset horizontally by 8mm to left and right. From the concept we know already, a first solution could be to write the strut three times and translate them using std::ops::translate().

test

use std::geo2d::*;
use std::ops::*;

Circle(d = 6.51mm) - Circle(d =  4.8mm);
(Circle(d = 6.51mm) - Circle(d =  4.8mm)).translate(x = 8mm);
(Circle(d = 6.51mm) - Circle(d =  4.8mm)).translate(x = -8mm);

Picture

The code above produces the expected result. However, the code is quite repetitive. We could improve it slightly by storing the expression Circle(d = 6.51mm) - Circle(d = 4.8mm) in a value, say strut. But even then, we’d still need to write out each translate(...) call manually.

And more importantly: What if we wanted the number of struts to be flexible or generated dynamically?

To solve this in a clean and scalable way, µcad supports multiplicity, allowing us to generate repeated geometry with minimal, reusable code.

Let’s explore that next.

TODO

  • Do we need to explain that we now uses ( .. ).translate() instead of curly brackets?

Multiplicity

To avoid having to call translate multiple times, µcad provides a powerful feature called multiplicity. Instead of applying translate() separately for each position, you can pass it an array of values. An array of values is expressed with [] brackets. µcad then will automatically apply the operation once for each value in the array.

This allows us to shorten the previous example significantly:

test

use std::geo2d::*;
use std::ops::*;

(Circle(d = 6.51mm) - Circle(d = 4.8mm)).translate(x = [-8mm, 0mm, 8mm]);

Picture

With just a single line of code, we've created three struts - each correctly positioned! This approach is not only more concise, but also easier to maintain and scale, especially if you later want to add more positions dynamically.

But we still have to write 8mm twice, but we can change this be multiplying the array with that value:

test

use std::geo2d::*;
use std::ops::*;

(Circle(d = 6.51mm) - Circle(d = 4.8mm)).translate(x = [-1, 0, 1] * 8mm);

test

Ranges

The term [-1, 0, 1] can be replaced with a range expression [-1..1], which yields the array we need:

test

use std::geo2d::*;
use std::ops::*;

(Circle(d = 6.51mm) - Circle(d = 4.8mm)).translate(x = [-1..1] * 8mm);

Picture

A range expression has the syntax [m..n] where m and n have to be of type Integer.

As you might have mentioned µcad ranges differ from ranges in other languages (like Rust): They include the end value - in Rust you would have to write [-1..=1] to achieve the same if you write [-1..1] in µcad.

Finish the struts

Maybe you already question yourself if there is something similar to std::geo2d::Frame() for our circles?

And yes there is, and it's called std::geo2d::Ring. So let's shorten our strut code a last time:

test

use std::geo2d::*;
use std::ops::*;

Ring(outer_d = 6.51mm, inner_d = 4.8mm).translate(x = [-1..1] * 8mm);

Picture

At this point, we are almost finished with the base. We just have to find a way to combine frame and structs.

Union operation

We can combine the frame and the struts into a single geometry by using the union operation or the | operator.

The code in the lego_brick.µcad with the whole 2D geometry of the brick's base will look like this:

test

use std::geo2d::*;
use std::ops::*;

thickness = 1.2mm;
width = 31.8mm;
height = 15.8mm;
frame = Frame(width, height, thickness);
struts = Ring(outer_d = 6.51mm, inner_d = 4.8mm)
             .translate(x = [-1..1] * 8mm);

frame | struts; // We could also write `{ frame; struts; }.union()` but the `|` operator is more elegant. 

If you export the file, you will see a frame and the structs combined into a single object.

Picture

Create a sketch for the base

Now, we want to turn the construction of the Lego brick base into a reusable, parametric component. In µcad, a reusable, parametric component that produces or transforms a geometry is called workbench.

There are three kinds of workbenches:

  • sketches: produce 2D geometry, e.g. Rect.
  • parts: produce 3D geometry, e.g. Sphere.
  • op: Turn some input geometry into output geometry, e.g. translate, union or subtract.

Definition of our first sketch

Let's encapsulate the construction of the frame into a sketch workbench called Base.

test

use std::geo2d::*;
use std::ops::*;

sketch Base(width: Length, height: Length, thickness = 1.2mm) {
    frame = Frame(width, height, thickness);
    struts = Ring(outer_d = 6.51mm, inner_d = 4.8mm)
             .translate(x = [-1..1] * 8mm);
    frame | struts;
}

Base(width = 31.8mm, height = 15.8mm);

Picture

If we examine the syntax of the above example, we can see the following things:

  • Names of sketches are commonly written in PascalCase, starting with a capital letter.
  • The sketch Base has 3 parameters width, height and thickness. Together they are called the building plan of Base.
  • width and height have the type Length and no default value, they are required.
  • thickness is also of type Length, but implicitly, because we have defined a default value 1.2mm which is a Length of unit mm.
  • The body { ... } of Base constructs the actual geometry.
  • Base(width = 15.8mm, height = 31.8mm) is a call of the sketch.

And the best part: We don't even need additional value stores for our measures like thickness, width etc. Every measure has a meaningful name in the parameters. This makes the code clearer and changes easier.

An analogy to natural language

In the previous sections, we have been introduced to main concepts of µcad. If we draw an analogy to natural language, we can summarize:

  • The workbenches Base, Frame and Circle act like a noun, the subject of the sentence - it's the geometry being described or manipulated.
  • The operation translate function like verbs, indicating operations being applied to the geometry.
  • The parameters x = 20mm and 45° serve as adverbs, specifying how the operations are carried out.
  • Groups {} serve as subclauses.
  • Assignments a = Rect(...) are used to give things a unique name: a is a rectangle.

This analogy helps illustrate how the µcad syntax is designed to be both readable and logical, resembling the structure of natural language in a way that makes the code easier to understand.

Now, we have seen all concepts to actually design our Lego brick in 3D.

Sketch caps & knobs

With the knowledge we have gained in the previous chapter, the remaining sections of the brick (Cap and Knobs) can be constructed swiftly.

Cap

The Cap is nothing more than a rectangle. We do not have to define a specific sketch Cap, instead we can use an alias:

test

// alias Rect as Cap
use std::geo2d::Rect as Cap;

Cap(width = 31.8mm, height = 15.8mm);

Picture

std::geo2d::Rect will now also be known as Cap.

Knobs

The knobs of the brick are simple circles with a diameter of 4.8mm. We can easily construct a grid with circles via multiplicity:

test

std::geo2d::Circle(d = 4.8mm, c = (
        x = ([-1..2] - 0.5) * 8mm, 
        y = ([0..1] - 0.5) * 8mm)
    );

Picture

Because ranges can only deal with integers, calculating x and y is a bit tricky here, because x is [-1..2] (which is [-1, 0, 1, 2]) and y is [0..1], we now have no element in the middle anymore. So we have to subtract 0.5 from all the array values before multiplying with 8mm to get a centered result.

To avoid this complication we can use the operation center(). By default, if we do not pass any arguments to operation, it will center the object to origin.

test

std::geo2d::Circle(d = 4.8mm, c = (
        x = [0..3] * 8mm, 
        y = [0..1] * 8mm)
    ).std::ops::center();

Picture

The code looks clearer now.

Tuples

Notice that we have called the std::geo2d::Circle with an additional argument c. c is given as a tuple (x = ..., y = ...). A tuple is a collection of (mostly named) values.

The parameter c of a circle is supposed to be a tuple of type (x: Length, y: Length). By passing an array of Length to the tuple, we are generating a multiplicity, which eventually creates 2*4 circles.

Let's create a sketch for the knobs:

test

sketch Knobs(diameter = 4.8mm) {
    std::geo2d::Circle(d = 4.8mm, c = (x = [0..3] * 8mm, y = [0..1] * 8mm))
        .std::ops::center();
}

Knobs();

Picture

We now have all the sketches we need! Let's move on and bring them together...

Putting it together

Below is an intermediate result of the sketches of the three components which we now successfully have defined:

test

use std::geo2d::*;
use std::ops::*;

const SPACING = 8mm;

sketch Base(width: Length, height: Length) {
    thickness = 1.2mm;
    frame = Frame(width, height, thickness);
    struts = Ring(outer_d = 6.51mm, inner_d = 4.8mm)
        .translate(x = [0..2] * SPACING)
        .center();
    frame | struts;
}

use Rect as Cap;

sketch Knobs() {
    Circle(d = 4.8mm, c = (x = [0..3] * SPACING, y = [0..1] * SPACING))
        .center();
}

width = 31.8mm;
height = 15.8mm;

Base(width, height);
Cap(width, height);
Knobs();

Picture

Across the Lego universe, the spacing of 8mm is used everywhere. To address this we can store it in a constant named SPACING using the const keyword. This makes SPACING available in all sketches within that file (or module). The name of a constant must be in capital letters. It can be used from within the current module/file and from all sketches and parts within that module.

In the next steps, we want to create a 3D geometry.

Create a part: LegoBrick

Now that we have brought everything together in 2D, we want to create a proper part using the sketches we developed to form a 3D Lego brick.

Extrude into 3D

Now, we want to convert the three 2D sketches into a 3D geometry part. This can be achieved by extrusion. The corresponding µcad operation is called std::ops::extrude.

As a first example, let's take the cap of the brick and extrude it by 1.0 mm into 3D:

test

use std::ops::extrude;
use std::geo2d::Rect as Cap;

width = 15.8mm;
height = 31.8mm;

Cap(width, height)
    .extrude(1.0mm);

This will create a box with dimensions 15.8 ⨯ 31.8 ⨯ 1.0 mm.

Note that with std::ops::extrude() we will extrude along Z-axis by default.

Picture

Create a first version of the LegoBrick

Let's make a brick out of our Base, the Knobs and the Cap sketches and integrate everything into a part.

We extrude Base, Knobs and Cap and translate them in Z direction if necessary. Afterwards, we combine the three components using the | operator.

test

use std::ops::*;
use std::geo2d::*;

const SPACING = 8mm;

sketch Base(width: Length, height: Length) {
    thickness = 1.2mm;
    frame = Frame(width, height, thickness);
    struts = Ring(outer_d = 6.51mm, inner_d = 4.8mm)
        .translate(y = [0..2] * SPACING)
        .center();
    frame | struts;
}

use Rect as Cap;

sketch Knobs() {
    center = (x = [0..1] * SPACING, y = [0..3] * SPACING);
    Circle(diameter = 4.8mm, center).center();
}

part LegoBrick(base_height = 9.6mm) {
    width = 15.8mm;
    height = 31.8mm;
    top_thickness = 1.0mm;

    base = Base(width, height)
        .extrude(base_height);

    cap = Cap(width, height)
        .extrude(top_thickness)
        .translate(z = base_height - top_thickness);

    knobs = Knobs()
        .extrude(1.7mm)
        .translate(z = base_height);

    // Combine all components
    base | cap | knobs;
}

LegoBrick();

When we export the code snippet above, an STL file will be exported instead of an SVG file.

Picture

Make a parameterization concept

In this step, we want to make the Lego brick part fully parametric and reusable. Via using parameters, we want to:

  • control the number of knobs in both directions

  • control the brick's height

  • create a reusable Lego brick library so that we can write this:

    lego_brick::LegoBrick(rows = 2, columns = 2, base_height = 9.6mm * 2);
    
  • All our sketches shall lay beside that LegoBrick inside a new module lego_brick.

First, though, we need to find a way to place elements more generically than before.

Custom operation

The knobs and struts are created using multiplicity by translate and align operations.

To make placing the elements more generic we will create an operation called grid which arranges elements in a grid which is centered to origin:

test

use std::ops::*;

const SPACING = 8mm;

op grid(columns: Integer, rows: Integer) {
    @input
        .translate(x = [0..columns] * SPACING, y = [0..rows] * SPACING)
        .center()
}

The grid operation takes rows and columns as parameters.

Operations - as we already know - have not only an output geometry but an input geometry as well. To be able to access those input geometry we need to use the keyword @input. With @input we insert the elements that are given by the caller. In our case that will be a knob or a strut sketch.

We now can rewrite Knobs and Frame sketches by adding rows and columns as parameter and using the grid operation:

test

use std::geo2d::*;
use std::ops::*;

const SPACING = 8mm;

op grid(columns: Integer, rows: Integer) {
    @input
        .translate(x = [1..columns] * SPACING, y = [1..rows] * SPACING)
        .center()
}

sketch Base(
    columns: Integer,
    rows: Integer,
    width: Length,
    height: Length
) {
    thickness = 1.2mm;
    frame = Frame(width, height, thickness);
    struts = Ring(outer_d = 6.51mm, inner_d = 4.8mm)
        .grid(columns = columns-1, rows = rows-1);
    frame | struts;
}

sketch Knobs(columns: Integer, rows: Integer) {
    Circle(d = 4.8mm)
        .grid(columns, rows);
}

use Rect as Cap;

columns = 4;
rows = 2;
width = columns * SPACING - 0.2mm;
height = rows * SPACING - 0.2mm;

Base(rows, columns, width, height);
Cap(width, height);
Knobs(rows, columns);

Picture

Additionally, to the grid operation, we compute the overall width and height in the LegoBrick part, which are:

  • width = rows * 8mm - 0.2mm
  • height = columns * 8mm - 0.2mm

Now we are ready to write the final part of a LegoBrick.

The final part

First we add some default values for rows and columns in the building plan and use them in the last statement where we call LegoBrick().

test

use std::geo2d::*;
use std::ops::*;

const SPACING = 8mm;

op grid(columns: Integer, rows: Integer) {
    @input
        .translate(x = [1..columns] * SPACING, y = [1..rows] * SPACING)
        .center()
}

sketch Base(
    columns: Integer,
    rows: Integer,
    width: Length,
    height: Length
) {
    thickness = 1.2mm;
    frame = Frame(width, height, thickness);
    struts = Ring(outer_d = 6.51mm, inner_d = 4.8mm)
        .grid(columns = columns-1, rows = rows-1);
    frame | struts;
}

use Rect as Cap;

sketch Knobs(columns: Integer, rows: Integer) {
    Circle(d = 4.8mm)
        .grid(columns, rows);
}

part LegoBrick(rows = 2, columns = 4, base_height = 9.6mm) {
    width = columns * SPACING - 0.2mm;
    height =rows * SPACING - 0.2mm;
    cap_thickness = 1.0mm;

    base = Base(rows, columns, width, height)
        .extrude(base_height - cap_thickness);

    cap = Cap(width, height)
        .extrude(cap_thickness)
        .translate(z = base_height - cap_thickness);

    knobs = Knobs(rows, columns)
        .extrude(1.7mm)
        .translate(z = base_height);

    base | cap | knobs;
}

// render a brick with default values
LegoBrick();

Picture

Let's make a library out of it, and use it from another file in the next section.

External module

Let's assume we want to use the LegoBrick from an external file.

Fortunately, this is simple! We just have to create a second file my_brick.µcad:

microcad create my_brick

The directory structure is supposed to contain these files:

lego_brick.µcad
my_brick.µcad

Let's add the following content to the my_brick.µcad file to create a few bricks with different parameters:

mod lego_brick;

use lego_brick::*;

// 2x2 double height
double_2x2 = LegoBrick(rows = 2, columns = 2, base_height = 9.6mm * 2);

// 4x2 single height
single_4x2 = LegoBrick(rows = 4, columns = 2);

// 3x2 one-third height
third_3x2 = LegoBrick(rows = 3, columns = 2, base_height = 3.2mm);

// generate geometry placing all elements side by side
use std::ops::translate;

single_4x2;
double_2x2.translate(y = -40mm);
third_3x2.translate(y = 40mm);

Picture

As you can see in the first line we use a mod statement to load our external module lego_brick.

Visibility

To make this work, we also need to change one line in our final part:

pub part LegoBrick(rows = 2, columns = 4, base_height = 9.6mm) {

Here we add the keyword pub to make LegoBrick visible from outside modules (like our my_brick.µcad):

Now you can export my_brick.µcad to generate the result of our tutorial.

Control Export

Until now when we export the whole the geometry as an STL this results in a single output file:

microcad export lego_brick

But we also can use the #[export] attribute to export each brick to a different file:

mod lego_brick;

use lego_brick::*;

#[export = "double_2x2.stl"]
double_2x2 = LegoBrick(rows = 2, columns = 2, base_height = 9.6mm * 2);

#[export = "single_4x2.stl"]
single_4x2 = LegoBrick(rows = 4, columns = 2);

#[export = "third_3x2.stl"]
third_3x2 = LegoBrick(rows = 5, columns = 1, base_height = 3.2mm);

When we export the file now, three files with the specified names will be created and we do not need the translate() operations anymore.

TODO

  • It seems unclear why the export attribute is used at assignments when we said before, that assignments will not generate any geometry.

Recap

In this tutorial you should have learned about the following things:

  • Installing µcad
  • Creating a new µcad file
  • Using basic 2D primitives in std::geo2d: Rect, Circle, Frame
  • Define and use named values and models
  • Combining geometries with std::ops::subtract and std::ops::union
  • Geometrical operators: |, -
  • Using the use statement
  • Move geometries with std::ops::translate
  • Arrays
  • Argument multiplicity
  • Ranges
  • Constants
  • Extruding sketches with std::ops::extrude
  • Creating custom sketches, parts and operations
  • Separating code into external modules
  • Controlling export with attributes

Congratulations, you now have successfully finished this tutorial!

If you could not get enough, try to master the additional exercises...

Additional Exercises

  1. Play around with the parameters of LegoBrick, e.g. create a 5x7 brick.
  2. Can you provoke any errors by bad parameters?
  3. Use multiplicity to create three Lego bricks.
  4. Make multiple Lego bricks from two arrays of given sizes (rows and columns).
  5. Our tutorial missed some details of the real Lego brick. Can you add them from these specifications?