Rotate labels on tiles generated by Maperitive - gis

I'm using custom generated tiles on web map (displayed using openlayers).
Tiles are generated by maperetive and it's great. However my map is rotated -3/4Pi (openlayers has this feature) and many labels are rendered upside down.
I belive maperitive has no feature to render labels relative to arbitrary angle. May be there are other options to fix this?

Maybe you can generate bitmap using export-bitmap command in maperetive. Then you rotate the whole. Hope this help!

I was able to solve the problem altering dll's of Maperitive (v2.4.1) using dnSpy.
Placement of labels is done by Karta.MapLabeling.Igor2LineFeatureLabelPlacementAlgorithm.EvaluatePosition() method (from Karta.dll). Below is the altered method. I added + 225f in three places. 225(deg) is equvalent of -3/4*Pi(radians). The added value should be positive.
public static LabelPositionEvaluation EvaluatePosition(IPolylineAnalysis polylineAnalysis, float position, float? lengthAround)
{
if (lengthAround != null)
{
float? num = lengthAround;
if (num.GetValueOrDefault() <= 0f && num != null)
{
throw new ArgumentOutOfRangeException("lengthAround");
}
}
LabelPositionEvaluation labelPositionEvaluation = new LabelPositionEvaluation(position);
float polylineLength = polylineAnalysis.PolylineLength;
float num2 = (lengthAround != null) ? (position * polylineLength - lengthAround.Value) : 0f;
float num3 = (lengthAround != null) ? (position * polylineLength + lengthAround.Value) : (polylineLength * 0.9999f);
labelPositionEvaluation.Fits = (num2 >= 0f && num3 <= polylineLength);
if (!labelPositionEvaluation.Fits)
{
return labelPositionEvaluation;
}
IPolylineWalker polylineWalker = polylineAnalysis.CreateWalker();
polylineWalker.MoveTo(num2);
int currentSegment = polylineWalker.CurrentSegment;
float num4 = (polylineAnalysis.SegmentLengths[polylineWalker.CurrentSegment] - polylineWalker.CurrentOffsetWithinSegment) * polylineWalker.CurrentAngle;
polylineWalker.MoveTo(num3);
int currentSegment2 = polylineWalker.CurrentSegment;
if (currentSegment2 == currentSegment)
{
num4 = ((lengthAround != null) ? (polylineWalker.CurrentAngle * lengthAround.Value * 2f) : (polylineWalker.CurrentAngle * polylineLength));
}
else
{
num4 += polylineWalker.CurrentOffsetWithinSegment * polylineWalker.CurrentAngle;
}
float num5 = 0f;
for (int i = currentSegment; i < currentSegment2; i++)
{
float num6 = (float)GeometryUtils.DifferenceBetweenAngles((double)polylineAnalysis.SegmentAngles[i], (double)polylineAnalysis.SegmentAngles[i + 1], 360.0);
if (num6 > num5)
{
num5 = num6;
}
if (i > currentSegment)
{
num4 += polylineAnalysis.SegmentLengths[i] * polylineAnalysis.SegmentAngles[i];
}
}
labelPositionEvaluation.AverageAngle = (float)GeometryUtils.NormalizeAngle((double)((lengthAround != null) ? (num4 / (lengthAround.Value * 2f) + 225f) : (num4 / polylineLength + 225f)), 360.0);
labelPositionEvaluation.MaxCornerAngle = num5;
float angleForPosition = (float)GeometryUtils.NormalizeAngle((double)(polylineAnalysis.GetAngleForPosition(labelPositionEvaluation.Position) + 225f), 360.0);
labelPositionEvaluation.ForwardDirection = (angleForPosition < 90f || angleForPosition > 270f);
return labelPositionEvaluation;
}

Related

Finding cubes that touch air.. [Searching elements in a vector]

