Arxiu de la categoria: Programació

Ordenant idees amb la llibreria SDL

Estic programant amb C++ i SDL el software per a una màquina de dards. Ja tinc quasi bé acabada tota la part de la lògica de l’aplicació, així que he començat a mirar-me la part gràfica, que faré amb la llibreria SDL com he utilitzat en altres projectes.

Arrel de migrar el projecte a SDL (tot i conservant la compatitibilitat amb la part de consola) he ensopegat amb un error de programació d’aquells que et fan perdre temps. Al final la cosa no ha sigut greu, diguem que no era un error de concepte sinó més aviat un despiste, degut a què el codi per moments s’està complicant i convé mantenir el codi ben ordenat i lògic.

El cas és que, arrel d’aquest contratemps, he volgut fer una petita recopilació del codi mínim SDL per pintar un tros de text per pantalla, utilitzant diferents tècniques: a) tota la part de SDL barrejada en el fitxer main.cpp; b) POO, utilitzant un fitxer per cada classe; c) POO, però ficant totes les classes en un sol fitxer.

Els tres exemples es poden descarregar:

cnchollow script. Generador de G-Code per a fresar diferents tipus de cavitats

Hem estat programant un script amb C++ per fer cavitats. Cavitats de diferent tipus: rectangulars, rectangulars amb cantos arrodonits, circulars i el·líptiques. A més, la profunditat de la cavitat pot ser constant (cavitat cilíndrica) o bé una cavitat esfèrica. Per tant, hi ha 8 possibilitats de cavitats diferents, com es veu a la imatge, amb diversos paràmetres que pots controlar. El script genera el G-Code directe per a ser fresat.

Si vols utilitzar el script, primer hauràs de mirar les il·lustracions que hi ha a la documentació, per tal d’entendre els diferents paràmetres. Bàsicament haurem d’introduir l’origen de coordenades de la cavitat (allà on està centrada); els semieixos en el cas dels rectangles-el·lipse, o el radi en el cas del cercle; la profunditat final en el cas de cavitats cilíndriques; el radi esfèric en el cas de cavitats esfèriques, que donarà lloc a més o menys profunditat esfèrica. En funció de la broca que tinguem i de la resolució que vulguem, també hem de definir el pas linial i el pas de profunditat (paràmetres -m i -n).

Per a les cavitats cilíndriques n’hi ha prou amb utilitzar broques normals de fresar (les de 2mm de diàmetre són les més petites que he aconseguit). Però per a les cavitats esfèriques és interessant utilitzar una broca tipus round nose bit, i així aconseguim un millor acabat, com es pot apreciar a la foto.

El primer projecte per utilitzar les meves cavitats és fresar el joc del mancala (o awalé o altres noms), on vull fresar vàries cavitats diferents. Ja informarem quan estigui acabat.

És un projecte escrit amb C++, no requereix cap llibreria especial, per tant la seva compilació és directa. Per la versió 1.0.6, la darrera versió:

$ g++ -o cnchollow cnchollow-1.0.6.cpp
Ex:
$ ./cnchollow -f rectangle -t cylindrical -x 100 -y 100 -r 30.23 -s 15.21 -z -10.3 -m 3.0 -n -1.0 -o ./cnchollow.ngc

Referències:

script cncstroke: millorant la conversió DXF a G-Code, II

Creo un projecte amb LibreCAD, creo la capa ”stroke”, i aquí fiquem les polilínies, tal com s’ha explicat en l’anterior article [1], i tal com es mostra en la imatge. Amb llenguatge C++, el primer que fem és llegir el fitxer dxf i detectar la capa stroke, amb tots els seus punts. En el LibreCAD totes les línies són iguals, tenen un origen i un final. Però a mi m’interessa detectar les polilínies, que són les línies enllaçades de manera que el punt final d’una línia és el mateix que el punt d’origen de la línia següent. Recordem l’estructura de dades que es fa servr:

struct linia {
\tint x0;
\tint y0;
\tint x1;
\tint y1;
\tdouble length;
};

struct polilinia {
\tstd::vector vlinia;
\tdouble length;
};

std::vector vpolilinia;
Recorrem totes les línies, i anem afegint elements al vector polilínia, i dins de cada polilínia anem afegint elements al vector línia. Per cada línia i per cada polilínia, tenim calculada la seva longitud. Un cop tenim omplert el vector de polilínies amb les seves línies, ja podem recórrer totes les polilínies i generar la sortida G-Code, de manera que l’inici d’una polilínia està a profunditat -2mm, i el final de la polilínia està a profunditat 0mm, tal com s’aprecia en aquest tros del G-Code resultant, i en la imatge:

