More layout editor features and fixes.

Add option to create null panes.
Fix pane lookup table to properly adjust when a pane is renamed.
Adjust selection transform for rotation.
Some undo fixes for removed panes.
This commit is contained in:
KillzXGaming 2019-10-05 21:15:19 -04:00
parent 00ba993fa1
commit 99d0986e87
7 changed files with 210 additions and 141 deletions

View file

@ -940,49 +940,56 @@ namespace LayoutBXLYT
if (isSelected)
{
if (!Runtime.LayoutEditor.IsGamePreview && !gameWindow)
{
var rect = pane.CreateRectangle();
rect = rect.RotateZ(pane.Rotate.Z);
GL.Disable(EnableCap.Blend);
GL.Disable(EnableCap.AlphaTest);
GL.Disable(EnableCap.Texture2D);
GL.UseProgram(0);
GL.Begin(PrimitiveType.LineLoop);
GL.Color4(Color.Red);
GL.Vertex2(rect.LeftPoint, rect.BottomPoint);
GL.Vertex2(rect.RightPoint, rect.BottomPoint);
GL.Vertex2(rect.RightPoint, rect.TopPoint);
GL.Vertex2(rect.LeftPoint, rect.TopPoint);
GL.End();
var transformed = rect;
var leftTop = new Vector2(transformed.LeftPoint, transformed.TopPoint);
var left = new Vector2(transformed.LeftPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var leftBottom = new Vector2(transformed.LeftPoint, transformed.BottomPoint);
var rightTop = new Vector2(transformed.RightPoint, transformed.TopPoint);
var right = new Vector2(transformed.RightPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var rightBottom = new Vector2(transformed.RightPoint, transformed.BottomPoint);
var top = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.TopPoint);
var bottom = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.BottomPoint);
DrawEdgeSquare(leftTop);
DrawEdgeSquare(left);
DrawEdgeSquare(leftBottom);
DrawEdgeSquare(rightTop);
DrawEdgeSquare(right);
DrawEdgeSquare(rightBottom);
DrawEdgeSquare(top);
DrawEdgeSquare(bottom);
GL.Enable(EnableCap.Blend);
GL.Enable(EnableCap.AlphaTest);
GL.Enable(EnableCap.Texture2D);
}
DrawSelectionBox(pane, isSelected);
}
}
public static void DrawSelectionBox(BasePane pane, bool isSelected)
{
var rect = pane.CreateRectangle();
rect = rect.RotateZ(pane.Rotate.Z);
GL.Disable(EnableCap.Blend);
GL.Disable(EnableCap.AlphaTest);
GL.Disable(EnableCap.Texture2D);
GL.UseProgram(0);
GL.Begin(PrimitiveType.LineLoop);
GL.Color4(isSelected ? Color.Red : Color.Green);
GL.Vertex2(rect.LeftPoint, rect.BottomPoint);
GL.Vertex2(rect.RightPoint, rect.BottomPoint);
GL.Vertex2(rect.RightPoint, rect.TopPoint);
GL.Vertex2(rect.LeftPoint, rect.TopPoint);
GL.End();
if (isSelected)
{
var transformed = rect;
var leftTop = new Vector2(transformed.LeftPoint, transformed.TopPoint);
var left = new Vector2(transformed.LeftPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var leftBottom = new Vector2(transformed.LeftPoint, transformed.BottomPoint);
var rightTop = new Vector2(transformed.RightPoint, transformed.TopPoint);
var right = new Vector2(transformed.RightPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var rightBottom = new Vector2(transformed.RightPoint, transformed.BottomPoint);
var top = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.TopPoint);
var bottom = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.BottomPoint);
DrawEdgeSquare(leftTop);
DrawEdgeSquare(left);
DrawEdgeSquare(leftBottom);
DrawEdgeSquare(rightTop);
DrawEdgeSquare(right);
DrawEdgeSquare(rightBottom);
DrawEdgeSquare(top);
DrawEdgeSquare(bottom);
}
GL.Enable(EnableCap.Blend);
GL.Enable(EnableCap.AlphaTest);
GL.Enable(EnableCap.Texture2D);
}
private static void GetTextureSize(BxlytMaterial pane, Dictionary<string, STGenericTexture> Textures, out float width, out float height)
{
width = 0;
@ -1340,49 +1347,11 @@ namespace LayoutBXLYT
GL.MultiTexCoord2(TextureUnit.Texture0, texCoords[3].X, texCoords[3].Y);
GL.Vertex2(rect.LeftPoint, rect.TopPoint);
GL.End();
}
if (!Runtime.LayoutEditor.IsGamePreview && !gameWindow)
{
GL.Disable(EnableCap.Blend);
GL.Disable(EnableCap.AlphaTest);
GL.Disable(EnableCap.Texture2D);
GL.UseProgram(0);
GL.Begin(PrimitiveType.LineLoop);
GL.Color4(selectionOutline ? Color.Red : Color.Green);
GL.Vertex2(rect.LeftPoint, rect.BottomPoint);
GL.Vertex2(rect.RightPoint, rect.BottomPoint);
GL.Vertex2(rect.RightPoint, rect.TopPoint);
GL.Vertex2(rect.LeftPoint, rect.TopPoint);
GL.End();
if (selectionOutline)
{
var transformed = rect;
var leftTop = new Vector2(transformed.LeftPoint, transformed.TopPoint);
var left = new Vector2(transformed.LeftPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var leftBottom = new Vector2(transformed.LeftPoint, transformed.BottomPoint);
var rightTop = new Vector2(transformed.RightPoint, transformed.TopPoint);
var right = new Vector2(transformed.RightPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var rightBottom = new Vector2(transformed.RightPoint, transformed.BottomPoint);
var top = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.TopPoint);
var bottom = new Vector2((transformed.RightPoint + transformed.LeftPoint) / 2, transformed.BottomPoint);
DrawEdgeSquare(leftTop);
DrawEdgeSquare(left);
DrawEdgeSquare(leftBottom);
DrawEdgeSquare(rightTop);
DrawEdgeSquare(right);
DrawEdgeSquare(rightBottom);
DrawEdgeSquare(top);
DrawEdgeSquare(bottom);
}
GL.Enable(EnableCap.Blend);
GL.Enable(EnableCap.AlphaTest);
GL.Enable(EnableCap.Texture2D);
}
if (!Runtime.LayoutEditor.IsGamePreview && !gameWindow)
{
DrawSelectionBox(pane, selectionOutline);
}
}
else