Using Away3D...I have this array of cubes I generated. This array of cubes are in a "Sector" that contains 50x50x50 cubes. The only thing the sector contains is the cubes coordinates and color. They are stored inside the Cube class. I only want to render the ones that touch air (currently "air" the "color" 0xFFFFFF)
I've tried this...an interesting number of ways...
My current method (slowest) was to make vector3D points for each object, and then use indexOf on a set of Vector3Ds containing the points of all the cubes in my "Sector".
public function renderSector(sector:Sector):void
{
trace("init sector render..");
allCubes = sector.cubes;
//Render only things touching air.
for each (var cube:Cube in sector.cubes)
{
//If the cube is not an air block
if (cube.color != 0xFFFFFF)
{
var topEstimate:Vector3D = new Vector3D(cube.x + 1, cube.y, cube.z);
var bottomEstimate:Vector3D = new Vector3D(cube.x, cube.y +1, cube.z);
var leftEstimate:Vector3D = new Vector3D(cube.x + 1, cube.y, cube.z +1);
var rightEstimate:Vector3D = new Vector3D(cube.x - 1, cube.y, cube.z);
var frontEstimate:Vector3D = new Vector3D(cube.x, cube.y -1, cube.z);
var backEstimate:Vector3D = new Vector3D(cube.x, cube.y, cube.z - 1);
//If the cube is next to an air block
if (checkForAir(topEstimate) || checkForAir(bottomEstimate) || checkForAir(leftEstimate) || checkForAir(rightEstimate) || checkForAir(frontEstimate) || checkForAir(backEstimate))
{
var meshCube:Mesh = new Mesh(new CubeGeometry(10, 10, 10), new ColorMaterial(cube.color));
meshCube.x = (sector.x * 125000) + (10 * cube.x);
meshCube.y = (sector.y * 125000) + (10 * cube.y);
meshCube.z = (sector.z * 125000) + (10 * cube.z);
trace("This cube touches air..rendering");
viewport.scene.addChild(meshCube);
}
}
}
}
private function checkForAir(point:Vector3D):Boolean
{
var returnValue:Boolean = new Boolean(false);
var index:int = allCubes.indexOf(point);
if (index > -1)
{
if (allCubes[index].color == 0xFFFFFF)
{
returnValue = true;
}
}
return returnValue;
}
Nothing happens. I get no cubes (letting it run for about 2 minutes) that have an air block next to them using a 3DVevtor. So, I try iterating through all my cubes again while fetching a list of cubes that are "next" to my current cube. I do this by comparing each cube to each other vs a stored 3DVector in my Sector class.
public function renderSector(sector:Sector):void
{
//Render only things touching air.
for each (var cube:Cube in sector.cubes)
{
//If the cube is next to an air block and is not an air block, render it.
if (cube.color != 0xFFFFFF)
{
var touchesAir:Boolean = new Boolean(false);
//Search touching cubes
var touchingCubes:Vector.<Cube> = new Vector.<Cube>();
for each (var possibleCube:Cube in sector.cubes)
{
if ((possibleCube.x == cube.x + 1 && possibleCube.y == cube.y && possibleCube.z == cube.z) ||
(possibleCube.y == cube.y + 1 && possibleCube.x == cube.x && possibleCube.z == cube.z) ||
(possibleCube.z == cube.z + 1 && possibleCube.x == cube.x && possibleCube.y == cube.y) ||
(possibleCube.x == cube.x - 1 && possibleCube.y == cube.y && possibleCube.z == cube.z) ||
(possibleCube.y == cube.y - 1 && possibleCube.x == cube.x && possibleCube.z == cube.z) ||
(possibleCube.z == cube.z - 1 && possibleCube.x == cube.x && possibleCube.y == cube.y))
{
touchingCubes.push(possibleCube);
}
}
for each (var touchingCube:Cube in touchingCubes)
{
if (touchingCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
if (touchesAir)
{
var meshCube:Mesh = new Mesh(new CubeGeometry(10, 10, 10), new ColorMaterial(cube.color));
meshCube.x = (sector.x * 125000) + (10 * cube.x);
meshCube.y = (sector.y * 125000) + (10 * cube.y);
meshCube.z = (sector.z * 125000) + (10 * cube.z);
trace("This cube touches air..rendering");
viewport.scene.addChild(meshCube);
}
}
}
It works..but it takes about 15 seconds for it to find one....The current spec of the Sector is a plane of 50x25x50 grass colored blocks. So this would take a while..
My first method (and oh man was this about an hour+ of brainstorming back) was to fetch the positions of each cube that [i]would[/i] be next to my main cube by basing it on the render order in my world generator function. [Seen below]
public static function generateSector(type:String, position:Vector3D):Sector
{
var returnSector:Sector;
var grassArray:Vector.<uint> = new Vector.<uint>();
grassArray.push(new uint(0x56b000));
grassArray.push(new uint(0x63c900));
grassArray.push(new uint(0x6fe300));
grassArray.push(new uint(0x7cfc00));
//Current types...grass field
switch(type)
{
case "grass":
var cubeArray:Vector.<Cube> = new Vector.<Cube>();
for (var x:int = 0; x < 50; x++) //Moving right
{
for (var z:int = 0; z < 50; z++) //Headed out.
{
for (var y:int = 0; y < 50; y++) //From bottom up.
{
if (y < 25)
{
var color:uint = grassArray[Math.floor(Math.random() * 4)];
}
else
{
var color:uint = 0xFFFFFF;
}
cubeArray.push(new Cube(x,y,z,color));
}
}
}
returnSector = new Sector(position.x, position.y, position.z, cubeArray);
break;
}
return returnSector;
}
Y building first (bottom to top)
then X
then Z
So, simple right? Based on the order of the cubes, I should be able to just pull, for example, the cube on top of my current cube by adding 1 to the index of my current cube, right? (Getting the other cubes respectively based on their order of course and catching errors for any cubes that would be outside of my 50x50x50 grid)
public function renderSector(sector:Sector):void
{
//Render only things touching air.
var counter:int = 0;
for each (var cube:Cube in sector.cubes)
{
//If the cube is next to an air block and is not an air block, render it.
if (cube.color != 0xFFFFFF)
{
var touchesAir:Boolean = new Boolean(false);
try
{
var topCube:Cube = sector.cubes[counter + 1];
if (topCube.color == 0xFFFFFF)
{
touchesAir == true;
}
}
catch(rangeError:RangeError)
{
}
//-------
try
{
var bottomCube:Cube = sector.cubes[counter - 1];
if (bottomCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var leftCube:Cube = sector.cubes[counter - (50 * 50)];
if (leftCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var rightCube:Cube = sector.cubes[(50 * 50) + counter];
if (rightCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var frontCube:Cube = sector.cubes[counter - 50];
if (frontCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
//-------
try
{
var backCube:Cube = sector.cubes[counter + 50];
if (backCube.color == 0xFFFFFF)
{
touchesAir = true;
}
}
catch (rangeError:RangeError)
{
}
if (touchesAir)
{
var meshCube:Mesh = new Mesh(new CubeGeometry(10, 10, 10), new ColorMaterial(cube.color));
meshCube.x = (sector.x * 125000) + (10 * cube.x);
meshCube.y = (sector.y * 125000) + (10 * cube.y);
meshCube.z = (sector.z * 125000) + (10 * cube.z);
trace("This cube touches air..rendering");
viewport.scene.addChild(meshCube);
}
}
}
}
This one renders in about 4 seconds! Though, no cubes actually appear on screen...and the trace statement never fires. I have had no luck finding out why.
TL;DR Let's say you have a grid of cubes. How do you only render the ones that are out in the open?
Or (great alternative) only render mesh's that you can "see". (I need the meshs not merged because I have to have listeners on them to remove them or add new meshes when clicked next to or ontop of them)
You know those moments when you forget to sleep and then you forget to add counters to your for each loops since you're referencing a counter inside of it?
counter = sector.cubes.indexOf(cube);
Need to make sure that the counter (which I should rename indexItem) matched the index of the current cube that I was running through inside my conditional that checks to see if the cube is "air" or not.
Of course, now I'm getting a resource limit error but this can be fixed by reducing the sector size and combining a few meshes.
[Fault] exception, information=Error: Error #3691: Resource limit for this resource type exceeded.

Who to talk to about non recursive Ackermann function?

I have written a non recursive solution to the Ackermann function, it seems to work perfectly and work faster than the common recursive solution. So I am confused as to why it is a non primitive recursive function if it can be solved iteratively? Could anyone tell me if I have misunderstood something about what primitive recursive functions are or who should I talk to about this to get an answer?
Below is the Java code:
import java.util.Scanner;
import java.util.ArrayList;
public class ackermann {
public static void main(String[] args){
Scanner in = new Scanner(System.in);
System.out.println("Enter m:");
int m = in.nextInt();
System.out.println("Enter n:");
int n = in.nextInt();
ack(m, n);
}
public static void ack(int inM, int inN){
if(inM < 0 || inN < 0) return;
ArrayList<ArrayList<Integer>> arr = new ArrayList<ArrayList<Integer>>();
for(int m = 0; m <= inM; m++){
arr.add(new ArrayList<Integer>());
}
Boolean done = false;
while(done == false){
for(int m = 0; m <= inM; m++){
int n = arr.get(m).size();
int a = 0;
if(m == 0) a = n + 1;
else if(n == 0){
if(arr.get(m - 1).size() <= 1) break;
a = arr.get(m - 1).get(1);
} else {
int k = arr.get(m).get(n - 1);
if(arr.get(m - 1).size() <= k) break;
a = arr.get(m - 1).get(k);
}
arr.get(m).add(a);
if(m == inM && n == inN){
System.out.println("Ack(" + inM + ", " + inN + ") = " + a);
done = true;
break;
}
}
}
}
}
Primitive recursive functions can be implemented using only assignment, +, and definite loops. By this I mean loops of the form:
for(int i = 0; i < n; i++) { ... }
Where n is a variable that isn't changed in the loop body. To get Ackermann's function, which majorizes all primitive recursive functions, one needs to add either a goto command or indefinite loops like your while loop.

Distributing widths of a group of objects in AS3

Say I have a set of 5 MovieClips with same y locations but spread out x locations in ascending order (e.g obj1.x = 0, obj5.x = 10), is there an AS3 method that helps me distribute their widths like the option under Modify > Align in flash, so their x locations are equally spaced between 0 and 10?
Thanks
Here is a function I wrote many years ago that you may find useful. It assumes that the things you want to space out are the sole children of a parent/container. You pass that parent to this function as the first parameter (displayObj). Hopefully the comments at the start explain to you well enough what each parameter does, if not just comment and I'll clarify.
/**
* Distribte all the children of the specified display object on either x or y axis
* #param displayObj - the display oject whose children should be distributed
* #param onX - should it distribute along the x axis (true) or the y axis (false)
* #param spacing - how much space between children in pixels
* #param center - should the children be centered on the opposite axis
* #param startingX - default 0
* #param startingY - default 0
* #param fixedWidth - use a fixed width instead the automatic width of each child
* #param foldPoint - how far along before the item should fold and be a new row/col
*/
public static function distributeAxis(displayObj:Sprite,onX:Boolean = true,spacing:Number = 5, center:Boolean = false, startingX:Number = 0,startingY:Number = 0,fixedWidth:Number = 0, foldPoint:Number = 0):void {
//Loop Through Children
var tObj:DisplayObject;
var tNum :Number = (onX) ? startingX : startingY;
var tNum2 :Number = (onX) ? startingY : startingX;
var max :Number = 0;
var centeringArray:Vector.<DisplayObject>
if (center) {
centeringArray = new Vector.<DisplayObject>();
}
for(var i:int = 0; i<displayObj.numChildren;i++)
{
tObj = displayObj.getChildAt(i);
if (onX) {
if (foldPoint > 0 && tNum + tObj.width > foldPoint) {
tNum = startingX;
tNum2 += max + spacing;
if(center){
distributeAxisCenterIt(centeringArray, max, onX);
centeringArray = new Vector.<DisplayObject>();
}
max = 0;
}
if(tObj.height > max) max = tObj.height;
tObj.x = tNum;
tObj.y = tNum2;
if(fixedWidth > 0){
tNum += fixedWidth + spacing;
}else{
tNum += tObj.width + spacing;
}
if(center){
centeringArray.push(tObj);
}
}else{
if(tObj.width > max) max = tObj.width;
if (foldPoint > 0 && tNum + tObj.height > foldPoint) {
tNum = startingY;
tNum2 += max + spacing;
if(center){
distributeAxisCenterIt(centeringArray, max, onX);
centeringArray = new Vector.<DisplayObject>();
}
max = 0;
}
tObj.y = tNum;
tObj.x = tNum2;
if(fixedWidth > 0){
tNum += fixedWidth + spacing;
}else{
tNum += tObj.height + spacing;
}
if(center){
centeringArray.push(tObj);
}
}
}
if (center) {
distributeAxisCenterIt(centeringArray, max, onX);
}
}
private static function distributeAxisCenterIt(array:Vector.<DisplayObject>, max:Number, onX:Boolean = true):void {
for each(var tObj:DisplayObject in array){
if(onX){
tObj.y += ((max - tObj.height) * .5);
}else{
tObj.x += ((max - tObj.width) * .5);
}
}
}
You need to use this: AS3 Commons UI. It has all you could ever want:) with layout/align etc. and components
but if you want a code not a framework this is what I'm using for spreading items across given width in my own "framework":
/**
* Distributes all components using given value as a limit.
* #param p_nMaxWidth width of the area to spread items
*/
protected function spreadComponents(p_nMaxWidth:Number):void
{
if (numChildren == 0) return;
if (p_nMaxWidth < 0 || isNaN(p_nMaxWidth)) p_nMaxWidth = 600;
var _oItem:DisplayObject;
var dx:Number = 0;
var i:int;
var _nWidth:Number = calculateWidths();//get sum of all items widths
var _nStep:Number = (p_nMaxWidth - _nWidth - getChildAt(numChildren - 1).width) / (numChildren-1);
for (i = 0; i < numChildren; i++)
{
_oItem = getChildAt(i);
//if(m_bResetChildrenPosition) _oItem.y = 0;//this I was using to reset or keep intact the items y position
_oItem.x = Math.round(dx);
dx += _oItem.width + _nStep;
}
//as a precaution if rounding would give incorrect position (which should be for last item exactly at the p_nMaxWidth - width (right edge aligned)).
getChildAt(numChildren - 1).x = p_nMaxWidth - getChildAt(numChildren - 1).width;
updateDimension();
}
//
/**
* Utility method, returns width of all children (sum).
* #return
*/
protected function calculateWidths():Number
{
var _nWidth:Number = 0;
for (var i:int = 0; i < numChildren-1; i++)
{
_nWidth += getChildAt(i).width;
}
return _nWidth;
}
this code spreads items horizontaly across given width - left edge of first item is at start, right edge of last item is at the end of that "width".
best regards

How can I implement Lanczos resampling after every canvas transform without having to make a new canvas?

UPDATE: Once I got this demo working... holy smokes, it's SLOW, like 12-16 seconds for only a level 2 render (when image is around 1000x2000 pixels). This is not even worth bothering with.
I found this really awesome and hopeful looking code in the top answer here: Resizing an image in an HTML5 canvas
//returns a function that calculates lanczos weight
function lanczosCreate(lobes){
return function(x){
if (x > lobes)
return 0;
x *= Math.PI;
if (Math.abs(x) < 1e-16)
return 1
var xx = x / lobes;
return Math.sin(x) * Math.sin(xx) / x / xx;
}
}
//elem: canvas element, img: image element, sx: scaled width, lobes: kernel radius
function thumbnailer(elem, img, sx, lobes){
this.canvas = elem;
elem.width = img.width;
elem.height = img.height;
elem.style.display = "none";
this.ctx = elem.getContext("2d");
this.ctx.drawImage(img, 0, 0);
this.img = img;
this.src = this.ctx.getImageData(0, 0, img.width, img.height);
this.dest = {
width: sx,
height: Math.round(img.height * sx / img.width),
};
this.dest.data = new Array(this.dest.width * this.dest.height * 3);
this.lanczos = lanczosCreate(lobes);
this.ratio = img.width / sx;
this.rcp_ratio = 2 / this.ratio;
this.range2 = Math.ceil(this.ratio * lobes / 2);
this.cacheLanc = {};
this.center = {};
this.icenter = {};
setTimeout(this.process1, 0, this, 0);
}
thumbnailer.prototype.process1 = function(self, u){
self.center.x = (u + 0.5) * self.ratio;
self.icenter.x = Math.floor(self.center.x);
for (var v = 0; v < self.dest.height; v++) {
self.center.y = (v + 0.5) * self.ratio;
self.icenter.y = Math.floor(self.center.y);
var a, r, g, b;
a = r = g = b = 0;
for (var i = self.icenter.x - self.range2; i <= self.icenter.x + self.range2; i++) {
if (i < 0 || i >= self.src.width)
continue;
var f_x = Math.floor(1000 * Math.abs(i - self.center.x));
if (!self.cacheLanc[f_x])
self.cacheLanc[f_x] = {};
for (var j = self.icenter.y - self.range2; j <= self.icenter.y + self.range2; j++) {
if (j < 0 || j >= self.src.height)
continue;
var f_y = Math.floor(1000 * Math.abs(j - self.center.y));
if (self.cacheLanc[f_x][f_y] == undefined)
self.cacheLanc[f_x][f_y] = self.lanczos(Math.sqrt(Math.pow(f_x * self.rcp_ratio, 2) + Math.pow(f_y * self.rcp_ratio, 2)) / 1000);
weight = self.cacheLanc[f_x][f_y];
if (weight > 0) {
var idx = (j * self.src.width + i) * 4;
a += weight;
r += weight * self.src.data[idx];
g += weight * self.src.data[idx + 1];
b += weight * self.src.data[idx + 2];
}
}
}
var idx = (v * self.dest.width + u) * 3;
self.dest.data[idx] = r / a;
self.dest.data[idx + 1] = g / a;
self.dest.data[idx + 2] = b / a;
}
if (++u < self.dest.width)
setTimeout(self.process1, 0, self, u);
else
setTimeout(self.process2, 0, self);
};
thumbnailer.prototype.process2 = function(self){
self.canvas.width = self.dest.width;
self.canvas.height = self.dest.height;
self.ctx.drawImage(self.img, 0, 0);
self.src = self.ctx.getImageData(0, 0, self.dest.width, self.dest.height);
var idx, idx2;
for (var i = 0; i < self.dest.width; i++) {
for (var j = 0; j < self.dest.height; j++) {
idx = (j * self.dest.width + i) * 3;
idx2 = (j * self.dest.width + i) * 4;
self.src.data[idx2] = self.dest.data[idx];
self.src.data[idx2 + 1] = self.dest.data[idx + 1];
self.src.data[idx2 + 2] = self.dest.data[idx + 2];
}
}
self.ctx.putImageData(self.src, 0, 0);
self.canvas.style.display = "block";
}
...
img.onload = function() {
var canvas = document.createElement("canvas");
new thumbnailer(canvas, img, 188, 3); //this produces lanczos3
//but feel free to raise it up to 8. Your client will appreciate
//that the program makes full use of his machine.
document.body.appendChild(canvas);
}
However, this implementation loads an image and renders it, end of story.
I have been trying to re-implement this code so that it does the filtering every time an existing canvas is scaled (think, zooming in and out of an image or document) without having to load a new image or create a new canvas.
How can I adapt it to work this way? Or is that even possible?
What you want to do is something like a singleton to reuse your canvas object. This will let you save the cost of create a new canvas object each time and you will reuse the same object
function getCanvas(){
var canvas;
if (typeof canvas === "undefined"){ canvas = document.createElement("canvas");}
return canvas;
}
img.onload = function() {
var canvas = getCanvas("canvas");
.... THE REST OF YOUR CODE .......
}
.
However this is not what slows your code, image scaling Algorithms are really heavy algorithms with intensive cpu use "usually make use of gpu acceleration at a really low level", and use advanced techniques like multiple bufferr and so others. here is a interesting tutorial in java.net on how image scaling works, it is in java but you can interpolate to any language.
Javascript is not ready for this techniques, so I recommend you to use the transformations available in the canvas api, as in the tutorial you read the efficient way is using the canvas2Dcontext.
var ctx = canvas.getContext("2d");
ctx.scale(2,2);

Soft Paint Bucket Fill: Colour Equality

I'm making a small app where children can fill preset illustrations with colours. I've succesfully implemented an MS-paint style paint bucket using the flood fill argorithm. However, near the edges of image elements pixels are left unfilled, because the lines are anti-aliased. This is because the current condition on whether to fill is colourAtCurrentPixel == colourToReplace, which doesn't work on the blended pixels at the lines.
(the colours are RGB uints)
I'd like to add a smoothing/treshold option like in Photoshop and other sophisticated tools, but what's the algorithm to determine the equality/distance between two colours?
if (match(pixel(x,y), colourToReplace) setpixel(x,y,colourToReplaceWith)
How to fill in match()?
Here, an image (left is situation, right is wanted)
alt text http://www.freeimagehosting.net/uploads/6aa7b4ad53.png
Here's my current full code:
var b:BitmapData = settings.background;
b.lock();
var from:uint = b.getPixel(x,y);
var q:Array = [];
var xx:int;
var yy:int;
var w:int = b.width;
var h:int = b.height;
q.push(y*w + x);
while (q.length != 0) {
var xy:int = q.shift();
xx = xy % w;
yy = (xy - xx) / w;
if (b.getPixel(xx,yy) == from) { //<- want to replace this line
b.setPixel(xx,yy,to);
if (xx != 0) q.push(xy-1);
if (xx != w-1) q.push(xy+1);
if (yy != 0) q.push(xy-w);
if (yy != h-1) q.push(xy+w);
}
}
b.unlock(null);
well, i guess the most natural approach is to calculate the difference between to colors. to achieve a sensible value, one should calculate the difference per channel. haven't tested it, but the following should work:
const perChanThreshold:uint = 5;
const overallThreshold:uint = perChanThreshold * perChanThreshold * 3;
function match(source:uint, target:uint):Boolean {
var diff:uint = 0, chanDiff:uint;
for (var i:int = 0; i < 3; i++) {
chanDiff = (source >> (i * 8)) & 0xFF;
diff += chanDiff * chanDiff;
}
return diff <= overallThreshold;
}
Made something that works:
c = b.getPixel(xx,yy);
if (c == to) continue;
if (c != from) d =
Math.pow(f1 - (c & 0xFF), 2) +
Math.pow(f2 - (c >> 8 & 0xFF), 2) +
Math.pow(f3 - (c >> 16 & 0xFF), 2)
if (c == from || d < tres) {