0

In my WPF application, I am trying to rotate a set of grouped shapes inside a canvas. I am getting grouped shape object TOP/LEFT position using the formulae mentioned in the below link.

Rotate a point by another point in 2D

If you rotate point (px, py) around point (ox, oy) by angle theta you'll get:

  p'x = cos(theta) * (px-ox) - sin(theta) * (py-oy) + ox
  p'y = sin(theta) * (px-ox) + cos(theta) * (py-oy) + oy

Since my rotated object is a grouped object it can contain multi level child. When I am ungrouping the grouped shape in canvas, I need to get the position and angle of the child shapes at the same position which is available in parent grouped shape object. How I can calculate the position of child object.

Sample Grouped object as shown in below image:

  • Rectangle (Parent Shape)
    • Rectangle (Child Shape 1)
    • Triangle (Child Shape 2)

Canvas grouped shape rotation Left part shows the rotated grouped shape whereas right part shows the rotated ungrouped child shapes.

For all shapes with in group, I have below information:

  • Child shape position with respect to parent group X & Y (Child_X & Child_Y)
  • Child shape angle with respect to parent group (Child_Angle)
  • Parent group angle and position with respect canvas (Parent_X, Parent_Y & Parent_Angle)
  • Width and Height of child shapes
  • Width and Height of Parent shapes

How I can get the location and angle for child shapes (Rectangle and triangle) after ungrouping? The child shapes should get placed at the same location in canvas where they are placed when they are inside the group. How I can calculate the location and angle for child shapes, Thanks in advance.

Below code I am using for storing the shape related parameters and logic for grouping and ungrouping.

/// <summary>
/// ShapeDiagram can be any shape like Rectangle/Triangle/Ellipse etc.
/// For now I am using only Rectangle template for 'ShapeDiagram' type.
/// </summary>
public class ShapeDiagram
{
    public double Left { get; set; }
    public double Top { get; set; }
    public double Angle { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
}

/// <summary>
/// Group can have more than one 'ShapeDiagram' elements. A custom shape can be created using child 'ShapeDiagram' elements.
/// User can rotate multiple 'ShapeDiagram' elements and create a group.
/// Group will have a rectangle border around the childrens as shown in the above image.
/// Group rectangle border is created using templates based on type like GroupShapeDigram or ShapeDiagram.
/// </summary>
public class GroupShapeDiagram : ShapeDiagram
{
    public ObservableCollection<ShapeDiagram> Childrens { get; set; }

}

/// <summary>
/// This class is binded to canvas and canvas child elements are binded to 'CanvasChildrens'
/// </summary>
public class ShapeHandler
{
    private ObservableCollection<ShapeDiagram> canvasChildrens;
    public ObservableCollection<ShapeDiagram> CanvasChildrens { get => canvasChildrens; set => canvasChildrens = value; }

    public ShapeHandler()
    {
        this.CanvasChildrens = new ObservableCollection<ShapeDiagram>();
    }

    public void AddCanvasChild(ShapeDiagram shape)
    {
        if(shape != null)
        {
            this.CanvasChildrens.Add(shape);
        }
    }

    public void RemoveCanvasChild(ShapeDiagram shape)
    {
        if (shape != null)
        {
            this.CanvasChildrens.Remove(shape);
        }
    }


    public GroupShapeDiagram DoGrouping(ObservableCollection<ShapeDiagram> childrens)
    {
        GroupShapeDiagram newGroup = new GroupShapeDiagram();

        newGroup.Childrens = childrens;
        newGroup.Angle = 0;
        newGroup.Top = 0;
        newGroup.Left = 0;
        newGroup.Width = GetNewGroupWidth(childrens);
        newGroup.Height = GetNewGroupHeight(childrens);

        // Add new group in the canvas child shapes collection
        this.CanvasChildrens.Add(newGroup);

        return newGroup;
    }

    public void DoUnGrouping(GroupShapeDiagram group)
    {
        // Remove selected group from the canvas child shapes collection
        this.CanvasChildrens.Remove(group);

        // TO-DO: Now adjust the group child shapes to correct location
        foreach(ShapeDiagram childShape in group.Childrens)
        {
            // *** This is where i am not getting correct values for Top/Left/Angle for child shapes after ungrouping
            childShape.Top += group.Top;
            childShape.Left += group.Left;
            childShape.Angle += group.Angle;
        }
    }

    private double GetNewGroupWidth(ObservableCollection<ShapeDiagram> childrens)
    {
        double maxWidth = 300;

        // TO-DO: Logic to get max width of all childrens with some buffer width, for now I am returning 300 as default group width

        return maxWidth;
    }