(* POLILINE #2 *)
G0 X 30 Y 30
G0 Z 3.000
F150
G1 Z -2.0
F400
G1 X 50 Y 40 Z -1.11
G1 X 60 Y 30 Z -0.55
G1 X 70 Y 40 Z 0
F150
G0 Z 5.000

Com s’aprecia en el G-Code, posicionem la broca al punt (30,30), foradem fins a -2.0mm, i anem passant per tres punts successius tot aixecant de forma proporcional la broca fins arribar al punt (70,40), en què la broca deixa d’atacar la superfície. Així s’aconsegueix un traç variable, tal com s’aprecia en la imatge. Amb aquesta tècnica podré perfilar detalls en els treballs CNC, com ara els cabells d’un retrat, o el traç en una caligrafia.

Com s’aprecia en la imatge (extrecta del projecte de fresar el retrat d’Albert Einstein en blanc i negre, en una altra entrada mostrarem el resultat final), es veu el resultat d’aplicar el script cncstroke per a les polilínies agrupades en la capa stroke del projecte LibreCAD. Veiem dos traços que acaben en punxa (a la part esquerra de la imatge, sobre fusta), i un altre traç, on no s’ha aplicat el script, que no acaba en punxa. A la part dreta de la imatge veiem un tros del projecte LibreCAD on s’han definit aquestes línies (dues que pertanyen a la capa cncstroke, i una altra que es fresa normal). Així doncs, amb un ús correcte del projecte LibreCAD, podem augmentar la resolució del fresat CNC, aconseguint un efecte més real i detallat.

Pots descarregar i estudiar el codi del script cncstroke des del següent enllaç [2] .
Referències:

Nota aclarativa: En el projecte cncstroke utilitzem el terme polilínia com a successió de línies enllaçades. Ara bé, per generar aquestes línies enllaçades, a LibreCAD, utilitzem l’eina línia (i no pas l’eina polilínia) (En una futura versió s’estudiarà la possibilitat d’utilitzar l’eina polilínia, o ambdues).

script cncstroke: millorant la conversió DXF a G-Code, I

Un dels treballs típics que es fan amb la CNC és el retrat de personatges en format blanc i negre. Per exemple, són coneguts els retrats del Che Guevara o l’Albert Einstein. Aquests dies estic treballant en la imatge de l’Ovidi Montllor [1].

La idea és senzilla i coneguda. Importo la imatge en LibreCAD [2] (el software CAD que utilitzo, que es open source), defineixo la silueta, i defineixo unes àrees de hatching que seran fresades (rebaixades). Faig servir el programa dxf2gcode [3] per exportar a G-Code, i freso tot el treball a una profunditat de 1.5mm. La limitació que tinc és que la fresa que faig servir és de 2mm de diàmetre, i això limita la resolució final que puc tenir.

Estic programant un petit script (cncstroke [4]) per tal de millorar la resolució dels meus treballs. Imaginem per exemple els cabells de l’Albert Einstein. Allò ideal és que les metxes de cabell acabin en punta, fent un traç que passi de gruixut a fi. Això s’aconsegueix fent que la profunditat d’atac de la broca sigui variable, des de 1.5mm de profunditat en l’inici del traç fins a 0mm al final del traç. Però per tal de què l’invent funcioni, no he de fer servir una broca normal, sinó una broca V-Shape (amb punta de 40 graus). D’aquesta manera aconseguiré l’efecte de fer un traç que passi de 2mm d’ample en l’origen a 0mm en l’extrem. Aquesta tècnica també serà molt útil per fresar lletres que simulin traços variables.

La idea és que en els meus projectes de LibreCAD tindré una capa amb la silueta bàsica (que fresaré amb una broca de 2mm), i una capa (que sempre anomenaré stroke) que constarà de polilínies que representaran el traç variable des de 2mm fins a 0 mm d’ample. Amb aquesta segona capa podré fer els detalls com ara cabells o caligrafia de lletres.

En aquest primer article només exposo l’estructura de dades que em permetrà gestionar la generació del G-Code a partir del projecte de LibreCAD. Quan recorri (programàticament) el contingut d’un projecte LibreCAD (format dxf), hauré de cercar la definició de la capa stroke i dels punts que conté. Això és relativament fàcil de fer. Aquesta successió de punts es poden agrupar en polilínies, entenent que una polilínia és un conjunt de línies que van empalmades.

Com que el número de poliínies és variable, i el número de línies que conté una polilínia també és variable, la millor manera de programar aquesta estructura amb llenguatge C++ és utilitzar vectors.

struct linia {
\tint x0;
\tint y0;
\tint x1;
\tint y1;
\tdouble dist;
};

struct polilinia {
\tstd::vector vlinia;
\tdouble dist;
};

std::vector vpolilinia;

Una línia consta d’un punt origen i un punt final (fàcilment es podrà calcular la longitud d’aquesta línia). Una polilínia consta d’un conjunt de línies, i la longitud de la polilínia serà la suma de les longituds individuals.

A continuació mostrem l’exemple mínim de com es poden gestionar diferents polilínies, cadascuna de les quals amb un número variable de línies.

// 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 següent article [5] s'explica la implementació del projecte cncstroke per tal de generar G-Code amb z-depth variable.

Referències:

Nota aclarativa: En el projecte cncstroke utilitzem el terme polilínia com a successió de línies enllaçades. Ara bé, per generar aquestes línies enllaçades, a LibreCAD, utilitzem l'eina línia (i no pas l'eina polilínia) (En una futura versió s'estudiarà la possibilitat d'utilitzar l'eina polilínia, o ambdues).