View file

@ -419,11 +419,10 @@ namespace LayoutBXLYT.Cafe
MaterialList = new MAT1(reader, this);
break;
case "pan1":
var panel = new PAN1(reader);
var panel = new PAN1(reader, this);
AddPaneToTable(panel);
if (!setRoot)
{
panel.IsRoot = true;
RootPane = panel;
setRoot = true;
}
@ -799,7 +798,7 @@ namespace LayoutBXLYT.Cafe
private byte _flags;
public TXT1(FileReader reader, Header header) : base(reader)
public TXT1(FileReader reader, Header header) : base(reader, header)
{
long startPos = reader.Position - 84;
@ -1010,7 +1009,7 @@ namespace LayoutBXLYT.Cafe
get { return WindowFrames.Count >= 8 ? WindowFrames[7] : null; }
}
public WND1(FileReader reader, Header header) : base(reader)
public WND1(FileReader reader, Header header) : base(reader, header)
{
WindowFrames = new List<BxlytWindowFrame>();
@ -1157,7 +1156,7 @@ namespace LayoutBXLYT.Cafe
}
public ALI1(FileReader reader, Header header) : base(reader)
public ALI1(FileReader reader, Header header) : base(reader, header)
{
}
@ -1177,7 +1176,7 @@ namespace LayoutBXLYT.Cafe
}
public SCR1(FileReader reader, Header header) : base(reader)
public SCR1(FileReader reader, Header header) : base(reader, header)
{
}
@ -1197,7 +1196,7 @@ namespace LayoutBXLYT.Cafe
}
public BND1(FileReader reader, Header header) : base(reader)
public BND1(FileReader reader, Header header) : base(reader, header)
{
}
@ -1285,7 +1284,7 @@ namespace LayoutBXLYT.Cafe
}
}
public class PRT1 : PAN1, IPartPane
public class PRT1 : PAN1,IPartPane
{
public override string Signature { get; } = "prt1";
@ -1441,7 +1440,7 @@ namespace LayoutBXLYT.Cafe
}
private Header layoutFile;
public PRT1(FileReader reader, Header header) : base(reader)
public PRT1(FileReader reader, Header header) : base(reader, header)
{
layoutFile = header;
@ -1741,7 +1740,7 @@ namespace LayoutBXLYT.Cafe
TexCoords[0] = new TexCoord();
}
public PIC1(FileReader reader, BFLYT.Header header) : base(reader)
public PIC1(FileReader reader, BFLYT.Header header) : base(reader, header)
{
ParentLayout = header;
@ -1872,18 +1871,28 @@ namespace LayoutBXLYT.Cafe
[DisplayName("User Data"), CategoryAttribute("User Data")]
public UserData UserData { get; set; }
public PAN1() : base()
public PAN1() : base() {
LoadDefaults();
}
public PAN1(Header header, string name) : base() {
LoadDefaults();
List<string> names = header.PaneLookup.Values.ToList().Select(o => o.Name).ToList();
Name = Utils.RenameDuplicateString(names, name);
}
public void LoadDefaults()
{
Alpha = 255;
PaneMagFlags = 0;
Name = "";
Translate = new Vector3F(0,0,0);
Rotate = new Vector3F(0,0,0);
Scale = new Vector2F(1,1);
Width = 0;
Height = 0;
Translate = new Vector3F(0, 0, 0);
Rotate = new Vector3F(0, 0, 0);
Scale = new Vector2F(1, 1);
Width = 30;
Height = 40;
UserDataInfo = "";
UserData = new UserData();
UserData = null;
originX = OriginX.Center;
originY = OriginY.Center;
@ -1893,8 +1902,9 @@ namespace LayoutBXLYT.Cafe
Visible = true;
}
public PAN1(FileReader reader) : base()
public PAN1(FileReader reader, BxlytHeader header) : base()
{
LayoutFile = header;
_flags1 = reader.ReadByte();
origin = reader.ReadByte();
Alpha = reader.ReadByte();

View file

@ -14,6 +14,9 @@ namespace LayoutBXLYT
{
public class BasePane : SectionCommon
{
[Browsable(false)]
public BxlytHeader LayoutFile;
[Browsable(false)]
public PaneAnimController animController = new PaneAnimController();
@ -22,9 +25,14 @@ namespace LayoutBXLYT
[DisplayName("Is Visible"), CategoryAttribute("Flags")]
public virtual bool Visible { get; set; }
public bool IsRoot = false;
[Browsable(false)]
public bool IsRoot
{
get { return Parent == null; }
}
[Browsable(false)]
public bool ParentIsRoot
{
get { return Parent != null && Parent.IsRoot; }
@ -41,8 +49,23 @@ namespace LayoutBXLYT
[Browsable(false)]
public virtual bool DisplayInEditor { get; set; } = true;
private string name;
[DisplayName("Name"), CategoryAttribute("Pane")]
public string Name { get; set; }
public string Name
{
get { return name; }
set
{
if (name != null && LayoutFile != null && LayoutFile.PaneLookup.ContainsKey(name))
LayoutFile.PaneLookup.Remove(name);
name = value;
if (LayoutFile != null)
LayoutFile.PaneLookup.Add(name, this);
}
}
[DisplayName("Translate"), CategoryAttribute("Pane")]
public Vector3F Translate { get; set; }
@ -112,7 +135,7 @@ namespace LayoutBXLYT
public CustomRectangle TransformParent(CustomRectangle rect)
{
return rect.GetTransformedRectangle(Parent, Translate, Scale);
return rect.GetTransformedRectangle(Parent, Translate,Rotate, Scale);
}
private CustomRectangle rectangle;
@ -311,10 +334,15 @@ namespace LayoutBXLYT
public bool IsHit(int X, int Y)
{
var rect = CreateRectangle();
var transformed = rect.GetTransformedRectangle(Parent, Translate, Scale);
var transformed = rect.GetTransformedRectangle(Parent, Translate, Rotate, Scale);
if ((X > transformed.LeftPoint) && (X < transformed.RightPoint) &&
(Y > transformed.BottomPoint) && (Y < transformed.TopPoint))
bool isInBetweenX = (X > transformed.LeftPoint) && (X < transformed.RightPoint) ||
(X < transformed.LeftPoint) && (X > transformed.RightPoint);
bool isInBetweenY = (Y > transformed.BottomPoint) && (Y < transformed.TopPoint) ||
(Y < transformed.BottomPoint) && (Y > transformed.TopPoint);
if (isInBetweenX && isInBetweenY)
return true;
else
return false;
@ -1265,14 +1293,20 @@ namespace LayoutBXLYT
PaneLookup.Add(pane.Name, pane);
}
public void AddPane(BasePane pane, string parent)
{
if (PaneLookup.ContainsKey(parent))
AddPane(pane, PaneLookup[parent]);
else
throw new Exception($"Failed to find parent pane! [{parent}]");
}
public void AddPane(BasePane pane, BasePane parent)
{
if (parent == null) return;
if (!PaneLookup.ContainsKey(pane.Name))
PaneLookup.Add(pane.Name, pane);
pane.Parent = parent;
parent.Childern.Add(pane);
parent.NodeWrapper.Nodes.Add(pane.NodeWrapper);
}
@ -1284,9 +1318,6 @@ namespace LayoutBXLYT
if (PaneLookup.ContainsKey(panes[i].Name))
PaneLookup.Remove(panes[i].Name);
if (panes[i].Parent == null)
continue;
var parent = panes[i].Parent;
parent.Childern.Remove(panes[i]);
@ -1386,20 +1417,39 @@ namespace LayoutBXLYT
public CustomRectangle RotateZ(float rotate)
{
var topLeft = RotateZPoint(LeftPoint, TopPoint, rotate);
var bottomRight = RotateZPoint(RightPoint, BottomPoint, rotate);
return new CustomRectangle(
LeftPoint,
RightPoint,
TopPoint,
BottomPoint);
(int)topLeft.X,
(int)bottomRight.X,
(int)topLeft.Y,
(int)bottomRight.Y);
}
public CustomRectangle GetTransformedRectangle(BasePane parent, Vector3F Transform, Vector2F Scale)
private OpenTK.Vector2 RotateZPoint(float p1, float p2, float rotate)
{
var rect = new CustomRectangle(
(int)(LeftPoint + Transform.X * Scale.X),
(int)(RightPoint + Transform.X * Scale.X),
(int)(TopPoint + Transform.Y * Scale.Y),
(int)(BottomPoint + Transform.Y * Scale.Y));
double angleInRadians = rotate * (Math.PI / 180);
double cosTheta = Math.Cos(angleInRadians);
double sinTheta = Math.Sin(angleInRadians);
var centerPoint = new OpenTK.Vector2(0,0);
return new OpenTK.Vector2(
(int)
(cosTheta * (p1 - centerPoint.X) -
sinTheta * (p2 - centerPoint.Y) + centerPoint.X),
(int)
(sinTheta * (p1 - centerPoint.X) +
cosTheta * (p2 - centerPoint.Y) + centerPoint.Y));
}
public CustomRectangle GetTransformedRectangle(BasePane parent, Vector3F Transform, Vector3F Rotate, Vector2F Scale)
{
var rect = this.RotateZ(Rotate.Z);
rect = new CustomRectangle(
(int)(rect.LeftPoint + Transform.X * Scale.X),
(int)(rect.RightPoint + Transform.X * Scale.X),
(int)(rect.TopPoint + Transform.Y * Scale.Y),
(int)(rect.BottomPoint + Transform.Y * Scale.Y));
if (parent != null)
return parent.TransformParent(rect);

View file

@ -903,5 +903,24 @@ namespace LayoutBXLYT
Runtime.LayoutEditor.PartsAsNullPanes = viewPartsAsNullPanesToolStripMenuItem.Checked;
ActiveViewport?.UpdateViewport();
}
#region Pane Adding
public BasePane AddNewNullPane()
{
BasePane pane = null;
if (ActiveLayout is BFLYT.Header)
pane = new BFLYT.PAN1((BFLYT.Header)ActiveLayout, "N_null");
if (pane != null)
{
pane.NodeWrapper = LayoutHierarchy.CreatePaneWrapper(pane);
ActiveLayout.AddPane(pane, ActiveLayout.RootPane);
}
return pane;
}
#endregion
}
}

View file

@ -227,7 +227,7 @@ namespace LayoutBXLYT
GetPanes(childPane,ref panes);
}
private PaneTreeWrapper CreatePaneWrapper(BasePane pane)
public static PaneTreeWrapper CreatePaneWrapper(BasePane pane)
{
PaneTreeWrapper paneNode = new PaneTreeWrapper();
paneNode.Text = pane.Name;

View file

@ -67,7 +67,7 @@ namespace LayoutBXLYT
{
layoutFile = header;
for (int i = 0; i < panes.Count; i++)
targetPanes.Add(new PaneInfo(panes[i]));
targetPanes.Add(new PaneInfo(panes[i], header.PaneLookup[panes[i].Parent.Name]));
}
public IRevertAction Revert()
@ -82,10 +82,10 @@ namespace LayoutBXLYT
public BasePane Parent;
public BasePane Pane;
public PaneInfo(BasePane pane)
public PaneInfo(BasePane pane, BasePane parent)
{
Pane = pane;
Parent = pane.Parent;
Parent = parent;
}
}
}