    private double GetNewGroupHeight(ObservableCollection<ShapeDiagram> childrens)
    {
        double maxHeight = 300;

        // TO-DO: Logic to get max heigth of all childrens with some buffer heigth, for now I am returning 300 as default group height

        return maxHeight;
    }

}

Here is the updated complete sample code which I am using for my application. ShapesDesignerCode

Thanks, IamHuM

IamHuM
  • 17
  • 2
  • You should rotate each shape individually if you need its individual location. WPF also offers a RotateTransform, which could simplify your task significantly. [How to: Rotate an Object](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/graphics-multimedia/how-to-rotate-an-object?view=netframeworkdesktop-4.8) – BionicCode May 04 '22 at 12:37
  • You get the location by referencing the attached property Canvas.GetTop(shape) etc. Then use the 2D point rotation formula to calculate the angle *Theta* based on the the points *P* and *P'*. – BionicCode May 04 '22 at 12:47
  • Or use the RotateTransform and reference the Angle property. – BionicCode May 04 '22 at 12:48
  • Just for your info, you can use the `System.Windows.Media.Matrix` struct and its `Matrix.RotateAt` method too if you really want to do everything manually. – BionicCode May 04 '22 at 22:47
  • Hello BionicCode, Thanks for your reply. I am already using RenderTransform and Angle property to update the shape rotation in UI. I am rotating the shape around the center of the shape. Almost same as the example which you mentioned except I am doing it through XAML binding. My major concern is after ungrouping I am not able to find the point inside canvas which will make sure that the shape is placed at the same place before ungrouping. How I can get that point after ungrouping application? – IamHuM May 07 '22 at 10:31
  • I tried to adding location of parent shape with location of child shape but then it does not work when parent and child shapes are rotated at an angle. Any suggestion for this calculations? – IamHuM May 07 '22 at 10:32
  • Can you please add more information, like how do you group and how do you render the shapes? It's easier to help when I can see how you have implemented your solution. Generally, a group (whatever you use to group your shapes - can't find any info on that...) is an abstract container. This container holds the information about transformations applied to its children. Since the grouped shapes seem to have a logical relationship I don't see any reason to ungroup them. Since the transformation information is applied on the group, grouping and ungrouping does not make much sense. – BionicCode May 07 '22 at 12:14
  • As I recommended in my first comment, you should transform each shape individually, if you can't leave them grouped for whatever reason or require the explicit coordinates of the group members. Calculating their coordinates is somewhat a redundant effort as calculating means to apply the transformation virtually to each shape, when you could rotate them individually in the first place. – BionicCode May 07 '22 at 12:14
  • *"I tried to adding location of parent shape with location of child shape but then it does not work when parent and child shapes are rotated at an angle."* - This is because you have transformed/rotated the coordinate system. You would have to rotate the resulting points to get their values relative to the original coordinate system. – BionicCode May 07 '22 at 12:18
  • *"For all shapes with in group, I have below information: [...] +Child shape position with respect to parent group X & Y (Child_X & Child_Y) +Child shape angle with respect to parent group (Child_Angle) [...] How I can get the location and angle for child shapes (Rectangle and triangle) after ungrouping?"* - It seems you already have all the information you need in you grouping object. Without understanding what you are trying to accomplish, I feel like you are writing a lot of redundant code. But I assume you have your reason to group them only for the rotation procedure. – BionicCode May 07 '22 at 12:24
  • I have added some code which I am using in the backend to save the shape values and to calculate all the child shape values after ungrouping. Please let me know your comments on how I can calculate location and angle for child shapes. Thanks in advance. – IamHuM May 07 '22 at 16:28
  • You should have posted this code from the beginning. It really helps to understand your problem and to suggest a solution. Are the shapes a child of the Canvas prior to becoming a group member? – BionicCode May 07 '22 at 16:57
  • Can you please show the transform operation? – BionicCode May 07 '22 at 17:15
  • However, Cavas.Top and Canvas.Left will return the position. And the angle of each shape inside a group is the same as the group's angle. – BionicCode May 07 '22 at 17:35

1 Answers1

0

It's not clear to me how you rotate the shapes and how you can get the required values to perform the calculations and what point you actually need. You still didn't reveal enough details to give you a working solution.

In case you need the shape position on the Canvas, you can reference the Canvas.Left and Canvas.Top properties and the angle of your group.

In case you need the new absolute location of the top-left corner of the shape, you can use the Matrix to calculate them:

double rotationAngle;
Point rotationCenter;
Point shapePositionOnCanvas;
var transformationMatrix = new Matrix();
transformationMatrix.RotateAt(rotationAngle, rotationCenter.X, rotationCenter.Y);
Point topLeftShapePosition = transformationMatrix.Transform(shapePositionOnCanvas);
BionicCode
  • 1
  • 4
  • 28
  • 44
  • Hello BionicCode, Please find the below link for my sample code, please let me know your suggestions on Upgrouping operation. [ShapesDesignerCode](https://github.com/Iam-HuM/ShapesDesigner) Thanks, IamHuM – IamHuM May 09 '22 at 05:27