Dithering child elements' dimensions to fill a parent element - javascript

Say I have a parent div with width 500px. It has 13 child elements that should fill its width.
If I give each child element a width of 500 / 13 = 38.46... pixels, the browser will floor the pixel values, so I end up with 13 elements that take up a total of 38 * 13 = 494 pixels. There will be 6 pixels on the right side of the parent div that are not filled.
Is there an easy way to dither the child element widths so that the remainder (6 pixels) is distributed among some of the child elements, resulting in a total width of 500 pixels?
If I have to do the calculations manually and there's no way to get the browser to manage it, what dithering algorithm might I use in this case?
EDIT: A clarification -- I'm doing these calculations on the client side using JavaScript. Also, the size of the parent div and the number of child divs vary at runtime; the figures above are just an example.

I'd suggest you just do everything with integer math yourself. You can then calculate what the uneven amount is and then decide how you want to distribute it across the elements. My supposition is that the least noticeable way to distribute the extra pixels would be to keep as many like width elements next to each other as possible.
One way of doing that would to calculate how many extra pixels N you have and then just give each N elements starting from the left one extra pixel. If you were worried about things not being centered, you could allocate the first extra pixel to the far left object, the second extra pixel to the far right, the third extra pixel to the 2nd from left, the fourth extra pixel from the 2nd from right, etc... This would have one more boundary between dissimilar width objects, but be more symmetric than the first algorithm.
Here's some code that shows how one could put the extra pixels on the end elements from outside in:
function distributeWidth(len, totalWidth) {
var results = new Array(len);
var coreWidth = Math.floor(totalWidth / len);
var extraWidth = totalWidth - (coreWidth * len);
var w,s;
for (var i = 0; i < len; i++) {
w = coreWidth;
if (extraWidth > 0) {
w++;
extraWidth--;
}
if (i % 2 == 0) {
s = i/2; // even, index from front of array
} else {
s = len - ((i+1)/2); // odd, index from end of array
}
results[s] = w;
}
return(results)
}
And here's a fiddle to see it in action: http://jsfiddle.net/jfriend00/qpFtT/2/

Related

How to subdivide a rectangle?

I'm looking to divide a rectangle up into smaller cells while trying to keep the cells close to the same size. Iā€™m trying to keep the cells widths and heights similar. Aspect ratio of 1:1 or closest to that.
Examples:
Number of blocks = 1
return rectangle
Number of blocks = 2
return splitCell(rectangle)
Number of blocks = 3
const cells = splitCell(rectangle)
return [...splitCell(cells[0]), cells[1]]
Number of blocks = 4
const cells = splitCell(rectangle)
return [...splitCell(cells[0]), ...splitCell(cells[1])]
Number of blocks = 5
This is where it starts getting tricky. Firstly the one row has a different number of cells. Secondly the cells in row 1 are a different size to row 2.
?
Number of blocks = 41
?
The split cell function
const splitCell = (cell): Array => cell.width > cell.height ? splitByWidth(cell) : splitByHeight(cell)
My initial solution was to find the largest sided cell and divide that but at a certain point most cells have the same dimensions so it ends up with a group of smaller cells on one side:
Something like:
for (let i = 1; i < n; i++) {
// findBiggest(cells)
// splitCell(biggestCell)
// add splitCell in to result array
}
But I am looking for a solution where most of the cells have a similar aspect ratio
Ideally, you would like to use N squares of area W.H/N, i.e. of side āˆš(W.H/N). So the number of squares per sides should be U=āˆš(N.W/H)) in width and V=āˆš(N.H/W)) in height, or the closest integers.
So you can form the subdivision in U.V squares, and add or remove one cell per row or per column (and rearrange evenly) to adjust to N.
As a fine tuning, you can also adjust the heights of the two different row types by using a penalty value (that rates the discrepancy to the ideal ratio; for instance, the absolute difference of the area and the ideal area W.H/N) and minimize the total penalty.

Function loop comparing three variables to three values contained in a nested array to find best match

