I have a 300px * 300px image. I also have some specific pixels every 100px in every direction (to 16 specific pixels in total).
0 1 2 <-- IntervalX
______ ______ ______
0 | | | |
|______|______|______|
1 | | | |
|______|______|______|
2 | | | |
|______|______|______|
^
IntervalY
I want to put every pixels (except the specific ones) in blocks bounded by the specific pixels, but defined by the pixels value in a 1D array, not 2D array.
const gridX = width / (trueX - 1);
const gridY = width / (trueY - 1);
//Loop for every pixel:
intervalX = Math.floor((pixel[inc].x) / gridX);
intervalY = Math.floor((pixel[inc].y) / gridY);
//Implementing formula for transforming from 1D array to 2D array : (y * width + x) = item number
//All leftmost known pixels start from a "zero" value, so we do not need to substract 1
let isNotFirstArray;
if (intervalX == 0)
isNotFirstArray = 0;
else
isNotFirstArray = 1;
p1 = pixel[intervalY * gridY * 299 + intervalX * gridX - isNotFirstArray];
p2 = pixel[intervalY * gridY * 299 + (intervalX + 1) * gridX - 1];
p3 = pixel[(intervalY + 1) * gridY * 299 + intervalX * gridX - isNotFirstArray];
p4 = pixel[(intervalY + 1) * gridY * 299 + (intervalX + 1) * gridX - 1];
pixel[inc].value = Math.round(bilinearInterpolate(p1, p2, p3, p4, j, i));
inc++;
The problem is the values for the specific pixels are not calculated correctly. The Y values are correct, but the X are not.
Later edit:
The error is like this: the coordinates of the specific pixels are correct on the Y axis, but not on the X axis. They are shifted for the inner intervals with 100px*(max intervals - current interval).
If I get that right, you would like to »filter-out« some of the pixels, which appear at some interval in x and y direction?
const img = <image array>,
imgWidth = 600,
gridX = 150,
gridY = 200;
let result = [];
for (let i = 0; i < input.length; i++) {
let x = i % imgWidth,
y = Math.floor(i / imgWidth);
if (!(x % gridX == 0 || y % gridY == 0)) {
result.push(input[i])
}
}
That should filter out all the indexes which are as special as you said. It remains an array of pixels of an image with the a width.
Related
I'm trying to pixelate an animation.. I have a .png image where I cut in frames. I have a scrollbar who gives a number, which is the new size of every pixel. (rasterSize)
I already did it for an image and it's working.
http://bht-homework.com/RMA/PIX_PRES1/
But for animation it looks like doesn't calculate the first pixels.
http://bht-homework.com/RMA/PIX_PRES2/
var imgData=context.getImageData(0,0,img.width,img.height);// width is 190,height 240
for (var x = 0; x < spriteSizeWidth; x++) {
for (var y = 0; y < spriteSizeHeight; y++) {
var rasterX = ((x / rasterSize) | 0) * rasterSize;
var rasterY = ((y / rasterSize) | 0) * rasterSize;
var rasterValIndex = (rasterX + rasterY * imgData.width) * 4;
r=imgData.data[rasterValIndex];
g=imgData.data[rasterValIndex + 1];
b=imgData.data[rasterValIndex + 2];
a=imgData.data[rasterValIndex + 3];
context.fillStyle="rgba(" +r+","+g+","+b+","+a+")";
context.fillRect(x,y,rasterSize,rasterSize);
}
}
Does someone has an idea how to fix it?
Thanks!
Though it might look as it would skip the first few pixels of your image, it actually just draws the blocks at the wrong position. It's offset by rasterSize.
You need to shift back the pixels to the correct position.
So simply replace
context.fillRect(x, y, rasterSize, rasterSize);
by
context.fillRect(x - rasterSize, y - rasterSize, rasterSize, rasterSize);
Given a grid of size 1000, find the x and y coords of a randomly placed element.
I've tried subdividing the grid into four sections, but I also have to make the solution time-complexity efficient.
const GRID_SIZE = 1000
class RandomElement {
constructor() {
const element = {
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE)
}
this._element = element
}
findInArea(x1, y1, x2, y2) {
console.log(`Scanning area (${x1}, ${y1}, ${x2}, ${y2})`)
return (
this._element.x >= x1 &&
this._element.y >= y1 &&
this._element.x < x2 &&
this._element.y < y2
)
}
findInCell(x, y) {
console.log(`Scanning cell (${x}, ${y}`)
return this._element.x === x && this._element.y === y
}
}
const RandomElement = new RandomElement()
const iselementHere1 = RandomElement.findInArea(0, GRID_SIZE, 0, GRID_SIZE)
console.log('Is element Here?', iselementHere1)
const iselementHere2 = RandomElement.findInArea(0, GRID_SIZE / 2, GRID_SIZE / 2, GRID_SIZE)
console.log('Is element Here?', iselementHere2)
const iselementHere3 = RandomElement.findInArea(GRID_SIZE / 2, 0, GRID_SIZE, GRID_SIZE / 2)
console.log('Is element Here?', iselementHere3)
const iselementHere4 = RandomElement.findInArea(GRID_SIZE / 2, GRID_SIZE / 2, GRID_SIZE, GRID_SIZE)
console.log('Is element Here?', iselementHere4)
Expressions
xx = Math.floor(this._element.x / (GRID_SIZE / 2))
yy = Math.floor(this._element.y / (GRID_SIZE / 2))
give you cell coordinates in 2x2 grid.
You can combine both in single parameter
cellYX = xx + 2 * yy
to get result 0..3 as cell number
0 | 1
-------
2 | 3
Convert your 2d array into another 2d array where every index holds {value, x, y}. Now sort it row / column wise. Then every element search will take rlogc (sorted column-wise) / clogr (sorted row-wise) and just output the (x,y) of that index (it's not the actual co-ordinates, but the co-ordinates of the given 2d-array)
I have this code
if ($('input#grommet').is(':checked')) {
if (width <= 96) {
var grommetQTY = 4;
} else if (width > 96) {
grommetQTY = (Math.floor(width / 24));
grommetQTY = grommetQTY - 4;
grommetQTY = grommetQTY * 2;
grommetQTY = grommetQTY +4;
}
}
and I need to add 2 to the grommetQTY for each whole 24 inches (2 feet) over 96 width. Is there a way to do this?
What this is attempting to accomplish is giving the pricing for a banner. We add 2 more grommets at 2 feet intervals, but because this is custom sizing, it could be 96 feet wide, and I don't want to have to write an else if statement for each two foot interval. I am hoping there is a way to only add two to the quantity everytime the width goes another 24 inches over the standard 96 inch width so if width is 96 or less, qty is 4, at 120 its 6, at 144 its 8, etc
Rather than coming up with the equation for this, here is a for loop that will do what you need:
var grommetQTY = 0;
for(var i = 96; i < n; i++)
if(i%24 == 0)
grommetQTY += 2;
where n is the length. This is terrible inefficient and could be sped up by just doing:
var grommetQTY = 0;
for(var i = 96; i < n; i+=24)
grommetQTY += 2;
This is nicer but still not ideal. The ideal solution in your case would be an equation.
P.S - The equation is Math.floor( Math.max((n - 96), 0) / 24 ) * 2 + 4 if I understand you correctly.
You could do something like this:
var extra = width - 96
var grommetQTY = 4;
if (extra > 0) {
grommet += Math.floor((extra / 24)) * 2;
}
The variable extra holds, as the name implies, extra length that you would want to take into consideration. If it's 0 or below that means it's 96 and under.
Then, for extra > 0 you could alternatively change it to extra >= 24 as any extra value less than 24 will return 0.
Math.pow(2, grommetQTY) * (Math.floor(96 / 24))
2 to the grommetQTY for each whole 24 inches in 96. Is that what you need?
Math.pow(a, b) gives a to the power of b
Edit:
var qty = (Math.floor(n / 24)) * 2
Where n is the pixels (96 in your example).
Edit:
var qty = 4 + ((width > 96) ? ((Math.floor((width - 96) / 24) * 2) : 0);
Is that it?
I am visualising bubbles for cities, bigger if the city has a bigger value. Eg:
London: 14500
New York: 100
Tokyo: 1100
The values range from ~100-15000
I am having trouble creating a function that will return reasonable values, so that the 100 value bubbles aren't too small. The only way I could think to do this is to set a minimum size, eg:
if (size < 5) { size = 5 }
However, this causes the cities with values of ~100 to look very similar to cities with values of ~1000. I'd like the values of approx 0-15000 to return as values between 0.5 and 1 (or something similar). How would this be done?
Here's what I have so far, but like I said it's causing values of 100 and values of 1000 to both be under the min value:
var minBulletSize = 7.5;
var maxBulletSize = 20;
var maxSquare = maxBulletSize * maxBulletSize * 2 * Math.PI;
var minSquare = minBulletSize * minBulletSize * 2 * Math.PI;
// create circle for each location
for (var i = 0; i < mapData.length; i++) {
var dataItem = mapData[i];
var value = dataItem.value;
// calculate size of a bubble
var square = (value/1000 - minBulletSize) / (maxBulletSize - minBulletSize) * (maxSquare - minSquare) + minSquare;
if (square < minSquare) {
square = minSquare;
}
if (square > maxSquare) {
square = maxSquare;
}
var size = Math.sqrt(square / (Math.PI * 2));
var id = dataItem.code;
}
I have taken a look at how to make a logarithmic function to look "logarithmic" within the constraints of 0.5 and 1 :
Math.log10(x / 0.8 + 1) / 3 + 0.5 where x is in between 0 to 24.5.
This is purely a function that seems to look good for me where you can get very dynamic numbers early although a clear growth can be seen in larger numbers.
Feel free to mess around with the numbers, this is VERY subjective.
Next you will need to fit in your 100~15000 range within 0 to 24.5.
I would simply do a x = (x - 100) / 608.16 to get your range to be within 0 to 24.5.
var minBulletSize = 7.5;
var maxBulletSize = 20;
var maxSquare = maxBulletSize * maxBulletSize * 2 * Math.PI;
var minSquare = minBulletSize * minBulletSize * 2 * Math.PI;
// create circle for each location
for (var i = 0; i < mapData.length; i++) {
var dataItem = mapData[i];
var value = dataItem.value;
// assuming value is in between 100 and 15000
value = (value - 100) / 608.16;
value = Math.log10(value / 0.8 + 1) / 3.0 + 0.5;
// value will be in between 0.5 and 1 on a logarithmic scale.
// Do what you want with it :)
}
Tinker the values within the functions until you find a perfect curve for you.
I'm sorry for the bad title (edit if you wish), but I don't know how to put this request. I attached an image from Photoshop that illustrates things a little bit better.
The request itself is quite simple: You have a numeric range from 10.0 to 0.0.
10 stands for the color #00ff00 (deep green) and 0.0 for the color #ff0000 (deep red).
Now, when you hand over the number 10.0 it will output #00ff00. When you give it something like 8.2 it would output something like #00cc33 so that you have a floating gradient all the way down but always float from #00ff00 over orange #ff9900 to #ff0000 (orange in the middle could also be omitted).
One way is to think of the colours like a segment of a circle and then make use of Math.cos or Math.sin.
function toColour(x) {
var pad = function (x) {
x = x.toString(16);
if (x.length === 1) {
return '0' + x;
}
return x;
}, r, g, b;
r = ~~(255 * Math.cos(Math.PI * x / 20)); // cos(0) = 1, cos(π/2) = 0
g = ~~(255 * Math.sin(Math.PI * x / 20)); // sin(0) = 0, sin(π/2) = 1
b = 0;
return '#' + pad(r) + pad(g) + pad(b);
}
// test the gradient produced
for (var i = 0; i <= 10; ++i) {
document.body.appendChild(
document.createElement('div')
).setAttribute('style', 'width: 400px;height: 5px;background: '+toColour(i)+';');
}
See this fiddle for test results (thanks to MikeM)
Just for fun:
function toHex( n ) {
var r = 255 - ( n / 10 * 255 | 0 );
g = n / 10 * 255 | 0;
return '#' +
( r ? ( r = r.toString(16), r.length == 2 ? r : '0' + r ) : '00' ) +
( g ? ( g = g.toString(16), g.length == 2 ? g : '0' + g ) : '00' ) + '00'
}
See it here (for what its worth): http://jsfiddle.net/xD6Uf/
Update
Inspired by Samuel Edwin Ward's use of HSL, the following function adapts hslToRgb from Brian Grinstead's MIT Licensed tinycolor.js:
Note: this function is not intended to be the finished article or represent good practice, it is a proof of concept only. There is no bounds checking and few concessions to readability.
n is a value from 0 to 10, and start, middle and end are each a hue value from 0 - 360.
See Mother-effing hsl to select hue, saturation and lightness values to experiment with.
function toHex( n ) {
var r, g, b, p, q,
start = 0, // 0 - 360 (red 0)
middle = 36, // 0 - 360 (orange 36, use 0 or null for no middle)
end = 120, // 0 - 360 (green 120)
saturation = 1, // 0 - 1
lightness = 0.5, // 0 - 1
hue = ( middle ?
n > 5 ? ( n -= 5, n / 5 * ( end - middle ) ) + middle :
( n / 5 * ( middle - start ) ) + start :
( n / 10 * ( end - start ) ) + start ) / 360;
function hue2hex( p, q, h ){
if ( h < 0 ) h++;
else if ( h > 1 ) h--;
h = ( h < 1/6 ? p + ( q - p ) * 6 * h : h < 1/2 ? q :
h < 2/3 ? p + ( q - p ) * ( 2/3 - h ) * 6 : p ) * 255 | 0;
return h ? ( h = h.toString(16), h.length > 1 ? h : '0' + h ) : '00';
}
if ( saturation === 0 ) {
n = lightness * 255 | 0;
r = g = b = n ?
( n = n.toString(16), n.length > 1 ? n : '0' + n ) : '00';
} else {
q = lightness < 0.5 ? lightness * ( 1 + saturation ) :
lightness + saturation - lightness * saturation;
p = 2 * lightness - q;
r = hue2hex( p, q, hue + 1/3 );
g = hue2hex( p, q, hue );
b = hue2hex( p, q, hue - 1/3 );
}
return '#' + r + g + b;
}
Example
toHex(0); // "#ff0000"
toHex(5); // "#ff9900"
toHex(10); // "#00ff00"
jsfiddle
It looks like you want to interpolate between colors.
You could just take the color values to be numbers and do an interpolation between them, but I think the results would be very odd.
You'll probably want to convert your colors from RGB to another color space and scale the hue component(s). HSL would be a nice choice because CSS3 supports specifying colors in that space.
I put code for both approaches up on jsfiddle.
These are the important parts:
/* numeric interpolation */
for (var value = 0; value < 10; value += 10/40) {
var color = (10-value)/10*(0xff0000-0xff00)+(0x00ff00),
hex = Math.floor(color).toString(16);
while (hex.length < 6) {
hex = "0" + hex;
}
/* hex now holds the hex code */
}
/* hue interpolation */
for (var value = 0; value < 10; value += 10/40) {
var hue = value/10*120, /* red is 0; green is 120 */
color = 'hsl(' + hue + ', 100%, 50%)';
/* hsl now holds the hsl color specification */
}
If you like you can use a fancier approach to interpolating the values than the simple linear formula I used (where x is the input scale and y is the output scale):
y = (x - minX) / (maxX - minX) * (maxY-minY) + minY
So basically you have some range in hex with two variables:
var startHex = "0x00ff00";
var endHex = "0xff0000"
int startInt = parseInt(startHex, 16);
int endInt = parseInt(endHex, 16);
int newInt = startInt + (endInt-startInt)/100 * floatValue; // floatValue is number between 10.0 and 0.0
var newHex = newDec.ToString(16);
I am assuming you intended for 100 increments possible between start and end. This could be done much more cleanly, but for illustration I broke it all out. In practice I would write this in 1-2 lines.
If you want to rely on the browser's rendering engine, you could use HTML5's canvas and createLinearGradient. One advantage of this approach is the ability to use color stops (though you could do this mathematically if you really wanted also).
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d');
canvas.height = 100; canvas.width = 1;
var gradient = context.createLinearGradient(0,0,0,100);
// color stops
gradient.addColorStop(0, '#00ff00');
gradient.addColorStop(0.5, '#ff9900');
gradient.addColorStop(1, '#ff0000');
context.fillStyle = gradient;
context.fillRect(0,0,1,100);
var point = 10.0,
colorData = context.getImageData(0, parseInt(point*canvas.height*.1, 10), 1, 1).data;
// rgba ( colorData[0], colorData[1], colorData[2], colorData[3] )
You can find the exact rgba coordinates of the color in colorData[0] (red), colorData[1] (blue), colorData[2] (green) and then easily convert those to hex values.
If you end up using this solution, set point to a number in the range of 0 to 9.9.