public function getBestOrientation(
        Item $item,
        ?OrientatedItem $prevItem,
        ItemList $nextItems,
        bool $isLastItem,
        int $widthLeft,
        int $lengthLeft,
        int $depthLeft,
        int $rowLength
    ): ?OrientatedItem {
        $possibleOrientations = $this->getPossibleOrientations($item, $prevItem, $widthLeft, $lengthLeft, $depthLeft);
        $usableOrientations = $this->getUsableOrientations($item, $possibleOrientations, $isLastItem);

        if (empty($usableOrientations)) {
            return null;
        }

        usort($usableOrientations, function (OrientatedItem $a, OrientatedItem $b) use ($widthLeft, $lengthLeft, $depthLeft, $nextItems, $rowLength) {
            $orientationAWidthLeft = $widthLeft - $a->getWidth();
            $orientationALengthLeft = $lengthLeft - $a->getLength();
            $orientationADepthLeft = $depthLeft - $a->getDepth();
            $orientationBWidthLeft = $widthLeft - $b->getWidth();
            $orientationBLengthLeft = $lengthLeft - $b->getLength();
            $orientationBDepthLeft = $depthLeft - $b->getDepth();

            $orientationAMinGap = min($orientationAWidthLeft, $orientationALengthLeft);
            $orientationBMinGap = min($orientationBWidthLeft, $orientationBLengthLeft);

            if ($orientationAMinGap === 0) { // prefer A if it leaves no gap
                return -1;
            }
            if ($orientationBMinGap === 0) { // prefer B if it leaves no gap
                return 1;
            }

            // prefer leaving room for next item in current row
            if ($nextItems->count()) {
                $nextItemFitA = count($this->getPossibleOrientations($nextItems->top(), $a, $orientationAWidthLeft, $lengthLeft, $depthLeft));
                $nextItemFitB = count($this->getPossibleOrientations($nextItems->top(), $b, $orientationBWidthLeft, $lengthLeft, $depthLeft));
                if ($nextItemFitA && !$nextItemFitB) {
                    return -1;
                }
                if ($nextItemFitB && !$nextItemFitA) {
                    return 1;
                }

                // if not an easy either/or, do a partial lookahead
                $additionalPackedA = $this->calculateAdditionalItemsPackedWithThisOrientation($a, $nextItems, $widthLeft, $lengthLeft, $depthLeft, $rowLength);
                $additionalPackedB = $this->calculateAdditionalItemsPackedWithThisOrientation($b, $nextItems, $widthLeft, $lengthLeft, $depthLeft, $rowLength);
                if ($additionalPackedA !== $additionalPackedB) {
                    return $additionalPackedB <=> $additionalPackedA;
                }
            }
            // otherwise prefer leaving minimum possible gap, or the greatest footprint
            return $orientationADepthLeft <=> $orientationBDepthLeft ?: $orientationAMinGap <=> $orientationBMinGap ?: $a->getSurfaceFootprint() <=> $b->getSurfaceFootprint();
        });

        $bestFit = reset($usableOrientations);
        $this->logger->debug('Selected best fit orientation', ['orientation' => $bestFit]);

        return $bestFit;
    }
public function getBestOrientation(
        Item $item,
        ?OrientatedItem $prevItem,
        ItemList $nextItems,
        bool $isLastItem,
        int $widthLeft,
        int $lengthLeft,
        int $depthLeft,
        int $rowLength
    ): ?OrientatedItem {
        $possibleOrientations = $this->getPossibleOrientations($item, $prevItem, $widthLeft, $lengthLeft, $depthLeft);
        $usableOrientations = $this->getUsableOrientations($item, $possibleOrientations, $isLastItem);

        if (empty($usableOrientations)) {
            return null;
        }

        usort($usableOrientations, function (OrientatedItem $a, OrientatedItem $b) use ($widthLeft, $lengthLeft, $depthLeft, $nextItems, $rowLength) {
            $orientationAWidthLeft = $widthLeft - $a->getWidth();
            $orientationALengthLeft = $lengthLeft - $a->getLength();
            $orientationADepthLeft = $depthLeft - $a->getDepth();
            $orientationBWidthLeft = $widthLeft - $b->getWidth();
            $orientationBLengthLeft = $lengthLeft - $b->getLength();
            $orientationBDepthLeft = $depthLeft - $b->getDepth();

            $orientationAMinGap = min($orientationAWidthLeft, $orientationALengthLeft);
            $orientationBMinGap = min($orientationBWidthLeft, $orientationBLengthLeft);

            if ($orientationAMinGap === 0) { // prefer A if it leaves no gap
                return -1;
            }
            if ($orientationBMinGap === 0) { // prefer B if it leaves no gap
                return 1;
            }

            // prefer leaving room for next item in current row
            if ($nextItems->count()) {
                $nextItemFitA = count($this->getPossibleOrientations($nextItems->top(), $a, $orientationAWidthLeft, $lengthLeft, $depthLeft));
                $nextItemFitB = count($this->getPossibleOrientations($nextItems->top(), $b, $orientationBWidthLeft, $lengthLeft, $depthLeft));
                if ($nextItemFitA && !$nextItemFitB) {
                    return -1;
                }
                if ($nextItemFitB && !$nextItemFitA) {
                    return 1;
                }

                // if not an easy either/or, do a partial lookahead
                $additionalPackedA = $this->calculateAdditionalItemsPackedWithThisOrientation($a, $nextItems, $widthLeft, $lengthLeft, $depthLeft, $rowLength);
                $additionalPackedB = $this->calculateAdditionalItemsPackedWithThisOrientation($b, $nextItems, $widthLeft, $lengthLeft, $depthLeft, $rowLength);
                if ($additionalPackedA !== $additionalPackedB) {
                    return $additionalPackedB <=> $additionalPackedA;
                }
            }
            // otherwise prefer leaving minimum possible gap, or the greatest footprint
            return $orientationADepthLeft <=> $orientationBDepthLeft ?: $orientationAMinGap <=> $orientationBMinGap ?: $a->getSurfaceFootprint() <=> $b->getSurfaceFootprint();
        });

        $bestFit = reset($usableOrientations);
        $this->logger->debug('Selected best fit orientation', ['orientation' => $bestFit]);

        return $bestFit;
    }
