2D object to cylinder


I have Zentangle bracelet and Generalized zentangle bracelet on Thingiverse.

Zentangle bracelet

Generalized zentangle bracelet

The patterns of these two works are from Symmetrical triangle generator.

Symmetrical triangle generator

How did I design these works? The bracelet is a cylinder, but patterns are 2D. You may draw lines on the surface of a cylinder directly, such as Random maze cylinder generator. But, the generator used only horizontal and vertical lines. Using this method is not too complex. The patterns, in the beginning, have a variety of directions, so drawing lines on a cylinder will require complex mathematics and implementation.

Circle again

Let's leave the above works alone temporarily and think from the other direction. How can you draw a character on a cylinder? The simplest way is projecting the character to a cylinder. For example.

radius = 10;
height = 20;
thickness = 1;
$fn = 24;

render() intersection() {
    difference() {
        cylinder(
            r1 = radius + thickness, 
            r2 = radius + thickness, 
            h = height, center = true);
        cylinder(
            r1 = radius, 
            r2 = radius, 
            h = height, center = true);
    }

    rotate([90, 0, 0]) linear_extrude(radius + thickness) 
        text("A", size = height, valign = "center", halign = "center");
}

It works. The character looks like this.

2D object to cylinder

But, this method cannot achieve the effect like this.

2D object to cylinder

The cylindrical projection cannot cross over 180 degrees because you simply linear_extrude the 2D text.

Recall what I mentioned in Circle, you can use triangles to make a circle. How about a cylinder? You just have to linear_extrude a circle so that a cylinder can consist of triangles, too. You can line up the triangles and intersect with a horizontal model linearly extruded from a 2D object.

2D object to cylinder

This operation will get several arcs, trapezoids. Next, wrap these arcs to form a circle.

2D object to cylinder

You'll get a cylinder with the 2D drawing on its surface. Isn't it simpler than drawing lines on the cylinders?

Implementation

Before writing code, we have to determine the dimension of the 2D drawing. For convenience, we position the 2D drawing as below.

2D object to cylinder

Now that, if we want to put a character on the cylinder, we should place the character as below.

linear_extrude(thickness) 
    translate([font_size / 2, font_size / 2, 0])
        rotate(90) 
            text("A", size = font_size, valign = "center", halign = "center");

2D object to cylinder

We have to define a module which creates triangles required by a circle. Every triangle is an isosceles triangle, and the legs' length is the radius of the circle.

We can use the built-in polygon module to create triangles. If a circle is composed of fn triangles, the base angle is a = 360 / fn. If the apex is placed at the origin, the coordinates of the other two vertexes are [radius * cos(a / 2), radius * sin(a / 2)] and [radius * cos(a / 2), -radius * sin(a / 2)]. According to the above, we define a one_over_fn_for_circle module below.

radius = 10;
$fn = 24;

module one_over_fn_for_circle(radius, fn) {
    a = 360 / fn;
    x = radius * cos(a / 2);
    y = radius * sin(a / 2);
    polygon(points=[[0, 0], [x, y],[x, -y]]);
}

one_over_fn_for_circle(radius, $fn);

2D object to cylinder

Then, we make the character stand up.

2D object to cylinder

And use triangles to intersect with the character.

2D object to cylinder

The circumference of the circle should equal the length of the 2D object, so 2 * PI * radius = length. That is radius = length / (2 * PI).

We may loop to create a required triangle, move them to the correct position, intersect with the character, move it back, and rotate an angle to form a circle. So, if you just want to wrap a character, you may implement it as follow.

tx = "A";
font_size = 20;
thickness = 1;
$fn = 24;

module one_over_fn_for_circle(radius, fn) {
    a = 360 / fn;
    x = radius * cos(a / 2);
    y = radius * sin(a / 2);
    polygon(points=[[0, 0], [x, y],[x, -y]]);
}

module text_to_cylinder(tx, font_size, thickness, fn) {
    r = font_size / 6.28318;
    a = 360 / fn;
    y = r * sin(a / 2);
    for(i = [0 : fn - 1]) {
        // move it back and rotate to form a circle
        rotate(a * i) translate([0, -(2 * y * i + y), 0])

        intersection() {
            // line up the triangle
            translate([0, 2 * y * i + y, 0]) 
                linear_extrude(font_size) 
                    one_over_fn_for_circle(r, fn);

            // make the character stand up
            translate([r - thickness, 0, font_size]) 
              rotate([0, 90, 0]) 
                    // the character
                    linear_extrude(thickness)
                        translate([font_size / 2, font_size / 2, 0])
                             rotate(90) 
                                 text(tx, size = font_size, valign = "center", halign = "center");
        }
    }
} 

text_to_cylinder(tx, font_size, thickness, $fn); 

This implementation creates the model below.

2D object to cylinder

You can see that the model edge is jagged. You can improve this by increasing the $fn value. A bigger $fn will have more triangles, however, more intersection operations with the characters, so it takes more time to render. It is one disadvantage of this method.

The algorithm of this module is suitable for all 2D drawing. As you saw in The children module, you can use children to represent the operated module so that we can refactor the above implementation to a generalized one.

tx = "A";
font_size = 20;
thickness = 1;
fn = 24;

module one_over_fn_for_circle(radius, fn) {
    a = 360 / fn;
    x = radius * cos(a / 2);
    y = radius * sin(a / 2);
    polygon(points=[[0, 0], [x, y],[x, -y]]);
}

module square_to_cylinder(length, width, square_thickness, fn) {
    r = length / 6.28318;
    a = 360 / fn;
    y = r * sin(a / 2);
    for(i = [0 : fn - 1]) {
        // line up the triangle
        rotate(a * i) translate([0, -(2 * y * i + y), 0])

         intersection() {
            // line up the triangle
            translate([0, 2 * y * i + y, 0]) 
                linear_extrude(width) 
                    one_over_fn_for_circle(r, fn);
            // make the character stand up
            translate([r - square_thickness, 0, width]) 
                rotate([0, 90, 0]) 
                    children(0);
        }
    }
}

square_to_cylinder(font_size, font_size, thickness, fn)
    linear_extrude(thickness)
        translate([font_size / 2, font_size / 2, 0])
            rotate(90) 
                text(tx, size = font_size, valign = "center", halign = "center");

The square_to_cylinder module is generalized. You can place a 2D drawing after it, and tell it the required arguments. It will automatically create a cylinder with the 2D drawing on it. If you want, the drawing can come from the picture read by the built-in surface module, such as PNG to pen holder.

PNG to pen holder