Posts Tagged haxe

Posted on Programming

Logic parsing & cat breeds

Logipar – an open source logic parsing library

Logipar, pronounced Lojipur (and soon you’ll see why), has a history.

It always starts out like this: I’m working on a project and it has some form of filter box – where you can type, and the results shown will be limited to those relevant to what you’ve typed. So you can type “cat” and it’ll show you all cats.

That’s easy enough. But then I want to be able to match multiple things simultaneously – to see all the cats as well as all the dogs.

Still easy, I normally split on commas – “cat, dog” would show all cats and all dogs.

And here is where things get troublesome. Sure, I can list all the cats and all the dogs, but what if i want to list only results that are both cats and dogs.

Okay this example is starting to break down. Where I’m going with this is logic parsing. Splitting on commas, I have to decide whether to join them with a logical OR (show all the cats as well as all the dogs) or with a logical AND (show only results that are BOTH a cat and a dog).

And sure, I can pick one or the other, but what if I want the option to use either?

So I made Logipar. An open source library for parsing logic strings. With Logipar I can type “cat OR dog“, or I can type “cat AND dog“. And it’ll parse that logic for me.

Logipar works in multiple languages, which is convenient because I work in multiple languages. Now to go back and add it to all the various tools I’ve ever written. JK, I probably won’t do that.

But all future projects.. That’s a different story!

You can use it too, if you want, I mean. No pressure.

 


Cat breed data

Having built Logipar, I wanted to create a demo for it. To identify any obvious bugs, sure, but more to better convey what Logipar does and is capable of.

For that, I needed some data. And the first thing that came to mind was cats.

When I went looking for a cat breed dataset, I found nothing. Sure, there were some websites with breed data – but they weren’t great. Fields would randomly be missing, or displayed in completely different ways.

I spent an entire day compiling the data from five different sites (wikipedia, cattime, petfinder, purina, and royalcanin), conjoining it, and then cleaning it up.

Now I have a cat breed dataset.

So if you want to see what Logipar can do and learn about cats, boy, do I have a tool for you: https://altef.github.io/logipar – conveniently iframed below:

It shows more or fewer fields based on the width of its container.



Posted on Programming

Gently flickering flame, part 3

Now that we can talk to the lights, and choose colours, it’s time to make the lights flicker!

Hue doesn’t like you to send more than 10 commands per second. Since I have two lights I’m going to update them once every 200 milliseconds (5 times per second).

This is easy to do, using a normal game-type timing loop. In the constructor of your Main.hx, add addEventListener(Event.ENTER_FRAME, onFrame);.

Then create the onFrame function. Here’s mine:

private function onFrame(e:Event) {
	var n = Lib.getTimer();
	if ( n - _last >= 200 ) { // Don't send an update unless 200ms have elapsed
		if ( change ) { // I watch for colour and brightness changes, and set a this variable when they occur
			var data = {
				"bri": Math.round(Math.min( 254, cp.l*255 )), // brightness is between 0 and 254
				"hue": Math.round(Math.min(65535, cp.h*65535)), // hue is between 0 and 65535
				"sat":Math.round(Math.min( 254, cp.s*255 )) // saturation is between 0 and 254
			}
			var ds = Json.stringify(data);
			for (n in Reflect.fields(lights)) { // Loop through all the lights. We want the light keys, hence the reflection
				hue.update(n, 'state', ds); // Update the light
			}
			change = false;
		} else if (cp.flicker) { // This is the flickering part! cp is my colour picker/user interface's name
			var hsl = flicker.flick( cp.h, cp.s, cp.l );  // The magic flicker function!
			var data = {
				"bri": hsl[2],
				"hue": hsl[0],
				"sat": hsl[1],
				"transitiontime": 2
			}
			var ds = Json.stringify(data);
			for (n in Reflect.fields(lights)) {
				hue.update(n, 'state', ds); // Update the light
			}
		}
		_last = n;
	}
}

For the flicker itself, I kept it pretty simple. I might eventually try a normally distributed pseudo-random variance on the hue.

class Flicker {
	public function new() {}
	public function flick(h:Float, s:Float, l:Float) {
		var b = clamp(0, vary(l*254, 0.2), 254); // Vary brightness by 20%
		var h = clamp(0, vary(h*65535, 0.05), 65535); // Vary hue by 5%
		var s = clamp(0, h*254, 254);
		return [h, s, b];
	}
	function vary(orig:Float, variance:Float):Int {
		var max = orig * variance;
		var half = max/2;
		return Math.round(orig + Math.random()*max - half);
	}
	function clamp( min, val, max ) {
		return Math.round( Math.max( min, Math.min( max, val ) ) );
	}
}
Posted on Programming

Gently flickering flame, part 2 (colours)

Now the we can talk to the lights, It’s time to deal with colours. We can’t just send RGB to the API, so I made a class that displays a rectangle of colours, and can convert from HSL to RGB for displaying each one. The HslToRgb function I stole from stackoverflow.

package;

import openfl.display.Sprite;
import openfl.events.MouseEvent;
import openfl.events.Event;


class Colours extends Sprite {
	private var _width:Int = 60; // Number of hues
	private var _height:Int = 30; // Number of saturations
	private var size:Int = 10;
	
	public var h:Float;
	public var s:Float;
	
	
	public function new() {
		super();
		var l = .9;
		for(x in 0..._width) {
			for(y in 0..._height) {
				var h = _height/2;
				var lo = (1-(y/_height/2)); // This is so it fades into white rather than grey
			
				var rgb = hslToRgb( x*1.00/_width, y*1.00/_height, lo);
				var c = rgb[0];
				c = (c<<8) + rgb[1];
				c = (c<<8) + rgb[2];
				graphics.beginFill(c);
				graphics.drawRect(x*size,y*size,size, size);
				graphics.endFill();
			}
		}
		addEventListener(MouseEvent.CLICK, onclick);
		addEventListener(MouseEvent.MOUSE_MOVE, onmove); // So it picks up finger drags
	}
	
	private function onmove(e:MouseEvent) {	
		if ( !e.buttonDown ) return;
		onclick(e);
	}
	private function onclick(e:MouseEvent) {
		h = Math.min(1, e.localX/size/_width);
		s = Math.min(1, e.localY/size/_height);
		var l = 0.5; // This is alpha
		var rgb = hslToRgb(h,s,l); 
		dispatchEvent( new Event(Event.CHANGE) );
	}
	
	/**
	* Converts an HSL color value to RGB. Conversion formula
	* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
	* Assumes h, s, and l are contained in the set [0, 1] and
	* returns r, g, and b in the set [0, 255].
	*
	* @param   Number  h       The hue
	* @param   Number  s       The saturation
	* @param   Number  l       The lightness
	* @return  Array           The RGB representation
	*/
	public function hslToRgb(h:Float, s:Float, l:Float):Array{
		var r:Float, g:Float, b:Float;
	
		if(s == 0){
			r = g = b = l; // achromatic
		}else{
			var hue2rgb = function hue2rgb(p:Float, q:Float, t:Float):Float{
				if(t < 0) t += 1;
				if(t > 1) t -= 1;
				if(t < 1/6) return (p + (q - p) * 6 * t);
				if(t < 1/2) return (q);
				if(t < 2/3) return (p + (q - p) * (2/3 - t) * 6);
				return (p);
			}
	
			var q:Float = l < 0.5 ? l * (1 + s) : l + s - l * s;
			var p:Float = 2 * l - q;
			r = hue2rgb(p, q, h + 1/3);
			g = hue2rgb(p, q, h);
			b = hue2rgb(p, q, h - 1/3);
		}
	
		return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
	}
}