user3386109 的回答把我推向了正确的方向,但我得到了一些关于帮助我解决这个问题的额外信息。看看这个例子:
父项以 0 面朝下的方式打印(这是标准的)。它有 3 个孩子:右、上、左。现在,父面板是我收到 X、Y 坐标的唯一面板。 (X,Y) 是 0 边的中心。另外我得到宽度和高度。对于以后的所有孩子,我会得到它所在的父母的宽度、高度和侧面。由于孩子应该始终通过自己的 0 侧连接到其父级,因此我可以使用已经显示的 mod 包装公式 user3386109 很容易地计算孩子的底侧:
底部孩子 =(底部父母 + 6 - 父母附件侧)% 4
这是最简单的部分。现在,一个复杂的问题是每个孩子可以比父母更宽或更小,比父母更高或更低。这可能会使计算我们需要绘制的左上角 (X,Y) 点变得复杂。然而,我一直都知道的一件事是,孩子所附着的父母侧的中心点应该与接触该父母的孩子侧中心的点相同(参见图片上的红线,这将告诉你我的意思)。
现在我使用了以下方法:我决定计算左上角的坐标,假设我可以将孩子画成“直立”,所以底部是 0 边。然后,我会沿着那个点旋转。
举个例子:
注意黑色的父面板。我从 XML 中知道我需要将子面板附加到父面板的第 1 侧。因此,我从自己的 0 侧中心计算父母 1 侧的中心点。我知道这将是孩子 0 面的中心,因为那是我需要将它们连接在一起的地方。然后我计算孩子的左上角(X,Y)坐标,这很简单。之后,我可以沿着它的中心 0 侧点旋转孩子。然后我们得到以下结果,其中父子节点在中心连接,并且子节点也以正确的方式旋转。
简而言之,方法总是一样的:
- 取父节点 0 边的中心(我们将存储在每个面板对象中)
- 相对于该点,计算孩子的 0 边中心将在哪里
- 如果我们有那个点,计算孩子的左上角,这样我们就知道从哪里开始绘制
- 沿其 0 侧中心点旋转子级(我们知道从底部一侧的旋转度数)
完成。一个额外的复杂因素是每个孩子都收到了一定的“抵消”价值。简而言之,这是一个正值或负值,表示将孩子推向某个方向(仍然依附于父母)。只需调整正确的坐标即可轻松解决此问题。
现在,要计算所有点,很明显这一切都取决于父旋转、自身旋转等。在检查变化时,我得出的结论是,很多公式看起来都非常相似。总的解释需要大量的打字,坦率地说,我不会被打扰。但是:这里的代码根据给定的父矩形、子宽度、它应该在父矩形的哪一侧以及偏移量来创建子矩形。
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;
}
所有这些的真实生活结果示例:
正如我已经提到的,实际的绘制只是通过将 ItemCollection 放在标准网格上、绑定到矩形集合并在那里设置适当的 ItemsPanel 和 ItemTemplate、标准 WPF 来进行。