更好,或者以不同方式编码类似功能的优点

发布于 2024-12-19 08:31:42 字数 4712 浏览 2 评论 0原文

我正在编写 GUI 代码(用 C++),现在我关心的是文本的行组织。我遇到的问题之一是代码变得非常长且令人困惑,并且我开始进入^2场景,其中对于我为文本演示添加的每个选项,我必须添加的函数数量write 是它的平方。在试图解决这个问题时,出现了一个特定的设计选择,我不知道更好的方法,也不知道它们之间的优点或缺点的程度:

我有两种在流程上非常相似的方法,即迭代通过相同的对象,考虑相同的约束,但最终在此流程之间执行不同的操作。为了满足任何人的兴趣,这些方法渲染文本,并确定是否有任何文本由于文本环绕其他对象或简单地分别位于行尾而溢出行。

这些函数需要针对左、右或居中文本进行复制和重写,这些文本具有不同的流程,因此无论我做出什么设计选择都会重复三次。

基本上,我可以继续我现在所拥有的,这是两种单独的方法来处理这些不同的操作,或者我可以将它们合并到一个函数中,该函数中包含 if 语句来确定是否渲染文本或找出是否存在文字溢出。

有没有普遍接受的正确方法来解决这个问题?否则,需要进行哪些权衡,有哪些迹象可能表明应该使用一种方法而不是另一种方法?还有其他方法可以做我错过的事情吗?

我已经对其进行了几次编辑,试图使其更容易理解,但如果不是,请问我一些问题,以便我可以编辑和解释。我还可以发布两种不同方法的源代码,但它们使用了很多函数和对象,需要很长时间才能解释。

// 编辑:源代码 //

函数 1:

void GUITextLine::renderLeftShifted(const GUIRenderInfo& renderInfo) { if(m_renderLines.empty()) 返回;

Uint iL = 0;

Array2t<浮点>渲染坐标;
renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[0].s_x;
renderCoords.s_y = renderInfo.s_offset.s_y + m_y;
浮动剩余像素InLine = m_renderLines[0].s_y;

for (Uint iTO= 0;iTO != m_text.size();++iTO)
{
    if(m_text[iTO].s_pixelWidth <=剩余像素内行)
    {
        字符串预览 = m_text[iTO].s_string;

        m_text[iTO].render(&renderCoords);
        剩余像素InLine -= m_text[iTO].s_pixelWidth;
    }

    别的
    {
        FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();

        浮点字符宽度=0;

        Uint iFirstCharacterOfRenderLine = 0;

        for(Uint iC = 0;;++iC)
        {
            if(iC == m_text[iTO].s_string.size()) 
            {
                // 包起来
                字符串 renderPart = m_text[iTO].s_string;
                renderPart.erase(iC, renderPart.size());
                renderPart.erase(0, iFirstCharacterOfRenderLine);
                m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
                    &渲染坐标);
                休息;
            }

            字符宽度 += m_text[iTO].s_font->getWidthOfGlyph(intData,
                m_text[iTO].s_string[iC]);

            if(字符宽度>剩余像素行) 
            {
                // 无法压入最后一个字符
                // 该行不再有空格

                // 首先,渲染我们已有的内容:

                字符串 renderPart = m_text[iTO].s_string;
                renderPart.erase(iC, renderPart.size());
                renderPart.erase(0, iFirstCharacterOfRenderLine);
                m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
                    &渲染坐标);


                if(++iL != m_renderLines.size())
                {
                    剩余像素InLine = m_renderLines[iL].s_y;
                    renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[iL].s_x;

                    // 很酷,现在尝试再次渲染这个角色
                     - 我知道了;
                    iFirstCharacterOfRenderLine = iC;
                    字符宽度=0;
                }
                别的
                {

                    // 辞职

                    休息;
                }
            }
        }
    }
}

// 完毕! }

功能2:

向量 GUITextLine::recalculateWrappingContraints_LeftShift() { m_pixelsOfCharacters = 0;

浮动像素剩余 = m_renderLines[0].s_y;

单位 iRL = 0;

// 遍历每个文本对象,将它们放入渲染线中
for(Uint iTO = 0;iTO != m_text.size();++iTO)
{
    // 如果整个文本对象适合一行
    if(剩余像素>= m_text[iTO].s_pixelWidth)
    {
        剩余像素 -= m_text[iTO].s_pixelWidth;
        m_pixelsOfCharacters += m_text[iTO].s_pixelWidth;
    }

    // 否则,逐个字符
    别的
    {
        // 现在获取一些数据,我们并不是每次函数调用都获取它
        FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();

        for(Uint iC = 0; iC != m_text[iTO].s_string.size();++iC)
        {
            浮点字符宽度 = m_text[iTO].s_font->getWidthOfGlyph(intData, '-');

            if(字符宽度 < 剩余像素)
            {
                剩余像素 -= 字符宽度;
                m_pixelsOfCharacters += 字符宽度;
            }

            else // 渲染行结束! 
            {
                m_pixelsOfWrapperCharacters += 剩余像素; // 我们可以跟踪我们使用了多少包装像素

                // 如果这是真的,那么我们在用完文本之前就用完了渲染线。意味着我们有一些溢出要返回
                if(++iRL == m_renderLines.size())
                {
                    返回 HarvesOverflowFrom(iTO, iC);
                }

                别的
                {
                    剩余像素 = m_renderLines[iRL].s_y;
                }
            }
        }

    }
}

矢量空溢出;
返回空溢出; }

