Categoría para traducir entidades html en NSString

septiembre 20, 2010

Recientemente desarrollando para el iOS, me encontré con la necesidad de eliminar las entidades html de una cadena de texto.

Para el que no lo sepa, las entidades dentro de HTML, o en cualquier lenguage de marcas, son lo mas parecido a una secuencia de escape, es decir, una cadena que representa a un caracter que originalmente no esta permitido. Por ejemplo, y como parece obvio, si queremos poner el caracter ‘<‘ en HTML, no podemos ponerlo directamente, pues forma parte del propio lenguage. Por ello, si queremos escribir dicho carácter tendremos que poner <.

La principal característica de las entidades de un lenguage de marcas, es que comienzan con ‘&’ y acaban con ‘;’.

Dentro de la API de manejo de cadenas de UIKit, existe un método para codificar URLs, que aunque pueda parecer similar, no lo es. Pues las URLs no tienen nada que ver con el lenguage HTML, solo nos indican la forma de acceder a los documentos que estan hechos con HTML 😉

Dichos métodos son, stringByAddingPercentEscapesUsingEncoding: y stringByReplacingPercentEscapesUsingEncoding:, y sustituyen los caracteres especiales de las URLs por sus equivalentes “escapados” usando porcentajes, como por ejemplo el símbolo del $ la convertirá en %24.

Pero sin embargo no hay ninguna función para transformar las entidades en sus caracteres equivalentes.

La primera solución que se nos puede ocurrir es usar el mensaje stringByReplacingOccurrencesOfString:withString: por cada entidad. Y aunque funciona, es bastante penalizador desde el punto de vista del rendimiento, ya que si tenemos el siguiente código.

NSString* cad = [NSString stringWithString:@"&lt;&apos;&amp;&apos;&gt;"];
	NSLog(@"%@", [[[[cad stringByReplacingOccurrencesOfString:@"&lt;" withString:@"<"]
				  stringByReplacingOccurrencesOfString:@"&gt;" withString:@">"]
				  stringByReplacingOccurrencesOfString:@"&amp;" withString:@"&"]
				  stringByReplacingOccurrencesOfString:@"&apos;" withString:@"'"]);

Recorrerá la cadena entera tantas veces como entidades queramos sustituir, lo que para cadenas largas puede ser un gasto bastante grande e innecesario.

Después de buscar por internet, no encontré nada que me convenciera, asi que tome el camino del medio, y hice una categoria de NSString, que recorre la cadena y sustituye las ocurrencias, de las entidades por sus caracteres.

Al ser una categoria, no hace falta mas que incluir el fichero .h y llamarlo sobre cualquier objeto NSString.

@interface NSString (RemoveHtmlEntities)

- (NSString*) stringByRemovingHtmlEntities;

@end

@implementation NSString (RemoveHtmlEntities)

