C# 旋转及;印刷方格

C# 旋转及;印刷方格,c#,wpf,algorithm,canvas,mvvm,C#,Wpf,Algorithm,Canvas,Mvvm,我有一个关于c#,wpf作业的简短问题。任务是读入一个XML文件,其中包含根面板的一个描述,然后它遵循一个固定模式,其中每个面板都有若干子面板,每个子面板都可以有若干子模式。很明显。我可以很好地阅读它,遍历模型也没有问题 问题是:我必须在wpf画布上打印这些面板。父面板和子面板之间的关系如下所示: 根面板具有X Y坐标以确定其起点。其他小组则没有 每个面板(包括根部)都有宽度和高度(不一定相同) 每个面板(根面板除外)都有一个属性“attachedToSide”,其值介于0到3之间。该值表示应

我有一个关于c#,wpf作业的简短问题。任务是读入一个XML文件,其中包含根面板的一个描述,然后它遵循一个固定模式,其中每个面板都有若干子面板,每个子面板都可以有若干子模式。很明显。我可以很好地阅读它,遍历模型也没有问题

问题是:我必须在wpf画布上打印这些面板。父面板和子面板之间的关系如下所示:

  • 根面板具有X Y坐标以确定其起点。其他小组则没有
  • 每个面板(包括根部)都有宽度和高度(不一定相同)
  • 每个面板(根面板除外)都有一个属性“attachedToSide”,其值介于0到3之间。该值表示应将子对象放置在父对象的一侧
  • 在针对父面板打印面板时,我们应始终将面板的“0”侧与父面板侧相对
因此,为了证明这一点,请看下面的草稿。根面板已打印。根有4个子元素。将面板移到根的右侧。该面板将有一个属性attachedToSide='1',表示它应该贴在其父面板的一侧。既然规则是0边应该粘在父边上,我们必须将其翻转90°。 故事就这样继续着

现在,打印本身没有问题。我在努力计算每个方块的实际位置。父级的第一个子级很容易,但从那时起,我必须根据前面的面板进行一些计算以正确定位它们,我不想采用嵌套if语句的方式。可能存在一个非常简单的算法来解决这个问题,但由于我不在这个领域,我有点挣扎。谁能给我一个正确的方向


细节:做这一切也纯粹是为了mvvm(只是为了见鬼),所以代码背后有0个代码。这些形状是一个带有自定义itemspaneltemplate和itemtemplate的itemcollection,我通过将旋转角度绑定到模型中的属性来进行旋转。

每个面板的模型包括

X,Y coordinates W,H dimensions R rotation value (one of four choices) C a list of up to four children A attached to side 计算子对象的Y值 请注意,子对象的位置主要取决于子对象的旋转值。如果childR为0,则子项位于父项之上。如果childR为1,则该孩子向右,以此类推

因此,如果childR是奇数,则子项与父项具有相同的Y值。如果childR为0,则childY是由孩子的身高调整的父系。当childR为2时,childY是由父项宽度(parentR奇数)或父项高度(parentR偶数)调整的父项

这将导致一个if-else链,如下所示:

if ( childR % 2 )                // odd values, child left or right
    childY = parentY
else if ( childR == 0 )          // child above
    childY = parentY - childH
else if ( parentR % 2 )          // odd values, adjust by parent width
    childY = parentY + parentW
else                             // even values, adjust by parent height
    childY = parentY + parentH   
(我在这里假设X,Y坐标表示面板左上角的位置,正Y是向下的。)


X计算与Y计算类似

从根开始,计算根的子元素的X,Y,R,然后递归地计算每个子元素的子元素的参数


这就完成了您的模型。在视图上显示面板非常简单,因为每个面板都有X、Y、W、H、R。

您可以使用递归函数打印面板的所有子级,并将所述面板作为参数传递,以便轻松访问位置、变换等。。。类似于:

public void PrintSelfAndChildren(Panel parent)
{
    ApplyTransform();
    PrintPanel();
    foreach(var child in parent.children)
    {
        PrintSelfAndChildren(child);
    }
}

user3386109的回答把我推向了正确的方向,但我得到了一些关于这个问题的额外信息,帮助我解决了这个问题。看看这个例子:

