Uno de los trabajos típicos que se hacen con la CNC es el retrato de personajes en formato blanco y negro. Por ejemplo, son conocidos los retratos del Che Guevara o Albert Einstein. Estos días estoy trabajando con la imagen de Ovidi Montllor [1].
La idea es sencilla y conocida. Importo la imagen en LibreCAD [2] (el software CAD que utilizo, que es open source), defino la silueta, y defino unas áreas de hatching que seran fresades (rebajadas). Utilizo el programa dxf2gcode [3] para exportar a G-Code, y freso todo el trabajo a una profundidad de 1.5mm. La limitación que tengo es que la fresa que utilizo es de 2mm de diámetro, y esto limita la resolución final que pudo alcanzar.
Estoy programando un pequeño script (cncstroke [4]) para mejorar la resolución de mis trabajos. Imaginemos por ejemplo los cabellos de Albert Einstein. Lo ideal es que las mechas de cabello acaben en punta, haciendo un trazo que pase de grueso a fino. Esto se consigue haciendo que la profundidad de ataque de la broca sea variable, desde 1.5mm de profundidad en el inicio del trazo hasta 0mm al final del trazo. Pero para que el invento funcione, no he de utilizar una broca normal, sino una broca V-Shape (con punta de 40 grados). De esta manera conseguiré el efecto de hacer un trazo que pase de 2mm de ancho en el origen a 0 mm en el extremo. Esta técnica también será muy útil para fresar letras que simulen trazos variables.
La idea es que en els mis proyectos de LibreCAD tendré una capa con la silueta básica (que fresaré con una broca de 2mm), y una capa (que siempre nombraré stroke) que constará de polilíneas que representarán el trazo variable desde 2mm hasta a 0 mm de ancho. Con esta segunda capa podré hacer los detalles como cabellos o caligrafía de letras.
En este primer artículo sólo expongo la estructura de datos que me permitirá gestionar la generación del G-Code a partir del proyecto de LibreCAD. Cuando recorra (programáticamente) el contenido de un proyecto LibreCAD (formato dxf), tendré que buscar la definición de la capa stroke y de los puntos que contiene. Esto es relativamente fácil de hacer. Esta sucesión de puntos se pueden agrupar en polilíneas, entendiendo que una polilínea es un conjunto de líneas que van empalmadas.
Como el número de poliíneas es variable, y el número de líneas que contiene una polilínea también es variable, la mejor manera de programar esta estructura con lenguaje C++ es utilizar vectores.
struct linia {
\tint x0;
\tint y0;
\tint x1;
\tint y1;
\tdouble dist;
};
struct polilinia {
\tstd::vector vlinia;
\tdouble dist;
};
std::vector vpolilinia;
Una línea consta de un punto origen y de un punto final (fácilmente se podrá calcular la longitud de esta línea). Una polilínea consta de un conjunto de líneas, y la longitud de la polilínea será la suma de les longitudes individuales.
A continuación mostramos el ejemplo mínimo de cómo se pueden gestionar diferentes polilíneas, cada una con un número variable de líneas.
// g++ -o polilinia polilinia.cpp
#include
#include
#include
#include
#include
struct linia {
\tint x0;
\tint y0;
\tint x1;
\tint y1;
\tdouble length;
};
struct polilinia {
\tstd::vector vlinia;
\tdouble length;
};
std::vector vpolilinia;
using namespace std;
int main() {
\t
\tdouble lengthlinia,lengthpolilinia;
\tpolilinia polilinia1;\t
\tlinia linia1;
\tlengthpolilinia=0;
\tlinia1.x0=34;
\tlinia1.y0=30;
\tlinia1.x1=120;
\tlinia1.y1=122;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;
\tpolilinia1.vlinia.push_back(linia1);
\tlinia1.x0=320;
\tlinia1.y0=322;
\tlinia1.x1=420;
\tlinia1.y1=422;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;
\tpolilinia1.vlinia.push_back(linia1);
\t
\tpolilinia1.length = lengthpolilinia;
\t
\tvpolilinia.push_back(polilinia1);
\t
\t//cout << vpolilinia.at(0).vlinia.size() << endl;
\t//anem per una altra polilinia
\tpolilinia1.vlinia.clear();
\tlengthpolilinia=0;
\tlinia1.x0=134;
\tlinia1.y0=130;
\tlinia1.x1=220;
\tlinia1.y1=222;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;
\tpolilinia1.vlinia.push_back(linia1);
\tlinia1.x0=220;
\tlinia1.y0=222;
\tlinia1.x1=320;
\tlinia1.y1=322;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;
\tpolilinia1.vlinia.push_back(linia1);
\tlinia1.x0=220;
\tlinia1.y0=222;
\tlinia1.x1=320;
\tlinia1.y1=322;
\tlengthlinia = sqrt(pow(linia1.x1-linia1.x0,2) + pow(linia1.y1-linia1.y0,2));
\tlinia1.length=lengthlinia;
\tlengthpolilinia += lengthlinia;
\tpolilinia1.vlinia.push_back(linia1);
\tpolilinia1.length = lengthpolilinia;
\tvpolilinia.push_back(polilinia1);\t
\t//cout << vpolilinia.at(1).vlinia.size() << endl;
\t//cout << vpolilinia.size() << endl;
\t//recorrem totes les polilínies
\tfor (int i=0; i
I la sortida per pantalla:
$ ./polilinia
polilinia #0. length: 267.358
linia #0. length: 125.936
linia #1. length: 141.421
polilinia #1. length: 408.779
linia #0. length: 125.936
linia #1. length: 141.421
linia #2. length: 141.421
En el siguiente artículo [5] se explica la implementación del proyecto cncstroke para generar G-Code con z-depth variable.
Referencias:
[1]. https://ca.wikipedia.org/wiki/Ovidi_Montllor_i_Mengual
\t
[2]. LibreCAD
\t
[3]. dfx2gcode
\t
[4]. cncstroke en wiki.joanillo.org
\t
[5]. Mejorando la conversión DXF a G-Code, II
Nota aclarativa: En el proyecto cncstroke utilizamos el término polilínea como sucesión de líneas enlazadas. Ahora bien, para generar estas líneas enlazadas, en LibreCAD, utilizamos la herramienta línea (y no la herramienta polilínea) (En una futura versión se estudiará la posibilidad de utilizar la herramienta polilínea, o ambas).