I have an array of 50 objects, each width a different width and height. My objective is to create panels to hold this objects and keep them in order, but this panel cannot exceed a width of 300 pixels or a height of 600 pixels, so I have to fit as many objects as I can within this panel and if they don't fit then I need to create a new panel until there are no more items to append.
So for example if I have an item with the dimensions of 150x75, then I could only place another object of the same dimensions, and if the next item does not have this dimensions then I create an empty object to take up these empty space. An example can be found at Codepen.io
Now this works as long as the height of each item is 75px, but the problem arises when the height exceeds this. I have been trying to solve this problem for a few days now, but this particular task seems to throw of the entire design. Here is my logic to check if the item fits:
layout.prototype.createPanel = function(){
//create a new panel
var panel = '<div style="float:left;margin:5px;border:2px solid black;min-width:'+this.WIDTH+'px;min-height:'+this.HEIGHT+'px;">';
var h = 0;
//as long as the panel height does not exceed the constant height
while(h < this.HEIGHT){
//this.commands += h+'<br>';
var w = 0;
panel += '<div>'; //create a new row
//as long as the panel width does not exceed the constant width
while(w < this.WIDTH){
var el = this.items[0]; //grab the first item for checking purposes
var fits = false;
//if the current column is the first one, then just check if the item fits vertically
if(w < this.int_width){
//as long as the item's height plus the current height of this panel is less than the static height, then it fits
fits = h + el.height <= this.HEIGHT ? true : false;
}
//else check if it fits horizontal and vertically
else{
if(this.fitsHorizontal(w,el.width) && h + el.height <= this.HEIGHT){
fits = true;
}
else{
fits = false;
}
}
//if the item fits, remove it from the array and append it to the panel
if(fits){
el = this.items.shift();
panel += el.html;
w+= el.width; //update the width
this.desired_height = el.height; //update the new desired height
//if this shifted item was the last one then terminate execution to avoid any infinite loops
if(this.items.length == 0){
this.has_items = false;
break;
}
}
//else create an empty element to fill up the space needed
else{
panel += '<div style="float:left;width:'+(this.WIDTH - w)+'px;height:'+this.desired_height+'px;background-color:#ccc;margin:1px;"></div>';
el.height = this.int_height;
break;
}
}
panel += '</div>'; //close the row
//if this shifted item was the last one then terminate execution to avoid any infinite loops
if(!this.has_items){
this.has_items = false;
break;
}
h+=this.desired_height;
}
panel += '</div>'; //close the panel
return panel;
}
layout.prototype.fitsHorizontal = function(current_width,item_width){
//if the current width plus the item width is less than the constant width them the item fits horizontal
if(current_width + item_width <= this.WIDTH){
return true;
}
return false;
}
layout.prototype.fitsVertical = function(current_height,item_height){
if(current_height + item_height <= this.HEIGHT && item_height == this.desired_height){
return true;
}
return false;
}
Is there anything wrong with the logic? I now I could solve it if I reorder the items, but they have to stay in order. The full code can be found ad Codepen.io
About your code, I can't understand what is the problem because it is not clear. I would like to give you some tips:
-When you need to work with someone (like here if you need help, or in a project, etc), you should share the same identical things that you know.
-In this case, where the language is not a specification, you should abstract the code as much as you can, pseudo-code is a good begin.
-you can describes the problems you met, from some example but also from images and schemes.
-If you add a specific codification to represent the problem you should respect that codification.
It is not really clear how your code works because it seems an hybrid between js and a pseudo-code. For what I can understand, the first logic error it seems to be related to the space division (rows columns): for example if you have 4 elements 1x1 (rows x columns) they will fill 1 row but, if you have 1 element 1x1,1 element 2x1 (rows x columns), 1 element 1x1 (rows x columns) and another element 2x1 (rows x columns), they complete a row, but you will also find some pieces in the row below, and you doesn't seems to registers this kind of behaviour in your code.
I hope to have understood well, here the jsfiddle and here the code:
<!DOCTYPE html>
<head>
<style>
.pad div{
margin:1px;
}
.pad{
background-color:#FFF;
border:#000 1px solid;
}
</style>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<body>
<script>
var pieceN=50;
var pieces = [];
const multip = 75;
var actualmaxheight=0;
const h=600;
const w=300;
const maxrows = 5;
const maxcols = 4;
var id="";
var actualcontent=0;
$('body').append('<div id="content'+actualcontent+'" class="pad" style="'+
'height:'+(h+4)+'px;width:'+(w+4)+'px"></div>');
id='content'+actualcontent;
actualcontent++;
//creating objects n times
for(var i=0; i<(pieceN); i++){
color=(Math.floor(Math.random()*16777215)).toString(16);
if(color.length==5){ color=color+'0'};
if(color.length==4){ color=color+'00'};
//for faster thing I'll use an already sorted array
eit = Math.floor((Math.random()*maxrows)+1);
wid = Math.floor((Math.random()*maxcols)+1);
pieces.push({
'height': eit*multip,
'width': wid*multip,
'rows': eit,
'cols': wid,
'color':'#'+color
});
}
var copy=pieces;
var i =0;
//while i is less then the number of the objects -1
//the last element will be inserted afther this cycle
//it can be also inserted in the cylce, anyway it's only 1 loop
while(i<pieces.length-1){
//if there is enough height for the element in the container
if(h-actualmaxheight>=parseInt(pieces[i].height)){
//the width fill te container width (insert)
if(parseInt(pieces[i].width)==w){
document.getElementById(id).innerHTML+=
'<div id="'+pieces.length+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>';
actualmaxheight+=parseInt(pieces[i].height);
}else{//if the width don't fill te container
//if this elem+nextone width fill the container width (insert x 2)
if(((parseInt(pieces[i].width)+parseInt(pieces[i+1].width))==w) && (parseInt(pieces[i].height)==parseInt(pieces[i+1].height))){
document.getElementById(id).innerHTML+=
'<div id="'+i+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(i+1)+'" style="'+
'width:'+pieces[i+1].width+'px;'+
'height:'+pieces[i+1].height+'px;'+
'background-color:'+pieces[i+1].color+';'+
'float:left"></div>';
actualmaxheight+=parseInt(pieces[i].height);
i++;//because 2 inserted
}else{
//else we insert 1 elem + 1 filling element
document.getElementById(id).innerHTML+=
'<div id="'+i+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(pieces.length+100)+'" style="'+
'width:'+(w-pieces[i].width)+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>';
actualmaxheight+=parseInt(pieces[i].height);
}
}
i++;//we surely inserted an elem
}else{
//else the container have not enough height for this elem
//fill the height with a black block and don't increase i
//because we inserted nothing
document.getElementById(id).innerHTML+=
'<div id="'+(pieces.length+1000)+'" style="'+
'width:'+w+'px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;'+
'float:left;color:#FFFFFF">here to fill H</div>';
$('body').append('<div id="content'+actualcontent+'" class="pad" style="'+
'height:'+(h+6+actualcontent*1.2)+'px;width:'+(w+4)+'px;color:#FFFFFF"></div>');
id='content'+actualcontent++; //id of the container updated
actualmaxheight=0;//reset height
}
}
//insert last element
i=pieces.length-1;//get its array index
if(h-actualmaxheight>=pieces[i].height){//there is enough height in this container
//the elem fill the width
if(parseInt(pieces[i].width)==w){
actualmaxheight+=pieces[i].height;
document.getElementById(id).innerHTML+=
'<div id="'+pieces.length+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(pieces.length+1000)+'" style="'+
'width:'+w+'px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;color:#FFFFFF;'+
'float:left">here to fill H</div>';
}else{ //the elem don't fill the width
actualmaxheight+=pieces[i].height;
document.getElementById(id).innerHTML+=
'<div id="'+pieces.length+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(pieces.length+10000)+'" style="'+
'width:'+(w-pieces[i].width)+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(pieces.length+1000)+'" style="'+
'width:'+w+'px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;color:#FFFFFF;'+
'float:left">here to fill H</div>';
}
}else{//there is not enough height in this container
if(parseInt(pieces[i].width)==w){//the elem fill the width
//fill the height with a black block
document.getElementById(id).innerHTML+=
'<div id="'+(pieces.length+1000)+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;color:#FFFFFF;'+
'float:left">here to fill H</div>';
//create another container and add the elem + black block to fill the height
actualmaxheight=parseInt(pieces[i].height);
$('body').append('<div id="content'+actualcontent+'" class="pad" style="'+
'height:'+(h+4)+'px;width:'+(w+4)+'px">'+
'<div id="'+pieces.length+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(pieces.length+1000)+'" style="'+
'width:'+w+'px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;color:#FFFFFF;'+
'float:left">here to fill H</div>'+
'</div>');
}else{//the elem don't fill the width
//fill the height with a black block to fill the height
document.getElementById(id).innerHTML+=
'<div id="'+(pieces.length+1000)+'" style="'+
'width:'+w+'px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;color:#FFFFFF;'+
'float:left">here to fill H</div>';
actualmaxheight=parseInt(pieces[i].height);
//create another container and add the elem + 1 elem to fill the width
//+ 1 black block to fill the height
$('body').append('<div id="content'+actualcontent+'" class="pad" style="'+
'height:'+(h+4)+'px;width:'+(w+4)+'px">'+
'<div id="'+pieces.length+'" style="'+
'width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(pieces.length+100)+'" style="'+
'width:'+(w-pieces[i].width)+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';'+
'float:left"></div>'+
'<div id="'+(pieces.length+1000)+'" style="'+
'width:'+w+'px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;color:#FFFFFF;'+
'float:left">here to fill H</div>'+
'</div>');
}
}
/*
//print the elments
for(var i=0; i<(copy.length); i++){
$('body').append('<div id="el'+i+'"'+
'style="width:'+copy[i].width+'px;'+
'height:'+copy[i].height+'px;'+
'background-color:'+copy[i].color+';float:left"></div>'
);
}
*/
</script>
</body>
</html>
I would like to clear some points
Anyway to present something, I wrote this algorithm (jsfiddle) that can or cannot be useful, depending on what are you searching :
<!DOCTYPE html>
<head>
<style>
.pad div{
margin:1px;
}
.pad{
background-color:#FFF;
border:#000 1px solid;
}
</style>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<body>
<script>
var pieceN=50;
var pieces = [];
var actualmaxheight=0;
var h=600;
var w=300;
var flag=0;
$('body').append('<div id="content0" class="pad" style="'+
'height:'+(h+4)+'px;width:'+(w+4)+'px"></div>');
//creating objects n times
for(var i=0; i<(pieceN); i++){
color=(Math.floor(Math.random()*16777215)).toString(16);
if(color.length==5){ color=color+'0'};
//for faster thing I'll use an already sorted array
pieces.push({
'height': Math.floor((Math.random()*600)+1),
'width':Math.floor((Math.random()*150)+1),
'color':'#'+color
});
}
var copy=pieces;
id='content0';
//for all the elements aren't placed(i remove every placed element)
for(i=0;i<pieces.length;i++){
flag=0;
k=0;
//while k<pieces.length confront every piece[k] with piece[i]
while(k<pieces.length){
//if exist 2 piece with sum of width==w(300), the same height and the actual container have enough height, let's add them
if(parseInt(pieces[i].width)+parseInt(pieces[k].width)==w && pieces[i].height==pieces[k].height && ((h-actualmaxheight)>parseInt(pieces[i].height))){
document.getElementById(id).innerHTML+='<div id="e'+(i+k)+'"'+
'style="width:'+pieces[i].width+'px;'+
'height:'+pieces[i].height+'px;'+
'background-color:'+pieces[i].color+';float:left"></div>'+
'<div id="e'+(k+i)+'2"'+
'style="width:'+pieces[k].width+'px;'+
'height:'+pieces[k].height+'px;'+
'background-color:'+pieces[k].color+';float:left"></div>';
actualmaxheight+=parseInt(pieces[i].height);
//remove them from the array
pieces = $.grep( pieces, function(n,index){
return index != k;
});
pieces = $.grep( pieces, function(n,index){
return index != i;
});
flag=1;
}
k++;
}
//if we inserted 0 elements in the while before, we have 2 reason
//1) there aren't 2 elements which satisfy the rules
//in this case we add the element with an element created to fill the empty space orizontal
if(flag==0){
for(j=0;j<pieces.length;j++){
if((h-actualmaxheight)>parseInt(pieces[j].height)){
document.getElementById(id).innerHTML+='<div id="e'+i+'"'+
'style="width:'+pieces[j].width+'px;'+
'height:'+pieces[j].height+'px;'+
'background-color:'+pieces[j].color+';float:left"></div>'+
'<div id="e'+i+'2"'+
'style="width:'+(w-parseInt(pieces[j].width))+'px;'+
'height:'+pieces[j].height+'px;'+
'background-color:'+pieces[j].color+';float:left"></div>';
actualmaxheight+=parseInt(pieces[j].height);
pieces = $.grep( pieces, function(n,index){
return index != j;
});
}
}
}
//2) there isn't an element which can be conained in the space remained in the container
//in this case we add a black stripe to fill the space vertical
if(actualmaxheight<h){
document.getElementById(id).innerHTML+='<div id="e'+j+i*k+'"'+
'style="width:300px;'+
'height:'+(h-actualmaxheight)+'px;'+
'background-color:#000000;float:left;color:#FFFFFF">i fill the space</div>';
}
//then we create a new container to repeat until all the elements are placed
$('body').append('<div class="pad" id="content'+j+'"'+
'style="margin-top:10px;'+
'height:'+(h+4)+'px;width:'+(w+4)+'px"></div>');
id='content'+j;
actualmaxheight=0;
i=0;
}
//print the elments
for(var i=0; i<(copy.length); i++){
$('body').append('<div id="el'+i+'"'+
'style="width:'+copy[i].width+'px;'+
'height:'+copy[i].height+'px;'+
'background-color:'+copy[i].color+';float:left"></div>'
);
}
</script>
</body>
</html>