docs: protected boolean fit2D(List<Box> containerProducts, Container holder, Box usedSpace, Space freeSpace, BooleanSupplier interrupt) {
  
  		if(rotate3D) {
  			// minimize footprint
  			usedSpace.fitRotate3DSmallestFootprint(freeSpace);
  		}
  
  		// add used space box now, but possibly rotate later - this depends on the actual remaining free space selected further down
  		// there is up to possible 4 free spaces, 2 in which the used space box is rotated
  		holder.add(new Placement(freeSpace, usedSpace));
  
  		if(containerProducts.isEmpty()) {
  			// no additional boxes
  			// just make sure the used space fits in the free space
  			usedSpace.fitRotate2D(freeSpace);
  
  			return true;
  		}
  
  		if(interrupt.getAsBoolean()) {
  			return false;
  		}
  
  		Space[] spaces = getFreespaces(freeSpace, usedSpace);
  
  		Placement nextPlacement = getBestBoxAndSpace(containerProducts, spaces, holder.getFreeWeight());
  		if(nextPlacement == null) {
  			// no additional boxes along the level floor (x,y)
  			// just make sure the used space fits in the free space
  			usedSpace.fitRotate2D(freeSpace);
  		} else {
  			// check whether the selected free space requires the used space box to be rotated
  			if(nextPlacement.getSpace() == spaces[2] || nextPlacement.getSpace() == spaces[3]) {
  				// the desired space implies that we rotate the used space box
  				usedSpace.rotate2D();
  			}
  	
  			// holder.validateCurrentLevel(); // uncomment for debugging
  	
  			removeIdentical(containerProducts, nextPlacement.getBox());
  	
  			// attempt to fit in the remaining (usually smaller) space first
  	
  			// stack in the 'sibling' space - the space left over between the used box and the selected free space
  			Space remainder = nextPlacement.getSpace().getRemainder();
  			if(remainder.nonEmpty()) {
  				Box box = getBestBoxForSpace(containerProducts, remainder, holder.getFreeWeight());
  				if(box != null) {
  					removeIdentical(containerProducts, box);
  	
  					if(!fit2D(containerProducts, holder, box, remainder, interrupt)) {
  						return false;
  					}
  				}
  			}
  			// double check that there is still free weight
  			// TODO possibly rewind if the value of the boxes in the remainder is lower than the one in next placement
  			if(holder.getFreeWeight() >= nextPlacement.getBox().getWeight()) {
  				if(!fit2D(containerProducts, holder, nextPlacement.getBox(), nextPlacement.getSpace(), interrupt)) {
  					return false;
  				}
  			}
  		} 
  		
  		// also use the space above the placed box, if any.
  		if(freeSpace.getHeight() > usedSpace.getHeight()) {
  			// so there is some free room; between the used space and the level height
  
  			// the level by level approach is somewhat crude, but at least some of the inefficiency
  			// can be avoided this way
  			Space above;
  			if(nextPlacement == null) {
  				// full width / depth
  				above = new Space(
  						freeSpace.getWidth(), 
  						freeSpace.getDepth(), 
  						freeSpace.getHeight() - usedSpace.getHeight(),
  						freeSpace.getX(),
  						freeSpace.getY(),
  						freeSpace.getZ() + usedSpace.getHeight()
  						);
  			} else {
  				// just directly above the used space
  				
  				// TODO possible include the sibling space if no box was fitted there
  				above = new Space(
  						usedSpace.getWidth(), 
  						usedSpace.getDepth(), 
  						freeSpace.getHeight() - usedSpace.getHeight(),
  						freeSpace.getX(),
  						freeSpace.getY(),
  						freeSpace.getZ() + usedSpace.getHeight()
  						);
  			}
  			int currentIndex = getBestBox(holder, above, containerProducts);
  
  			if(currentIndex != -1) {
  				// should be within weight already
  				Box currentBox = containerProducts.get(currentIndex); 
  				
  				containerProducts.remove(currentIndex);
  				
  				if(!fit2D(containerProducts, holder, currentBox, above, interrupt)) {
  					return false;
  				}
  			}
  		}
  		
  		return true;
  	}