inline static HRESULT FindChartPosition()

in UVAtlas/isochart/packingcharts.cpp [1398:1647]


    inline static HRESULT FindChartPosition(
        PackingDirection direction,
        ATLASINFO& atlasInfo,
        PACKINGINFO* pPackingInfo,
        size_t dwRotationID,
        XMFLOAT2& resultOrg,
        float& fBetweenArea,
        float& fAreaLost)
    {
        VERTEX_ARRAY* pAtlasBorder = nullptr;
        VERTEX_ARRAY* pChartBorder = nullptr;

        Axis TangentAxis = YAxis;
        Axis RadialAxis = XAxis;

        // The radial coordinate of the border vertex in atlas, which is nearest to the new chart
        float fAtlasNearChartExtreme = 0;
        // The radial coordinate of the border vertex in atlas, which is farest to the new chart
        float fAtlasAwayChartExtreme = 0;
        // The largest tangent coordinate of the border vertex in atlas.
        float fAtlasTangentMaxExtreme = 0;
        // The smallest tangent coordinate of the border vertex in atlas.
        float fAtlasTangentMinExtreme = 0;

        float fChartTangentSize = 0;
        float fChartRadialSize = 0;
        bool bPackingFromLowerPlace = false;

        VertexLocation invalidChartLocationAgainstAtlas;
        VertexLocation invalidatlasLocationAgainstChart;

        switch (direction)
        {
        case FromRight:
            pAtlasBorder = &(atlasInfo.currentRightBorder);
            pChartBorder = &(pPackingInfo->leftBorder[dwRotationID]);
            fAtlasNearChartExtreme = atlasInfo.fBoxRight;
            fAtlasAwayChartExtreme = atlasInfo.fBoxLeft;
            fAtlasTangentMaxExtreme = atlasInfo.fBoxTop;
            fAtlasTangentMinExtreme = atlasInfo.fBoxBottom;
            fChartTangentSize = pPackingInfo->fUVHeight[dwRotationID];
            fChartRadialSize = pPackingInfo->fUVWidth[dwRotationID];
            invalidChartLocationAgainstAtlas = LeftToBorder;
            invalidatlasLocationAgainstChart = RightToBorder;
            break;

        case FromLeft:
            pAtlasBorder = &(atlasInfo.currentLeftBorder);
            pChartBorder = &(pPackingInfo->rightBorder[dwRotationID]);
            fAtlasNearChartExtreme = atlasInfo.fBoxLeft;
            fAtlasAwayChartExtreme = atlasInfo.fBoxRight;
            fAtlasTangentMaxExtreme = atlasInfo.fBoxTop;
            fAtlasTangentMinExtreme = atlasInfo.fBoxBottom;
            fChartTangentSize = pPackingInfo->fUVHeight[dwRotationID];
            fChartRadialSize = pPackingInfo->fUVWidth[dwRotationID];
            bPackingFromLowerPlace = true;
            invalidChartLocationAgainstAtlas = RightToBorder;
            invalidatlasLocationAgainstChart = LeftToBorder;
            break;

        case FromTop:
            pAtlasBorder = &(atlasInfo.currentTopBorder);
            pChartBorder = &(pPackingInfo->bottomBorder[dwRotationID]);
            TangentAxis = XAxis; // x field
            RadialAxis = YAxis;
            fAtlasNearChartExtreme = atlasInfo.fBoxTop;
            fAtlasAwayChartExtreme = atlasInfo.fBoxBottom;
            fAtlasTangentMaxExtreme = atlasInfo.fBoxRight;
            fAtlasTangentMinExtreme = atlasInfo.fBoxLeft;
            fChartTangentSize = pPackingInfo->fUVWidth[dwRotationID];
            fChartRadialSize = pPackingInfo->fUVHeight[dwRotationID];
            invalidChartLocationAgainstAtlas = BelowBorder;
            invalidatlasLocationAgainstChart = AboveBorder;
            break;

        case FromBottom:
            pAtlasBorder = &(atlasInfo.currentBottomBorder);
            pChartBorder = &(pPackingInfo->topBorder[dwRotationID]);
            TangentAxis = XAxis; // x field
            RadialAxis = YAxis;
            fAtlasNearChartExtreme = atlasInfo.fBoxBottom;
            fAtlasAwayChartExtreme = atlasInfo.fBoxTop;
            fAtlasTangentMaxExtreme = atlasInfo.fBoxRight;
            fAtlasTangentMinExtreme = atlasInfo.fBoxLeft;
            fChartTangentSize = pPackingInfo->fUVWidth[dwRotationID];
            fChartRadialSize = pPackingInfo->fUVHeight[dwRotationID];
            bPackingFromLowerPlace = true;
            invalidChartLocationAgainstAtlas = AboveBorder;
            invalidatlasLocationAgainstChart = BelowBorder;
            break;

        default:
            assert(false);
            return E_FAIL;
        }

        VERTEX_ARRAY& atlasBorder = *pAtlasBorder;
        VERTEX_ARRAY& chartBorder = *pChartBorder;

        XMFLOAT2* pOrigUV = pPackingInfo->pStandardUV;
        // Border is composite with virtual vertices
        if (chartBorder[0]->dwIDInRootMesh == INVALID_VERT_ID)
        {
            pOrigUV = pPackingInfo->pStandardVirtualCorner;
        }

        float fMinAreaLost = FLT_MAX;
        float fMiniBetweenArea = FLT_MAX;
        fAreaLost = FLT_MAX;

        float fMinTangentPosition = VECTOR_ITEM(&atlasBorder[0]->uv, TangentAxis);

        // Decide the searching step length
        float fTangentRange =
            VECTOR_ITEM(&atlasBorder[atlasBorder.size() - 1]->uv, TangentAxis) -
            VECTOR_ITEM(&atlasBorder[0]->uv, TangentAxis) - fChartTangentSize;

        size_t dwTangentLenInPixel;
        if (fTangentRange < 0)
        {
            dwTangentLenInPixel = 1;
        }
        else
        {
            dwTangentLenInPixel =
                static_cast<size_t>(fTangentRange / atlasInfo.fPixelLength) + 1;
        }

        size_t dwStepLength = GetSearchStepLength(dwTangentLenInPixel);
        ISOCHARTVERTEX startExtraVertex, endExtraVertex;

        // To guarantee enough gutter between different charts, add 2 extra vertex
        // at each end of current chart border.
        VERTEX_ARRAY newChartBorder;

        try
        {
            newChartBorder.push_back(&startExtraVertex);
            newChartBorder.insert(newChartBorder.end(), chartBorder.cbegin(), chartBorder.cend());
            newChartBorder.push_back(&endExtraVertex);
        }
        catch (std::bad_alloc&)
        {
            return E_OUTOFMEMORY;
        }

        // Put current chart far away atlas, by fRadialDelta offset
        float fRadialDelta;
        if (bPackingFromLowerPlace)
        {
            fRadialDelta =
                fAtlasNearChartExtreme - fChartRadialSize - 100 * atlasInfo.fGutter;
        }
        else
        {
            fRadialDelta =
                fAtlasNearChartExtreme + fChartRadialSize + 100 * atlasInfo.fGutter;
        }

        // Search the optimal packing position
        for (size_t i = 0; i < dwTangentLenInPixel; i += dwStepLength)
        {
            fBetweenArea = 0;

            // Searching from center to both sides
            float fTangentDelta;
            if (dwTangentLenInPixel > 1)
            {
                fTangentDelta =
                    fMinTangentPosition + float((i + dwTangentLenInPixel / 2) % dwTangentLenInPixel)
                    * fTangentRange / float(dwTangentLenInPixel - 1);
            }
            else
            {
                fTangentDelta = fMinTangentPosition;
            }

            // Move chart to new position
            MoveChartToNewPosition(
                newChartBorder,
                pOrigUV,
                TangentAxis,
                RadialAxis,
                fTangentDelta,
                fRadialDelta,
                atlasInfo.fGutter);

            // Find correspond segments on atlas border and chart border
            size_t atlasBorderStart, atlasBorderEnd;
            size_t newChartBorderStart, newChartBorderEnd;
            if (!FindCorrespondSegmentsOfBorders(
                atlasBorder,
                newChartBorder,
                atlasBorderStart,
                atlasBorderEnd,
                newChartBorderStart,
                newChartBorderEnd,
                TangentAxis))
            {
                continue;
            }

            // Calculate the minimal distance between chart and atlas
            float fMinDistance = FLT_MAX;
            if (!CalMinDistanceBetweenAtlasAndChart(
                invalidatlasLocationAgainstChart,
                invalidChartLocationAgainstAtlas,
                bPackingFromLowerPlace,
                newChartBorder,
                newChartBorderStart,
                newChartBorderEnd,
                atlasBorder,
                atlasBorderStart,
                atlasBorderEnd,
                TangentAxis,
                RadialAxis,
                atlasInfo.fGutter,
                fMinDistance,
                fBetweenArea))
            {
                continue;
            }

            // Move chart from far away position to the real packing position. Check if current
            // position is better than old ones
            UpdateOptimalPosition(
                bPackingFromLowerPlace,
                atlasInfo,
                atlasBorder,
                fAtlasNearChartExtreme,
                fAtlasAwayChartExtreme,
                fAtlasTangentMaxExtreme,
                fAtlasTangentMinExtreme,
                TangentAxis,
                RadialAxis,
                fChartTangentSize,
                fChartRadialSize,
                fTangentDelta,
                fRadialDelta,
                fMinDistance,
                fBetweenArea,
                resultOrg,
                fMinAreaLost,
                fMiniBetweenArea);
        }

        fBetweenArea = fMiniBetweenArea;
        fAreaLost = fMinAreaLost;
        return S_OK;
    }