Dibujando una rosa polar en C++

 En matemáticas, una rosa polar es el nombre que recibe cualquier miembro de una familia de curvas de ecuación r(θ) = cos(kθ) por asemejarse a una flor de pétalos, representadas en un sistema de coordenadas polares.

Las coordenadas polares o sistema de coordenadas polares son un sistema de coordenadas bidimensional en el que cada punto del plano se determina por una distancia y un ángulo. Este sistema es ampliamente utilizado en física y trigonometría.

En nuestro ejemplo utilizaremos el estándar C++ 11 y las herramientas de la Biblioteca Estándar. Utilizaremos como elemento para dibujar, una imagen en formato .bmp sin compresión en escala de grises, lo que simplifica la lectura y escritura al requerir solo un byte  por píxel.


Nota: No se utilizan ventanas ni animaciones para garantizar que el código resultante sea multiplataforma y no requiera elementos extra para ejecutarse.


 

Nuestra imagen .bmp puede considerarse un sistema de coordenadas cartesiano, con un sistema de referencia conformado por dos rectas perpendiculares que se cortan en el origen, cada punto del plano puede “nombrarse” mediante dos números: (x, y), que son las coordenadas del punto, llamadas abscisa y ordenada, respectivamente, que son las distancias ortogonales de dicho punto respecto a los ejes cartesianos.

Para convertir del sistema polar al cartesiano tenemos:

x = r cos θ

y = r sen θ

Primer paso, leemos la imagen del disco y la cargamos en memoria RAM:

	string img = "/direccion_imagen/foto1.bmp";
	streampos size;
	char *memblock;
	int *pIntTamFich;
	int *pIntinicioim;

	ifstream photo(img, ios::in | ios::binary | ios::ate);

	size = photo.tellg();
	memblock = new char[size];
	photo.seekg(0, ios::beg);
	photo.read(memblock, size);
	photo.close();

Para comprobar que el fichero leído es una imagen .bmp válida chequeamos que los bytes 0 y 1 almacenados en el arreglo memblock correspondan con los caracteres ‘B’ y ‘M’:

if (memblock[0] == 'B' && memblock[1] == 'M') {

Posteriormente extraemos información necesaria de la cabecera del fichero, como es la dimensión de la imagen, la altura y el ancho en píxeles y la posición de inicio de la imagen. Para más información acerca de la estructura del formato, consultar este enlace.

char bytes[] = { memblock[2], memblock[3], memblock[4], memblock[5] };
		pIntTamFich = (int*) bytes;
		cout << "Tamaño del fichero: " << *pIntTamFich << endl;

		char bytesti[] = { memblock[34], memblock[35], memblock[36],
				memblock[37] };
		int *pIntti = (int*) bytesti;
		cout << "Tamaño de la imagen: " << *pIntti << endl;

		char bytespixw[] = { memblock[18], memblock[19], memblock[20],
				memblock[21] };
		int *pIntpixw = (int*) bytespixw;
		cout << "Anchura en píxeles: " << *pIntpixw << endl;

		char bytespixh[] = { memblock[22], memblock[23], memblock[24],
				memblock[25] };
		int *pIntpixh = (int*) bytespixh;
		cout << "Altura en píxeles: " << *pIntpixh << endl;

		char bytesinicioim[] = { memblock[10], memblock[11], memblock[12],
				memblock[13] };
		pIntinicioim = (int*) bytesinicioim;
		cout << "Posición de inicio de la imagen: " << *pIntinicioim << endl;
//utilizamos variables convenientes para el trabajo posterior
		int offsetimg = *pIntinicioim;
		int ancho = *pIntpixw;
		int altura = *pIntpixh;
		float O = 0;
		float r = 0;
		int x = 0;
		int y = 0;

Damos color negro a la imagen, con el objetivo de eliminar la información anterior y ver con mayor claridad el dibujo.

for (int i = offsetimg; i < size; ++i) {
			memblock[i] = 0;
		}

Evaluamos la función, teniendo en cuenta que θ es la variable independiente y r es dependiente, por lo que iteramos sobre todos los posibles ángulos, de 0o a 360o. Debemos considerar que modificaremos píxeles individuales de la imagen cada vez y sus coordenadas son discretas, por lo que es posible que ocurran saltos, para minimizar esto, utilizamos un incremento pequeño, buscando como resultado una imagen lo más continua posible. 

for (float i = 0; i < 360; i = i + 0.1) {//utilizamos i + 0.1 para 
//minimizar los saltos en el resultado
			O = i * 3.14 / 180;//llevamos de grados a 
//radianes, aunque también es posible iterar directamente en radianes
			r = 400 * sin(2 * O);//en esta función 400 es 
//el tamaño de la imagen y 2 establece el número de pétalos, estos 
//valores son parámetros de la función y pueden ser variados a 
//conveniencia.
//realizamos el paso a coordenadas cartesianas.
			x = (int) r * cos(O);
			y = (int) r * sin(O);
//realizamos una traslación al centro de la imagen, para apreciarla 
//completamente
			x = x + (ancho / 2);
			y = y + (altura / 2);
//escribimos el resultado en la imagen cargada en memoria, aunque la 
//tratamos como un un sistema (x;y) está cargada en un arreglo 
//unidimensional.
			memblock[y * ancho + offsetimg + x] = 255;

		}

Escribimos los cambios al disco y obtenemos el siguiente resultado:

 

Imagen rosa polar

Este ejemplo es básico, sin tratamiento de errores ni validación de entrada y con parámetros fijos, por lo que solo es válido como demostración.

Código fuente completo del ejemplo:

//============================================================================
// Name        : C_PolarFlowerDraw.cpp
// Author      : Javier Alfonso Valdés
// Version     :
// Copyright   : Libre para uso educativo
// Description : Dibujar flor polar en C++
//============================================================================

#include <iostream>
#include <fstream>
#include <bitset>
#include "math.h"

using namespace std;

int main() {

	string img = "/home/jalfonso/Escritorio/foto1.bmp";
	//getline(std::cin, img);
	streampos size;
	char *memblock;
	int *pIntTamFich;
	int *pIntinicioim;

	ifstream photo(img, ios::in | ios::binary | ios::ate);

	size = photo.tellg();
	memblock = new char[size];
	photo.seekg(0, ios::beg);
	photo.read(memblock, size);
	photo.close();
	if (memblock[0] == 'B' && memblock[1] == 'M') {
		cout << "Foto en memoria" << endl;

		char bytes[] = { memblock[2], memblock[3], memblock[4], memblock[5] };
		pIntTamFich = (int*) bytes;
		cout << "Tamaño del fichero: " << *pIntTamFich << endl;

		char bytesti[] = { memblock[34], memblock[35], memblock[36],
				memblock[37] };
		int *pIntti = (int*) bytesti;
		cout << "Tamaño de la imagen: " << *pIntti << endl;

		char bytespixw[] = { memblock[18], memblock[19], memblock[20],
				memblock[21] };
		int *pIntpixw = (int*) bytespixw;
		cout << "Anchura en píxeles: " << *pIntpixw << endl;

		char bytespixh[] = { memblock[22], memblock[23], memblock[24],
				memblock[25] };
		int *pIntpixh = (int*) bytespixh;
		cout << "Altura en píxeles: " << *pIntpixh << endl;

		char bytesinicioim[] = { memblock[10], memblock[11], memblock[12],
				memblock[13] };
		pIntinicioim = (int*) bytesinicioim;
		cout << "Posición de inicio de la imagen: " << *pIntinicioim << endl;

		int offsetimg = *pIntinicioim;
		int ancho = *pIntpixw;
		int altura = *pIntpixh;
		float O = 0;
		float r = 0;
		int x = 0;
		int y = 0;

		for (int i = offsetimg; i < size; ++i) {
			memblock[i] = 0;
		}

		for (float i = 0; i < 360; i = i + 0.1) {
			O = i * 3.14 / 180;
			r = 400 * sin(2 * O);

			x = (int) r * cos(O);
			y = (int) r * sin(O);

			x = x + (ancho / 2);
			y = y + (altura / 2);

			memblock[y * ancho + offsetimg + x] = 255;

		}

		cout << "Imagen modificada" << endl;
		string newfile = "/home/jalfonso/Escritorio/foto1_salida.bmp";

		ofstream outfile(newfile, ios::binary | ios::out);

		outfile.write(memblock, size);

		outfile.close();

	}

	return 0;
}

Comentarios