public function getBestOrientation ( Item $ item , ? OrientatedItem $ prevItem , ItemList $ nextItems , bool $ isLastItem , int $ widthLeft , int $ lengthLeft , int $ depthLeft , int $ rowLength ) : ? OrientatedItem { $ possibleOrientations = $ this -> getPossibleOrientations ( $ item , $ prevItem , $ widthLeft , $ lengthLeft , $ depthLeft ) ; $ usableOrientations = $ this -> getUsableOrientations ( $ item , $ possibleOrientations , $ isLastItem ) ; if ( empty ( $ usableOrientations ) ) { return null ; } usort ( $ usableOrientations , function ( OrientatedItem $ a , OrientatedItem $ b ) use ( $ widthLeft , $ lengthLeft , $ depthLeft , $ nextItems , $ rowLength ) { $ orientationAWidthLeft = $ widthLeft - $ a -> getWidth ( ) ; $ orientationALengthLeft = $ lengthLeft - $ a -> getLength ( ) ; $ orientationADepthLeft = $ depthLeft - $ a -> getDepth ( ) ; $ orientationBWidthLeft = $ widthLeft - $ b -> getWidth ( ) ; $ orientationBLengthLeft = $ lengthLeft - $ b -> getLength ( ) ; $ orientationBDepthLeft = $ depthLeft - $ b -> getDepth ( ) ; $ orientationAMinGap = min ( $ orientationAWidthLeft , $ orientationALengthLeft ) ; $ orientationBMinGap = min ( $ orientationBWidthLeft , $ orientationBLengthLeft ) ; if ( $ orientationAMinGap === 0 ) { // prefer A if it leaves no gap return - 1 ; } if ( $ orientationBMinGap === 0 ) { // prefer B if it leaves no gap return 1 ; } // prefer leaving room for next item in current row if ( $ nextItems -> count ( ) ) { $ nextItemFitA = count ( $ this -> getPossibleOrientations ( $ nextItems -> top ( ) , $ a , $ orientationAWidthLeft , $ lengthLeft , $ depthLeft ) ) ; $ nextItemFitB = count ( $ this -> getPossibleOrientations ( $ nextItems -> top ( ) , $ b , $ orientationBWidthLeft , $ lengthLeft , $ depthLeft ) ) ; if ( $ nextItemFitA && ! $ nextItemFitB ) { return - 1 ; } if ( $ nextItemFitB && ! $ nextItemFitA ) { return 1 ; } // if not an easy either/or, do a partial lookahead $ additionalPackedA = $ this -> calculateAdditionalItemsPackedWithThisOrientation ( $ a , $ nextItems , $ widthLeft , $ lengthLeft , $ depthLeft , $ rowLength ) ; $ additionalPackedB = $ this -> calculateAdditionalItemsPackedWithThisOrientation ( $ b , $ nextItems , $ widthLeft , $ lengthLeft , $ depthLeft , $ rowLength ) ; if ( $ additionalPackedA !== $ additionalPackedB ) { return $ additionalPackedB <=> $ additionalPackedA ; } } // otherwise prefer leaving minimum possible gap, or the greatest footprint return $ orientationADepthLeft <=> $ orientationBDepthLeft ? : $ orientationAMinGap <=> $ orientationBMinGap ? : $ a -> getSurfaceFootprint ( ) <=> $ b -> getSurfaceFootprint ( ) ; } ) ; $ bestFit = reset ( $ usableOrientations ) ; $ this -> logger -> debug ( 'Selected best fit orientation' , [ 'orientation' => $ bestFit ] ) ; return $ bestFit ; }
Get the best orientation for an item.

@param Item                $item
@param OrientatedItem|null $prevItem
@param ItemList            $nextItems
@param bool                $isLastItem
@param int                 $widthLeft
@param int                 $lengthLeft
@param int                 $depthLeft
@param int                 $rowLength

@return OrientatedItem|null
Get the best orientation for an item .