所以基本上,render() 将 renderCoordinates 作为参数,并从中获取需要渲染的全局位置。 calcWrappingConstraints 计算出对象中有多少文本超出了分配的空间,并将该文本作为函数返回。

m_renderLines 是两个浮点结构的 std::vector,其中 .s_x = 渲染可以开始的位置,.s_y = 渲染空间有多大 - 不是,它本质上是“renderLine”的宽度,而不是它结束的位置。

m_text 是 GUIText 对象的 std::vector,其中包含文本字符串和一些数据,如样式、颜色、大小等。它还在 s_font 下包含对字体对象的引用,该对象执行渲染、计算字形的宽度等。

希望这能澄清事情。

I'm writing the code for a GUI (in C++), and right now I'm concerned with the organisation of text in lines. One of the problems I'm having is that the code is getting very long and confusing, and I'm starting to get into a n^2 scenario where for every option I add in for the texts presentation, the number of functions I have to write is the square of that. In trying to deal with this, A particular design choice has come up, and I don't know the better method, or the extent of the advantages or disadvantages between them:

I have two methods which are very similar in flow, i.e, iterate through the same objects, taking into account the same constraints, but ultimately perform different operations between this flow. For anyones interest, the methods render the text, and determine if any text overflows the line due to wrapping the text around other objects or simply the end of the line respectively.

These functions need to be copied and rewritten for left, right or centred text, which have different flow, so whatever design choice I make would be repeated three times.

Basically, I could continue what I have now, which is two separate methods to handle these different actions, or I could merge them into one function, which has if statements within it to determine whether or not to render the text or figure out if any text overflows.

Is there a generally accepted right way to going about this? Otherwise, what are the tradeoffs concerned, what are the signs that might indicate one way should be used over the other? Is there some other way of doing things I've missed?

I've edited through this a few times to try and make it more understandable, but if it isn't please ask me some questions so I can edit and explain. I can also post the source code of the two different methods, but they use a lot of functions and objects that would take too long to explain.

// EDIT: Source Code //

Function 1:

void GUITextLine::renderLeftShifted(const GUIRenderInfo& renderInfo) {
if(m_renderLines.empty())
return;

Uint iL = 0;

Array2t<float> renderCoords;
renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[0].s_x;
renderCoords.s_y = renderInfo.s_offset.s_y + m_y;
float remainingPixelsInLine = m_renderLines[0].s_y;

for (Uint iTO= 0;iTO != m_text.size();++iTO)
{
    if(m_text[iTO].s_pixelWidth <= remainingPixelsInLine)
    {
        string preview = m_text[iTO].s_string;

        m_text[iTO].render(&renderCoords);
        remainingPixelsInLine -= m_text[iTO].s_pixelWidth;
    }

    else
    {
        FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();

        float characterWidth = 0;

        Uint iFirstCharacterOfRenderLine = 0;

        for(Uint iC = 0;;++iC)
        {
            if(iC == m_text[iTO].s_string.size()) 
            {
                // wrap up
                string renderPart = m_text[iTO].s_string;
                renderPart.erase(iC, renderPart.size());
                renderPart.erase(0, iFirstCharacterOfRenderLine);
                m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
                    &renderCoords);
                break;
            }

            characterWidth += m_text[iTO].s_font->getWidthOfGlyph(intData,
                m_text[iTO].s_string[iC]);

            if(characterWidth > remainingPixelsInLine) 
            {
                // Can't push in the last character
                // No more space in this line

                // First though, render what we already have:

                string renderPart = m_text[iTO].s_string;
                renderPart.erase(iC, renderPart.size());
                renderPart.erase(0, iFirstCharacterOfRenderLine);
                m_text[iTO].s_font->renderString(renderPart.c_str(), intData,
                    &renderCoords);


                if(++iL != m_renderLines.size())
                {
                    remainingPixelsInLine = m_renderLines[iL].s_y;
                    renderCoords.s_x = renderInfo.s_offset.s_x + m_renderLines[iL].s_x;

                    // Cool, so now try rendering this character again
                    --iC;
                    iFirstCharacterOfRenderLine = iC;
                    characterWidth = 0;
                }
                else
                {

                    // Quit

                    break;
                }
            }
        }
    }
}