父级打印时0面朝下(这是标准)。它有3个子项:右、上、左。现在,父面板是唯一接收X,Y坐标的面板。(X,Y)是0边的中心。此外,我还得到了宽度和高度。对于以后的所有孩子,我会得到他们的宽度、高度和父母的侧面。因为孩子应该总是用自己的0边连接到父母,所以我可以很容易地使用mod wrapping formule user3386109计算孩子的底面,已经显示了: 底端子节点=(底端父节点+6-父节点附件端)%4

这是最简单的部分。现在,一个复杂的情况是每个孩子都可能比父母宽或小,比父母高或低。这可能会使计算需要绘制的左上角(X,Y)点的问题变得复杂。然而,我一直知道的一件事是,孩子所依附的父侧的中心点应该与接触该父侧的子侧中心点相同(请参见图片上的红线,这将告诉您我的意思)

现在,我使用了以下方法:我决定计算左上角点的坐标,假设我可以“竖直”绘制子对象,因此底部是0边。然后,我会沿着那个点旋转

举个例子:

请注意黑色的父面板。我从XML中知道,我需要将子面板附加到父面板的第1面。因此,我从父1侧的0侧中心计算其中心点。我知道这将是孩子们0边的中心,因为那是我需要将他们连接在一起的地方。然后我计算childs的左上(X,Y)坐标,这很简单。之后,我可以沿着子对象的中心0边点旋转子对象。然后我们得到以下结果,其中父对象和子对象在中心连接,子对象也以正确的方式旋转

简言之,始终采用相同的方法:

  • 取父对象0边的中心(我们将存储在每个面板对象中)
  • 相对于该点,计算子对象0侧中心的位置
  • 如果我们有那一点,计算孩子们的身高
    public void PrintSelfAndChildren(Panel parent)
    {
        ApplyTransform();
        PrintPanel();
        foreach(var child in parent.children)
        {
            PrintSelfAndChildren(child);
        }
    }
    
        private static Rectangle CreateRectangle(string name, float width, float height, int sideOfParent, float offset, Rectangle parent)
        {
            Rectangle rect = new Rectangle() { Name = name, Width = width, Height = height, Offset = offset };
            // Calculate which side should be at the bottom, depending on the bottom side of the parent, 
            // and which side of the parent the new rectangle should be attached to
            rect.BottomSide = (parent.BottomSide + 6 - sideOfParent) % 4;
    
            // Calculate the bottom mid point of the rectangle
            // If | bottom side parent - bottom side child | = 2, just take over the mid bottom point of the parent
            if (Math.Abs(parent.BottomSide - rect.BottomSide) == 2) { rect.MidBottom = parent.MidBottom; }
            else
            {
                // Alternative cases
                // Formulas for both bottom side parent = 0 or 2 are very similar per bottom side child variation (only plus/minus changes for Y formulas)
                // Formulas for both bottom side parent = 1 or 3 are vary similar per bottom side child variation (only plus/minus changes for X formulas)
                // Therefor, we create a "mutator" 1 / -1 if needed, to multiply one part of the formula with, so that we either add or subtract
                Point parPoint = parent.MidBottom;
                if (parent.BottomSide % 2 == 0)
                {
                    // Parent has 0 or 2 at the bottom
                    int mutator = (parent.BottomSide == 0) ? 1 : -1;
                    switch (rect.BottomSide % 2 == 0)
                    {
                        case true: rect.MidBottom = new Point(parPoint.X, parPoint.Y - (mutator * parent.Height)); break;
                        case false:
                            if (rect.BottomSide == 1) rect.MidBottom = new Point(parPoint.X + (parent.Width / 2), parPoint.Y - (mutator * (parent.Height / 2)));
                            else rect.MidBottom = new Point(parPoint.X - (parent.Width / 2), parPoint.Y - (mutator * (parent.Height / 2)));
                            break;
                    }
                }
                else
                {
                    // Parent has 1 or 3 at the bottom
                    int mutator = (parent.BottomSide == 1) ? 1 : -1;
                    switch (rect.BottomSide % 2 == 1)
                    {
                        case true: rect.MidBottom = new Point(parPoint.X + (mutator * parent.Height), parPoint.Y); break;
                        case false:
                            if (rect.BottomSide == 0) rect.MidBottom = new Point(parPoint.X + (mutator * (parent.Height / 2)), parPoint.Y - (parent.Width / 2));
                            else rect.MidBottom = new Point(parPoint.X + (mutator * (parent.Height / 2)), parPoint.Y + (parent.Width / 2));
                            break;
                    }
                }
            }
    
            return rect;
        }