I have been a lurker on this site for a while while I have been designing a small program in JavaScript. I have run into an issue that I cant seem to solve though, and I need your help!
You can find my question at the bottom of the post. in layman's terms, I need help setting up a function loop to compare three values against three values in a nested array, where each value can be compared to all three values in the array.
I am working on a type of calculator to find the best way to cut a 3D rectangular piece of material to get the best yield, given a certain cut size I need. I have many different potential sizes of material to cut from, sorted in a nested array, like this:
data[i][x] where i = number key of material arrays available, and x(each array) is set like so
[material ID#, height, width, length, volume]
What I have is a cut size i need to cut out of the parent material, which consists of: height, width, length.
The problem I have run into is the best way to align the cut size in the parent material (comparing each value against the other: height, width, length) to return the highest yield possible.
I will mention that I do have a few constraints.
First, whatever lines up in the height measurement in the parent material can only be 1 cut size tall. The other two dimensions (length and width) can be multiple sizes each if they fit.
so, to reiterate constraints:
Height must be 1 unit.
Width can be multiple units
Length can be multiple units
The orientation of the cut piece does not matter, so long as these constraints are held to.
Length or width can be a decimal, but only whole cut sizes will count.
Below, I have placed some code I designed to do this, but the problem is it is not nearly dynamic enough, as you can see. it only compares height to height, width to width, and so forth.
The input for this is as follows:
cutheight;
cutwidth;
cutlength;
data[i][x]; // each nested data array is like so: [material ID key, height of material, width of material, length of material, volume of material]
for (i = 0; i < data.length; i++) {
if((data[i][1] > cutheight) && (data[i][2] > cutwidth) && (data[i][3] > cutlength)) {
insertSort(results, data[i], (Math.min((data[i][4] * (Math.ceil((numCutPiecesNeeded) / ((Math.floor(data[i][2]/cutwidth)) * (Math.floor(data[i][3]/cutlength)))) ) ))) ); // the last step in this function determines pieces yielded per material
}
}
function insertSort(array, match, materialVolumeMin){
//itterate through the results and see where the new match should be inserted
for( j = 0; j < array.length; j++){
// if the billetVolumeMin of match is less than the materialVolumeMin of the item at position j
if(array[j][5] > materialVolumeMin){
//insert the match at position j
array.splice(j, 0, match);
array[j].push(materialVolumeMin);
return true;
}
}
// if the materialVolumeMin of match is not less than anything in array then push it to the end. This should also cover the case where this is the first match.
// push match
match.push(materialVolumeMin);
array.push(match);
// set match index 5 to the materialVolumeMin for future comparison
//array[array.length -1].push(materialVolumeMin);
return true;
}
What this code does is return all the nested arrays with a new value attached to the end, which is in effect the total volume of material needed to get the cuts you need out of it. I use this volume as a way of sorting the arrays to find the most cost effective (or highest yield) material to use.
I believe this is more of a logic question, but I have no idea how to do this. I do know that it would be better to have multiple cuts available on the height axis as well, but due to various factors this is not possible.
I also believe that the height should be found first, by comparing all three dimensions of the cut size to the parent material, and finding the one that has the least waste
How I see this maybe happening:
Math.min((data[i][1]-cutheight), (data[i][1]-cutwidth), (data[i][1]-cutlength));
From there, I really dont know. I appreciate any and all help, advice, or suggestions. Thank you!
Edit: I dont think I was clear in my Question. The code above is how im going about the problem now, but is by no means how I want to actually do it in the finished program.
My question is:
How can I set up a function to compare my three sizes to each possible material size in my nested array, and return the best match, where data[i][5] (which is data[i][4] (volume of material) times the number of cut pieces I need, divided by how many fit in said parent material) is the smallest out of all of my possible choices.

calculating rotate and translate position based on scroll value of window

I want to move the object in my case its a plane along the shown curve on page scroll step by step taking into consideration the amount of scroll value.firstly the object moves in straight line and then after a point it changes its direction and move in that direction.How to calculate those co-ordinates?
There are two ways you could get this one. I'll try to explain both in detail.
Scenario 1: Simple path like in the question.
Scenario 2: Arbitrary complex path.
Scenario 1:
In this case you can use a simple formula. Let's go with y = -x^2. This will yield a parabola, which has a similar shape as the path in the question. Here are the steps for what to do next (we assume your scrolling element is the body tag and I assume you have jquery):
Get the "y" value of the body using the following code:
var y = $("body").scrollTop();
Plug this value into the formula. I will give 3 examples where y is 0, 100 and 225 respectively.
//y=0
0 = -x^2
-x = sqrt(0)
x = +/- 0
So if we scroll and we are at the top of the page, then x will be zero.
//y=100
100 = -x^2
-x = sqrt(100)
x = +/- 10
The equation yieldsx as either positive of negative x but we only want positive so be sure to Math.abs() the result.
//y=225
225= -x^2
-x = sqrt(225)
x = +/- 15
From this you can see that the further we scroll down the more the object moves to the right.
Now set the "left" css of your object to the calculated value. This should be enough for this method.
Scenario 2
For more complex paths (or even random paths) you should rather put the x-values into an array ahead of time. Lets say you generate the array randomly and you end up with the following x-values:
var xvals = [0, 0.5, 1, 0.5, 0];
We use normalized x-values so that we can later calculate the position independent from screen size. This particular series of values will cause the object to zig-zag across the screen from left to right, then back to left.
The next step is to determine where our scroll position is at relative to the total scroll possibility. Lets say our page is 1000px in height. So if the scoll position is at zero then x = 0. If scroll = 500 then x = screenWidth. If scroll = 250 then x = 0.5 * screenWidth etc.
In the example I won't multiply with screen width for the sake of simplicity. But given the x value this should be simple.
The first thing you might want to get ready now is a lerping function. There is plenty of example code and so on so I trust that part to you. Basically it is a function that looks like this:
function lerp(from, to, prog);
Where from and to are any values imaginable and prog is a value between 0 and 1. If from is 100 and to is 200, a prog value of 0.5 will yield a return of 150.
So from here we proceed:
Get the scroll value as a normalized value
// scrollval = 200
var totalScroll = 1000;
var normScroll = scrollval/totalScroll; // answer is 0.2
Before we get to lerp we first need to get the x-values to lerp from and to. To do this we have to do a sort of lerping to get the correct index for the xvals array:
// normScroll = 0.2
var len = xvals.length; // 5
var indexMax = Math.ceil((len-1) * normScroll); // index = 1
var indexMin = Math.floor((len-1) * normScroll); // index = 0
Now we know the 2 x values to lerp between. They are xvals[0] which is 0, and xvals[1] which is 0.5;
But this is still not enough information. We also need the exact lerping "prog" value:
// We continue from the x indeces
scrollRange.x = totalScroll / len * indexMin; // 0
scrollRange.y = totalScroll / len * indexMax; // 250
var lerpingvalue = (scrollVal - scrollRange.x) / (scrollRange.y - scrollRange.x);// 0.8
Now we finally have everything we need. Now we know we need a value between xvals[0] and xvals[1] and that this value lies at 80% between these two values.
So finally we lerp:
var finalX = lerp(xvals[0], xvals[1], lerpingvalue);// 0.4
Now we know that the x coordinate is at 0.4 of the total screen size.
Trigger these calculations on each scroll event and you should be on your way.
I hope this was clear enough. If you try and try and can't get results and can show how hard you tried then I'll be happy to write a complete index.html sample for you to work from. Good luck!
EDIT: I made a mistake with the lerpingval calculation. Fixed now.

Selecting the "lowest" items in a 2d array

I have an array - it's populated with 1s and 0s.
When "rendered" out - it looks like this:
Basically, I would like to select the lower sections (highlighted in red). Is there anyway to select just the lowest 1s?
Thanks again!
[edit]
I should mention that the lowest points are random each time!
[edit2]
Currently I'm just selecting everything below a certain area and seeing if it's a 1 and doing what I want... Is there no better way?
You loop through the 2d array in reverse...
var lowest = [];
var threshold = 6; // find the 6 "lowest" 1's
for(var row=arr.length-1; row>=0; row--)
for(var col=arr[row].length-1; col>=0; col--)
if(arr[row][col] == 1 && threshold > 0) {
threshold--;
lowest.push({x: col, y: row});
}
Another way :
1) compute per row density = number of black pixel per row
put this data inside a new 1D array.
2) decide where you consider it is leg or not (with a treshold possibly,
or a relative threshold (ex: less than 30% mean value of non-null rows ...) ).
3) push all (x,y) values in the 'leg' rows.
This will avoid lots of small points 'eating' the pixel threshold
before you come to the body of the monster.