// Done! }

Function 2:

vector GUITextLine::recalculateWrappingContraints_LeftShift()
{
m_pixelsOfCharacters = 0;

float pixelsRemaining = m_renderLines[0].s_y;

Uint iRL = 0;

// Go through every text object, fiting them into render lines
for(Uint iTO = 0;iTO != m_text.size();++iTO)
{
    // If an entire text object fits in a single line
    if(pixelsRemaining >= m_text[iTO].s_pixelWidth)
    {
        pixelsRemaining -= m_text[iTO].s_pixelWidth;
        m_pixelsOfCharacters += m_text[iTO].s_pixelWidth;
    }

    // Otherwise, character by character
    else
    {
        // Get some data now we don't get it every function call
        FSInternalGlyphData intData = m_text[iTO].stealFSFastFontInternalData();

        for(Uint iC = 0; iC != m_text[iTO].s_string.size();++iC)
        {
            float characterWidth = m_text[iTO].s_font->getWidthOfGlyph(intData, '-');

            if(characterWidth < pixelsRemaining)
            {
                pixelsRemaining -= characterWidth;
                m_pixelsOfCharacters += characterWidth;
            }

            else // End of render line! 
            {
                m_pixelsOfWrapperCharacters += pixelsRemaining; // we might track how much wrapping px we use

                // If this is true, then we ran out of render lines before we ran out of text. Means we have some overflow to return
                if(++iRL == m_renderLines.size())
                {
                    return harvestOverflowFrom(iTO, iC);
                }

                else
                {
                    pixelsRemaining = m_renderLines[iRL].s_y;
                }
            }
        }

    }
}

vector<GUIText> emptyOverflow;
return emptyOverflow; }

So basically, render() takes renderCoordinates as a parameter and gets from it the global position of where it needs to render from. calcWrappingConstraints figures out how much text in the object goes over the allocated space, and returns that text as a function.

m_renderLines is an std::vector of a two float structure, where .s_x = where rendering can start and .s_y = how large the space for rendering is - not, its essentially width of the 'renderLine', not where it ends.

m_text is an std::vector of GUIText objects, which contain a string of text, and some data, like style, colour, size ect. It also contains under s_font, a reference to a font object, which performs rendering, calculating the width of a glyph, ect.

Hopefully this clears things up.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

少女净妖师 2024-12-26 08:31:42

在这种情况下没有普遍接受的方法。
然而,任何编程场景中的常见做法都是删除重复的代码。
我认为当方向对结果的改变太大而无法进行这种划分时,您会陷入如何按方向划分代码的困境。在这些情况下,重点关注三种算法的共同部分,并将它们划分为任务

当我为 MFC 复制 WinForms 流布局控件时,我做了类似的事情。我处理两种类型的对象:固定位置(您的图片等)和自动位置(您的文字)。

在您提供的示例中,我可以列出示例的常见部分。

Write Line (direction)

  • bool TestPlaceWord (direction) // 如果无法将单词放在前一个单词旁边,则返回 false
  • bool WrapPastObject (direction) // 如果超出行,则返回 false
  • bool WrapLine (direction) // 如果运行则返回 false没有空间容纳新行。

无论您面对什么方向,都会执行这些操作中的每一个。

最终,每个方向的算法差异太大,无法再简化。

There is no generally accepted way in this case.
However, common practice in any programming scenario is to remove duplicated code.
I think you're getting stuck on how to divide code by direction, when direction changes the outcome too much to make this division. In these cases, focus on the common portions of the three algorithms and divide them into tasks.

I did something similar when I duplicated WinForms flow layout control for MFC. I dealt with two types of objects: fixed positional (your pictures etc.) and auto positional (your words).

In the example you provided I can list out common portions of your example.

Write Line (direction)

  • bool TestPlaceWord (direction) // returns false if it cannot place word next to previous word
  • bool WrapPastObject (direction) // returns false if it runs out of line
  • bool WrapLine (direction) // returns false if it runs out of space for new line.

Each of these would be performed no matter what direction you are faced with.

Ultimately, the algorithm for each direction is just too different to simplify anymore than that.

可爱咩 2024-12-26 08:31:42

访客模式的实现怎么样?听起来这可能就是你所追求的东西。

How about an implementation of the Visitor Pattern? It sounds like it might be the kind of thing you are after.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文