- (NSString*) stringByRemovingHtmlEntities {
	NSMutableString* newStr = [[NSMutableString alloc] init];
	NSUInteger i = 0;
	unichar c = 0;
	NSUInteger strLen = [self length];
	while (i < strLen) {
		c = [self characterAtIndex:i];
		if ( c == '&' ) {
			if ( [self rangeOfString:@"amp;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (5>(strLen-i))?strLen-i:5)].location != NSNotFound ) {
				[newStr appendFormat:@"&"];
				i += 5;
			} else if ([self rangeOfString:@"lt;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (4>(strLen-i))?strLen-i:4)].location != NSNotFound) {
				[newStr appendFormat:@"<"];
				i += 4;
			} else if ([self rangeOfString:@"gt;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (4>(strLen-i))?strLen-i:4)].location != NSNotFound) {
				[newStr appendFormat:@">"];
				i += 4;
			} else if ([self rangeOfString:@"quot;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (6>(strLen-i))?strLen-i:6)].location != NSNotFound) {
				[newStr appendFormat:@"\""];
				i += 6;
			} else if ([self rangeOfString:@"apos;" options:NSCaseInsensitiveSearch range:NSMakeRange(i, (6>(strLen-i))?strLen-i:6)].location != NSNotFound) {
				[newStr appendFormat:@"'"];
				i += 6;
			} else {
				[newStr appendFormat:@"&"];
				i++;
			}
		} else {
			[newStr appendFormat:@"%c", c];
			i++;
		}
	}
	return [newStr autorelease];
}

@end

Como veis, solo transforma las entidades que necesitaba en el momento de desarrollo, pero es trivial añadir mas clausulas “else if” al código 😉

Un posible uso del código sería

	NSString* cad = [NSString stringWithString:@"&lt;&apos;&amp;&apos;&gt;"];
        NSLog(@"%@", [cad stringByRemovingHtmlEntities]); 
       // Imprime <'&'>
Anuncios

Juego de la Vida en un canvas de HTML5

septiembre 13, 2010

Poco a poco HTML5 va tomando forma, lo que hace un tiempo era poco mas que un proyecto de estandar, hoy en día ya es una realidad en la mayoría de los navegadores.

Si usamos Safari, Firefox o Chrome ya somos capaces de disfrutar de la mayoría de las ventajas que el nuevo estandar nos propone.

Una de las grandes mejoras es la habilidad para ponder “pintar” en una zona especialmente diseñada para ello. Dicha zona se conoce como “canvas” y vamos a poder utilizarla como si de cualquier otro contexto gráfico se tratara.

Seguramente pensareis que esto no es tan gran novedad, esto ya se podia hacer antes con cosas como Flash o Applets de Java, pero el canvas de HTML5 va un paso mas adelante.

Sin duda la principal novedad de dicho canvas no es tanto el que nos permita dibujar en el navegador sino que es que es propio navegador quien decide como tratar esa zona. A diferencia de la aproximación de flash o java donde era necesario un plugin en comunicación con el navegador.

Este cambio supone un salto drástico en el rendimiento de este tipo de aplicaciones. Seguro que si habéis usado flash o applets java os habréis dado cuenta lo exigente que era para las cosas que mostraban por pantalla. Sin embargo, con el canvas y una buena implementación por parte del navegador, no solo este rendimiento será mejor, sino que además podremos usar cosas como la acceleración nativa 3D.

Además, la ausencia de la necesidad de plugins facilita enormemente la labor de porting a otras plataformas, si el navegador soporta HTML5, no se necesitará nada mas.

Y para terminar con las ventajas, estamos hablando de un estándar, libre de patentes, no controlado por ninguna empresa y con los requerimientos totalmente abiertos. Cualquiera puede implementar un navegador HTML5 sin tener que pagar nada a nadie.

Se me nota entusiasmado, ¿no? :). Creo que es para estarlo, pues la web es uno de los medios de comunicacion mas importante hoy en día, y el hecho de estandarizar algo tan común en la web como son estas aplicaciones es para estar de enhorabuena.

Pues bien, investigando un poco en como se usa el canvas de HTML5, me vino a la mente lo fácil que sería hacer un simulador del famoso “juego” “Juego de la Vida” de Conway.

El juego de la vida, es como se define en la wikipedia, un juego de 0 jugadores. ¿Un Juego sin jugadores? :), básicamente diseñamos una situación inicial y lo único que hacemos es ver como evoluciona la población de células que forman el juego.

El juego está formado por un conjunto de células, representadas por un punto, si la célula esta viva, se pintará de color verde, y si la célula esta muerta, no se pintará. Según ciertas condiciones dependientes del numero de vecinos, cada célula vivirá, morirá o nacerá en la siguiente generación. La implementación es muy sencilla y al igual que con la entrada anterior sobre Mandelbrot, los resultados son bastante interesantes y sin duda algunos patrones de evolución son bastante vistosos.

Aconsejo echar un vistazo a la entrada de la wikipedia para saber un poco más en que consiste el juego.

Hablando de la implementación, pintar en el canvas de HTML5 es bastante sencillo, básicamente, se reduce a 3 lineas

// ...
var context = canvas.getContext("2d");
context.fillStyle = "rgb(100,0,100)";
context.fillRect(10,10, 100, 100);
// ...

Con esas 3 líneas dibujamos un cuadrado en la posicion 10, 10 del canvas. De hecho una vez que obtenemos el contexto, podemos hacer las típicas operaciones, como dibujar cuadrados, arcos, rutas o imagenes.

Para mas detalles, os aconsejo que visiteis el sitio de desarrolladores de mozilla, donde tienen un fantastico tutorial de lo que se puede hacer con el canvas: https://developer.mozilla.org/en/Canvas_tutorial

Finalmente, os dejo con el código fuente de una implementación sencilla del juego de la vida y que como no puedo incrustar en este post de wordpress, podeis ver aquí

<html>
<head>
	<script type="text/javascript">
		var cellSize=10;
		var hcells = 70;
		var vcells = 40;
		var board;
		var canvas;
		var context; 
		var timerId = undefined;
		
		function drawShapeInBoard()
		{
			// Draw Some shapes in the board
			
			board[10][10] = board[12][10] = board[12][9] = board[14][8] = board[14][7] = board[14][6] = board[16][7] = board[16][6] = board[16][5] = board[16][6] = 1;
			
			board[25][30] = board[26][30] = board[27][30]  = board[28][30] = board[29][30] = board[29][30] = board[30][30] = board[31][30] = board[33][30] = board[34][30] = board[35][30] = board[29][30] = 1;
		}
		
		function initGame() {
			canvas = document.getElementById("gameoflife");
			context = canvas.getContext("2d");		
			canvas.addEventListener("click", onCanvasClick, false);	
			initBoard();
			drawShapeInBoard();
			drawBoard();
		}
		
		function onMouseMove(evt) {
			context.fillStyle = "rgb(200,200,200)"			
			context.fillRect(evt.clientX - canvas.offsetLeft, evt.clientY - canvas.offsetTop, cellSize, cellSize);
		}
		
		function onCanvasClick(evt) {
			var x = evt.clientX - canvas.offsetLeft;
			var y = evt.clientY - canvas.offsetTop;
			var boardX = parseInt(x / cellSize);
			var boardY = parseInt(y / cellSize);
			if ( isAlive(boardX, boardY) ) board[boardX][boardY] = 0
			else board[boardX][boardY] = 1;
			
			drawBoard();
		}
		
		function initBoard() {
			board = [];
			for ( i = 0; i < hcells; i++) {
				board[i] = [];
				for ( j = 0; j < vcells; j++ ) {
					board[i][j] = 0;
				}
			}
		}
		
		function drawBoard() {
			for ( i = 0; i < hcells; i++) {
				for ( j = 0; j < vcells; j++ ) {
					if ( board[i][j] == 0){
						context.fillStyle = "rgb(180,180,180)";
					} else {
						context.fillStyle = "rgb(0,170,0)";
					}					
					context.fillRect(i*cellSize, j*cellSize, cellSize, cellSize);					
				}
			}
		}
		
		function isAlive(x,y) {
			if ( board[x] ) 
				if ( board[x][y] == 1) return true;
			return false;
		}
		
		function numberOfNeighbours(x, y) {
			var count = 0;
			if ( isAlive(x-1, y   ) ) count++;
			if ( isAlive(x-1, y-1 ) ) count++;
			if ( isAlive(x-1, y+1 ) ) count++;
			if ( isAlive(x  , y-1 ) ) count++;
			if ( isAlive(x  , y+1 ) ) count++;
			if ( isAlive(x+1, y-1 ) ) count++;
			if ( isAlive(x+1, y+1 ) ) count++;
			if ( isAlive(x+1, y ) ) count++;
			
			return count;
		}
		
		function nextStep() {
			var newBoard = [];
			for ( i = 0; i < hcells; i++) {
				newBoard[i] = [];
				for ( j = 0; j < vcells; j++ ) {
					var neighbours = numberOfNeighbours(i, j);
					if ( isAlive(i,j) && neighbours < 2) newBoard[i][j] = 0;
					else if ( isAlive(i,j) && neighbours > 3 ) newBoard[i][j] = 0;
					else if ( isAlive(i,j) && (neighbours == 2 || neighbours == 3) ) newBoard[i][j] = 1;
					else if ( !isAlive(i,j) && (neighbours == 3) ) newBoard[i][j] = 1;
					else newBoard[i][j] = 0;
				}
			}
			board = newBoard;
		}
		
		function computeNS() {
			nextStep();
			drawBoard();
		}
		
		function startNS() {
			if ( ! timerId) {
				timerId = setInterval("computeNS()", 150);
				var status = document.getElementById("status");
				status.innerHTML = "<b>Running</b>";
			}
		}
		
		function stopNS() {
			clearInterval(timerId);
			timerId = undefined;
			var status = document.getElementById("status");
			status.innerHTML = "<b>Stopped</b>";
		}
		
		function clearBoard() {
			initBoard();
			drawBoard();
		}
		
	</script>
</head>
<body onload="initGame()">
	<p align="center">
		The Game of Life
	</p>
	<p align="center">
		<canvas id="gameoflife" width="700" height="400" border="1"></canvas>
		<br/>
		<button onclick="computeNS()">Next Step</button>
		<button onclick="startNS()">Start</button>
		<button onclick="stopNS()">Stop</button>
		<button onclick="clearBoard()">Clear</button>
		<br/>
		<span id="status"><b>Stopped</b></span>
	</p>
</body>	
</html>

Dibujando un Fractal Mandelbrot con Ruby

septiembre 6, 2010

Hace mucho tiempo, cuando aún me pegaba con pascal en primero de carrera empecé a descubrir los fractales. Aún recuerdo lo bien que lo pasaba con el fractint, “navegando por las profundidades de mandelbrot”. Investigando un poco mas, descubrí que la formula que se utilizaba para generar dicho fractal era bastante sencilla, aún así, un software como el fractint parecía demasiado complejo.

Sin embargo, la curiosidad me picaba, ¿Qué pasaría si hiciera un programa que siguiera la formula que sirve para generar el fractal de mandelbrot?. Al fin y al cabo, la formula parecía sencilla de programar incluso para un imberbe programador 🙂

Sin más espera me puse manos a la obra y con el amado Pascal, hice un programa que supuestamente dibujaría el fractal de mandelbrot. ¿Y qué pasó?, pues ante mi asombro, al ejecutar el programa, allí estaba el famoso gráfico con forma de guitarra. ( Notese la licencia literaria, como buen programador, nada funciona a la primera 😀 )

Hace poco, trasteando con el nuevo “juguete”, ruby, me volvio a picar la curiosidad de dibujar de nuevo el fractal. En esta ocasión, gracias a las utilidades que nos da un lenguaje como Ruby y una gran libreria como SDL, en pocos minutos tenia el famoso gráfico en pantalla.

Centrandonos en el lado técnico del fractal, dibujar el conjunto de mandelbrot es más o menos sencillo.

Para cada numero imaginario se desarrolla la serie z(n+1) = z(n)^2 + c. Siendo la z(0) = 0,0 y c el punto sobre el que calculamos el desarrollo de la serie. Para algunos valores de c, la serie converge, y para otros rapidamente escapa a un valor umbral que habitualmente se establece en 2+2i. Si la serie converge, el punto no pertenece a la serie de mandelbrot, y por lo tanto se pinta de negro. Para los puntos que escapan al valor umbral, según los pasos que han necesitado para escapar se les pinta de un color. Pero eso si, a todos los que escapan con el mismo numero de pasos, se les da el mismo color.

Así por ejemplo, para el punto 1+i, si desarrollamos la serie tendremos:

z(1) = (0+0i)^2 + 1+i => 1+i
z(2) = (1+i)^2 + 1+i => 1+3i
z(3) = (1+3i)^2 + 1+i => -7+7i

z(3) escapa de los límites, por lo que el punto 1+i, pertenece al conjunto de mandelbrot y se pintará con el color elegido para los números que escapan en 3 pasos.

Si hacemos esto mismo con cada punto dentro del rango 2, -2 tanto para real como para imaginario, obtendremos el gráfico que encabeza el post.

Aunque han pasado unos cuantos años, no deja de sorprenderme que un programa tan sencillo tenga como salida un gráfico tan interesante y, por que no, bonito.

Realmente el fractal mandrelbrot es apasionante, una de sus propiedades es debido a que es un fractal, se autoreplica, por lo que podemos encontrar la misma forma si vamos haciendo zoom dentro del gráfico. No voy a entrar en mas detalles, pero os dejo con el enlace de la wikipedia que describe muchos detalles de este fascinante mundo.

Para finalizar el post, os dejo con el código ruby que dibuja la imagen. Lo único que necesitais para ejecutarlo es ruby y rubysdl.

require 'sdl'

WIDTH = 600
HEIGHT = 400
REALRANGE = 2
IMRANGE = 2
MAXSTEPS = 20

def mandelbrot_steps(x, y)
	steps = 0
	newx = newy = 0
	while steps < MAXSTEPS and ( ((newx*newx) + (newy*newy)) < ((REALRANGE*REALRANGE) + (IMRANGE*IMRANGE)) ) do
		xtemp = (newx * newx) - (newy * newy) + x
		newy = 2 * newx * newy + y
		newx = xtemp
		steps+=1
	end

	return steps
end

SDL.init SDL::INIT_VIDEO
screen = SDL.set_video_mode WIDTH, HEIGHT, 24, SDL::HWSURFACE

pallete = Array.new
pallete[0] = screen.format.mapRGB(0,0,0)
pallete[1] = screen.format.mapRGB(40,36,196)
pallete[2] = screen.format.mapRGB(30,144,255)
pallete[3] = screen.format.mapRGB(0,0,128)
pallete[4] = screen.format.mapRGB(0,255,0)
pallete[5] = screen.format.mapRGB(0,255,255)
pallete[6] = screen.format.mapRGB(0,0,255)
pallete[7] = screen.format.mapRGB(255,255,255)
pallete[8] = screen.format.mapRGB(255,100,255)
pallete[9] = screen.format.mapRGB(0,255,255)
pallete[10] = screen.format.mapRGB(100,255,100)

(-WIDTH/2).upto WIDTH/2 do |x|
	(-HEIGHT/2).upto HEIGHT/2 do |y|
		scaledX = (x * 2)/(WIDTH/2).to_f - 0.7
		scaledY = (y * 2)/(HEIGHT/2).to_f
		color = pallete[0]
		steps = mandelbrot_steps scaledX, scaledY
		color =  pallete[steps % 10] if steps < MAXSTEPS
		screen.put_pixel x+(WIDTH/2), y+(HEIGHT/2), color
	end
end

running = true
while running do
	while event = SDL::Event2.poll do
		case event
		when SDL::Event2::Quit
			running = false
		end
	end

	screen.flip
end

SDL.quit