Arranging rectangles within a boundary without overlap and x contraints

I am trying to arrange an unknown number of rectangles so that they dont overlap each other. There are a number of constraints when rearranging rectangles:
Can ONLY move in the positive y (up) direction with the exception the
condition where moving up will push the rectangle outside of the
container boundary.
CANNOT move in the x (left or right) direction We should get some
reasonable padding in between rectangles on all sides.
The top most rectangle should be the first rectangle (denoted by a label in the jsbin link)
I wrote a small something that would generate the main problem here at jsbin. So far the only thing that comes to my mind is a situation where I iterate through these rectangles back and forth. I was wondering if anyone can suggest an approach or better yet point to an existing solution.
You need to compute the vertical position of each rectangle using the height of the preceding rectangles. It may be useful to check if the problem has a solution.
// Generate random rectangles, with the vertical position set to zero
var padding = 5;
var num_rectangles = 10;
var rectangles = [];
for (var k = 0; k < num_rectangles; k += 1) {
rectangles.push({
x: 50 * Math.random(),
y: padding,
width: Math.max(50 * Math.random(), 20),
height: Math.max(50 * Math.random(), 20)
});
}
// Update the vertical position of the item j as the sum of the heigths
// of the rectangles 0, ..., j - 1
for (var j = 1; j < num_rectangles; j += 1) {
rectangles[j].y = rectangles[j - 1].y + rectangles[j - 1].height + padding;
}
And then just draw the rectangles as usual. I wrote a small example http://jsfiddle.net/FuepP/2/
The only thing I can think of at the moment is basically a branch and bound search. Starting from the bottom, iteratively resolve colliding pairs by pushing one or the other rectangle up (branching). If you go above the limit, backtrack to the previous branch.
I wouldn't be surprised if solving this problem was NP-Complete. It's a more constrained version of the bin packing problem. On the other hand, over-constraining problems tends to make them very easy so maybe it's not NP-Complete. I tried to think of a reduction for a few minutes but didn't come up with anything.

Categories