View file

@ -227,7 +227,6 @@ namespace LayoutBXLYT
GlobalShader.Compile();
}
bool PreviewHitbox = false;
if (PreviewHitbox)
{
@ -240,10 +239,10 @@ namespace LayoutBXLYT
//Hitbox debug
var hitbox = pane.CreateRectangle();
hitbox = hitbox.GetTransformedRectangle(pane.Parent, pane.Translate, pane.Scale);
hitbox = hitbox.GetTransformedRectangle(pane.Parent, pane.Translate, pane.Rotate, pane.Scale);
GL.Begin(PrimitiveType.Quads);
GL.Color4(Color.FromArgb(28, 255, 0, 0));
GL.Color4(Color.FromArgb(128, 255, 0, 0));
GL.Vertex2(hitbox.LeftPoint, hitbox.BottomPoint);
GL.Vertex2(hitbox.RightPoint, hitbox.BottomPoint);
GL.Vertex2(hitbox.RightPoint, hitbox.TopPoint);
@ -325,7 +324,7 @@ namespace LayoutBXLYT
GL.Rotate(pane.Rotate.Z, 0, 0, 1);
GL.Scale(pane.Scale.X, pane.Scale.Y, 1);
DrawDefaultPane(shader, pane, true);
DrawDefaultPane(shader, pane, false);
GL.PopMatrix();
}
@ -414,7 +413,7 @@ namespace LayoutBXLYT
BxlytToGL.DrawTextbox(pane, GameWindow, bitmap, effectiveAlpha,
Textures, SelectedPanes, textPane.RenderableFont == null, isSelected);
else
DrawDefaultPane(shader, pane);
DrawDefaultPane(shader, pane, isSelected);
}
else if (pane is BFLYT.SCR1)
BxlytToGL.DrawScissorPane(pane, GameWindow, effectiveAlpha, isSelected);
@ -423,7 +422,7 @@ namespace LayoutBXLYT
else if (pane is BFLYT.PRT1)
DrawPartsPane(shader, (BFLYT.PRT1)pane, effectiveAlpha, isSelected, parentAlphaInfluence);
else
DrawDefaultPane(shader, pane);
DrawDefaultPane(shader, pane, isSelected);
}
else
isRoot = false;
@ -466,7 +465,7 @@ namespace LayoutBXLYT
private void DrawDefaultPane(BxlytShader shader, BasePane pane, bool isSelectionBox = false)
{
if (!Runtime.LayoutEditor.DisplayNullPane && !isSelectionBox || GameWindow || Runtime.LayoutEditor.IsGamePreview)
if (!Runtime.LayoutEditor.DisplayNullPane || GameWindow || Runtime.LayoutEditor.IsGamePreview)
return;
Vector2[] TexCoords = new Vector2[] {
@ -487,7 +486,7 @@ namespace LayoutBXLYT
color,
};
BxlytToGL.DrawRectangle(pane, GameWindow, pane.Rectangle, TexCoords, Colors);
BxlytToGL.DrawRectangle(pane, GameWindow, pane.Rectangle, TexCoords, Colors, true, 255, isSelectionBox);
}
private void DrawPartsPane(BxlytShader shader, BFLYT.PRT1 pane, byte effectiveAlpha,bool isSelected, bool parentInfluenceAlpha)
@ -729,13 +728,14 @@ namespace LayoutBXLYT
{
RenderEditor();
var coords = convertScreenToWorldCoords(e.Location.X, e.Location.Y);
pickOriginMouse = coords;
GL.PopMatrix();
//Add a content menu
var selectOverlapping = new STToolStripItem("Select Overlapping");
var createPanes = new STToolStripItem("Create Pane");
createPanes.DropDownItems.Add(new STToolStripItem("Null Pane"));
createPanes.DropDownItems.Add(new STToolStripItem("Null Pane", CreateNullPaneAction));
createPanes.DropDownItems.Add(new STToolStripItem("Picture Pane"));
createPanes.DropDownItems.Add(new STToolStripItem("Text Box Pane "));
createPanes.DropDownItems.Add(new STToolStripItem("Window Pane"));
@ -765,6 +765,21 @@ namespace LayoutBXLYT
Console.WriteLine("SelectedPanes " + SelectedPanes.Count);
}
private void CreateNullPaneAction(object sender, EventArgs e) {
var pane = ParentEditor.AddNewNullPane();
SetupNewPane(pane);
}
private void SetupNewPane(BasePane pane)
{
if (pane == null) return;
pane.Translate = new Syroot.Maths.Vector3F(pickOriginMouse.X, pickOriginMouse.Y, 0);
SelectedPanes.Add(pane);
glControl1.Invalidate();
}
private void DeletePaneAction(object sender, EventArgs e) {
DeleteSelectedPanes();
}
@ -795,6 +810,7 @@ namespace LayoutBXLYT
{
UndoManger.AddToUndo(new LayoutUndoManager.UndoActionPaneDelete(SelectedPanes, LayoutFile));
LayoutFile.RemovePanes(SelectedPanes);
SelectedPanes.Clear();
ParentEditor?.UpdateHiearchyTree();
glControl1.Invalidate();
}
@ -805,7 +821,8 @@ namespace LayoutBXLYT
if (toolMenu != null)
{
string name = toolMenu.Text;
SelectedPanes.Clear();
if (Control.ModifierKeys != Keys.Control)
SelectedPanes.Clear();
if (LayoutFile.PaneLookup.ContainsKey(name))
SelectedPanes.Add(LayoutFile.PaneLookup[name]);
@ -828,9 +845,12 @@ namespace LayoutBXLYT
if (!Runtime.LayoutEditor.DisplayNullPane && pane.IsNullPane)
isVisible = false;
if (isVisible && pane.DisplayInEditor && pane.IsHit(X, Y) && pane.Name != "RootPane")
if (isVisible && pane.DisplayInEditor && pane.IsHit(X, Y) && !pane.IsRoot)
{
SelectedPane = pane;
return;
}
foreach (var childPane in pane.Childern)
SearchHit(childPane, X, Y, ref SelectedPane);
}
@ -850,17 +870,18 @@ namespace LayoutBXLYT
isVisible = false;
if (isVisible && pane.DisplayInEditor && pane.IsHit(X, Y) && pane.Name != "RootPane")
SelectedPanes.Add(pane);
if (!SelectedPanes.Contains(pane))
SelectedPanes.Add(pane);
foreach (var childPane in pane.Childern)
SelectedPanes = GetHitPanes(childPane, X, Y, SelectedPanes);
GetHitPanes(childPane, X, Y, SelectedPanes);
return SelectedPanes;
}
private PickAction SearchEdgePicking(BasePane pane, int X, int Y)
{
var transformed = pane.CreateRectangle().GetTransformedRectangle(pane.Parent, pane.Translate, pane.Scale);
var transformed = pane.CreateRectangle().GetTransformedRectangle(pane.Parent, pane.Translate, pane.Rotate, pane.Scale);
var leftTop = new Point(transformed.LeftPoint, transformed.TopPoint);
var left = new Point(transformed.LeftPoint, (transformed.BottomPoint + transformed.TopPoint) / 2);
var leftBottom = new Point(transformed.LeftPoint, transformed.BottomPoint);