Applying 2D transformations (scale, rotate, translate) on diagram nodes


Hi,

I would like to apply more than one 2D transformation at a time on a SfDiagram node.
What I tried is creating two points:
Point1 (Node.OffsetX,Node.OffsetY)
Point2 (Node.OffsetX+Node.UnitWidth,Node.OffsetY+UnitHeight)
and apply Matrix transformatios on the two points for Scale and Translate, and adding to Node.RotateAngle for the rotation.

The problem is that if I rotate a node shaped like a rectangle by 45 degrees and then scale it times 2 only in the X axis, I don't know how to do that, because if I only change node Offset, Height, Width and rotate angle it will still look like a rectangle, but what I need is to change it to a parallelogram by my 2D transformations.

Is there a way to do that?

Thanks!

Tanya Zelikman.

21 Replies 1 reply marked as answer

KR Karkuvel Rajan Shanmugavel Syncfusion Team April 22, 2021 03:14 PM UTC

Hi Tanya, 
 
Requirement: Need to apply 2D transformations on diagram nodes. 
 
Currently, we are working on your requirement. We will provide sample for your requirement on 23rd April, 2021. 
 
Regards, 
Karkuvel Rajan S 



KR Karkuvel Rajan Shanmugavel Syncfusion Team April 23, 2021 07:46 AM UTC

Hi Tanya, 
 
Requirement: Need to apply transforms for diagram nodes. 
 
We have prepared a sample for your requirement. In the sample we have used a button click to transform Rectangle into Parallelogram. In the button we have used the Matrix transform to change the geometry of the shape of the Node. Please find the sample in below link. 
 
 
Regards, 
Karkuvel Rajan S 


Marked as answer

TZ Tanya Zelikman April 27, 2021 10:32 AM UTC

Hi Karkuvel Rajan,

Thank you for the sample.

I have a clarification questions about this subject.

The matrix I see in the example does change the rectangle into a parrallelogram, but what I need is to get the transformation parameters as input and apply them on the node for the following transformations. I tried using the following matrixes like in your example but I don't see the effect on the node:

a. Translate - input is translateX and translateY. I've tried doing that by using a matrix that is created like this:

Matrix matrixTranslate = new Matrix();

matrixTranslate.Translate(OffsetX, OffsetY);

b. Scale - I also tried:

Matrix matrix = new Matrix();

matrix.ScaleAt(ScaleX, ScaleY, OffsetX, OffsetY);

c. Rotate

matrixRotateAt.RotateAt(RotationAngle, OffsetX, OffsetY);

Is it possible to use input parameterers to change the node appearance and location?

Thanks





KR Karkuvel Rajan Shanmugavel Syncfusion Team April 28, 2021 09:09 AM UTC

Hi Tanya, 
 
Requirement: Need to apply transform for the Node. 
 
In SfDiagram, for Node we have Shape and ContentTemplate properties to set any geometry for Node. We can apply any transform for that geometry. We have UnitHeight, UnitWidth properties for Scaling,  OffsetX, OffsetY properties for Translate, RotateAngle property for rotation. We can edit these values in runtime also.   
 
If we misunderstood your requirement please elaborate your requirement through any screenshot or video representation. 
 
Regards, 
Karkuvel Rajan S 



TZ Tanya Zelikman April 28, 2021 04:23 PM UTC

Hi Karkuvel Rajan,

I understand the possibility to change the properties UnitHeight, UnitWidth, OffsetX, OffsetY and RotateAngle.
That would be enough if I only had to apply one 2D transformation on each node.
The issue is that I need to apply more than one transformation on each node.
For example, I need to be able to apply rotate transformation a node shaped like a rectangle by 45 degrees and then scale it times 2 only in the X axis.
That would change the node to a parallelogram, so just changing the above properties will not be enough.
But this is only one example, I need to apply any number of transformations on a node.
Therefore I have a few questions:

1. The idea I currently have is: getting each of the node shape vertices in a VSG format, getting the (x,y) of each point, adding the OffsetX to the x and OffsetY to the y, applying all the transformations I need using matrixes, and then calculating the new OffsetX and OffsetY and setting the node shape to the new VSG points.
I really hope there's an easier way to do this, please tell me if there is one.

2. Is there a way to get the points of a node shape?
For example, if I set a node shape like this:

Shape = new RectangleGeometry { Rect = new Rect(0, 0, Width, Height) },

and I want to get a list of points of the rectangle vertices?
I've tried using

Node.Shape.ToString();

but it returns

"System.Windows.Media.RectangleGeometry"

instead of the SVG format that I thought I will have, like this one:

"M0,0L30,0L30,30L0,30z"

and also this fails:

Geometry.Parse("System.Windows.Media.RectangleGeometry");

but this doesn't:
Geometry.Parse("M0,0L30,0L30,30L0,30z");

so the questions are:
a. Is there a way to get a string of points from the Shape so I can use the GeometryParse as in your sample?
b. Is there a way to get a list of points from the Shape in (x,y) format, so I can add OffsetX and OffsetY and apply my transformations?

Thank you,
Tanya.




KR Karkuvel Rajan Shanmugavel Syncfusion Team April 29, 2021 12:08 PM UTC

Hi Tanya, 
 
Currently, we are validating your requirement. We will update you with more details on 30th April, 2021. 
 
Regards, 
Karkuvel Rajan S 



KR Karkuvel Rajan Shanmugavel Syncfusion Team April 30, 2021 06:40 AM UTC

Hi Tanya, 
 
Requirement: Need to apply transforms for diagram nodes. 
 
We have prepared a simple sample for your requirement. We have used the node properties as matrix values and used them for the transform. Please find the code example below. 
 
Code Example: 
 
 
            Matrix Mat = new Matrix(node.OffsetX, node.OffsetY, node.OffsetX + node.UnitWidth, node.OffsetY + node.UnitHeight, node.OffsetX, node.OffsetY); 
 
            Mat.Rotate(-45); 
 
            Mat.Translate(node.OffsetX, node.OffsetY);             
 
            MatrixTransform matrix = new MatrixTransform() { Matrix =  Mat};             
 
            PathGeometry geometry = Geometry.Combine(Geometry.Parse(node.Shape.ToString()),Geometry.Parse(node.Shape.ToString()), GeometryCombineMode.Union, matrix); 
 
            node.Shape = geometry; 
 
 
 
Query 
Response 
 
Is there a way to get a string of points from the Shape so I can use the GeometryParse as in your sample? 
 
For Shape property please use our default Rectangle shape not by using Rectangle geometry. This will help you to get the path data from the shape property. Please find the code example below. 
 
Code example: 
 
 
            <ResourceDictionary.MergedDictionaries> 
                <ResourceDictionary Source="/Syncfusion.SfDiagram.Wpf;component/Resources/BasicShapes.xaml" /> 
            </ResourceDictionary.MergedDictionaries> 
 
<sync:NodeViewModel x:Name="node" UnitHeight="40" UnitWidth="120" OffsetX="200" OffsetY="200" Shape="{StaticResource Rectangle}"/> 
 
 
 
 
 
Is there a way to get a list of points from the Shape in (x,y) format, so I can add OffsetX and OffsetY and apply my transformations? 
 
 
If we used PathData directly as like the above example we can get the points directly. Please find the code example below. 
 
 
            PathGeometry g = Geometry.Parse(node.Shape.ToString()).GetFlattenedPathGeometry(); 
 
            foreach (var f in g.Figures) 
            { 
                foreach (var s in f.Segments) 
                { 
                    if (s is PolyLineSegment) 
                    { 
                        foreach (var pt in ((PolyLineSegment)s).Points) 
                        { 
                            // Write the code for your requirement 
                        } 
                    } 
                } 
            } 
 
 
 
 
 
Regards, 
Karkuvel Rajan S 



TZ Tanya Zelikman May 6, 2021 02:21 PM UTC

Hi,

First of all I want to say thank you so much, your answers helped me tremendously so far!

I do have another questions.

I'm subscribing to the  Node.PropertyChanged event because I need to do some calculations when the user drags the diagram node or stretches it.
I'm using the parameter of type  PropertyChangedEventArgs to know which action was done by the user.

The issue is that I need to perform two different types of calculations in the two cases:

1. Parameter OffsetX of the node was changed because the user dragged the node
and
2. Parameter OffsetX of the node was changed because the user stretched the node in the negative X axis direction.

The only idea I have so far is using the fact that in case 2 the callback is called with OffsetX immediately after it is called with UnitWidth, so I can set a timer and check
when I receive OffsetX how much time has passed since the last time I received UnitWidth. I'm not sure this solution is bug safe, if the user works quickly.

Is there a better way to differentiate the two cases?

Thank you,
Tanya.


KR Karkuvel Rajan Shanmugavel Syncfusion Team May 7, 2021 03:46 AM UTC

Hi Tanya, 
 
We can achieve your requirement by using the NodeChangedEvent of the SfDiagram. NodeChangedEvent gives you the what type of action you have done through InteractionState property and changes in the values of Node. Please refer the following code example. 
 
Code Example: 
 
 
            (sfdiagram.Info as IGraphInfo).NodeChangedEvent += MainWindow_NodeChangedEvent; 
            // Here sfdiagram is the instance of SfDiagram. 
 
        private void MainWindow_NodeChangedEvent(object sender, ChangeEventArgs<object, NodeChangedEventArgs> args) 
        { 
            if(args.NewValue.InteractionState == NodeChangedInteractionState.Dragging) 
            { 
                // Write your necessary calculation. 
                // args.Item value is always node. So, you can get its property values here itself. 
            } 
            else if(args.NewValue.InteractionState == NodeChangedInteractionState.Resizing) 
            { 
                // Write your necessary calculation. 
                // args.Item value is always node. So, you can get its property values here itself. 
            } 
        } 
 
 
For your convenience we have also created a sample that can be downloaded from the following link. 
 
 
To Know more about interaction in node please follow the below Documentation link. 
 
 
Regards, 
Karkuvel Rajan S 



TZ Tanya Zelikman May 23, 2021 09:14 AM UTC

Hi again and thank you for your help!

I have another question regarding node appearance.
I'm trying to display an open shape node such as "Delay", but it appears as a closed shape. I've attached the node appearance jpg file.

I'm setting the shape as such:

Shape = DisplayVm.BasicShapesDictionary["Delay"],

setting the shape style:

style.Setters.Add(new Setter(Shape.FillProperty, Brushes.Transparent));

style.Setters.Add(new Setter(Shape.StretchProperty, Stretch.Fill));

style.Setters.Add(new Setter(Shape.StrokeProperty, Brushes.Black));

style.Setters.Add(new Setter(Shape.StrokeThicknessProperty, 2d));

but the shape appears closed.

I've tried using "Delay" shape in the "SfDiagram binding with TreeView" sample instead of the rectangle by changing the XAML file and it looked as expected, but I have to work in code behind.

Could you help me with that?

Thanks,

Tanya.




Attachment: ClosedShapeDelay_cdc54e63.7z


KR Karkuvel Rajan Shanmugavel Syncfusion Team May 24, 2021 12:19 PM UTC

Hi Tanya, 
 
Reported issue: Delay shape looks as closed path 
 
We have validated the reported issue and its not an issue. Delay shape is a closed path only. In our UG we have wrongly update the Delay image. We have analyze any other category shapes that will help you to achieve your requirement. But any built-in shapes in any category doesn’t match your requirement. So we have provided a open path for your requirement. Please use the below provided path to achieve your requirement. 
 
Path Data:  
 
M3030.9443,2728L3030.9443,2777.39L3060.9443,2807.02L3090.9443,2777.39L3090.9443,2728

Code Example: 
 
node.Shape = Geometry.Parse("M3030.9443, 2728L3030.9443, 2777.39L3060.9443, 2807.02L3090.9443, 2777.39L3090.9443, 2728"); 
 
Regards, 
Karkuvel Rajan S 



TZ Tanya Zelikman May 26, 2021 05:56 PM UTC

Hi,

 

Thank you for your answer,


I figured out that the reason I was getting a closed shape was using the Geometry.Combine method which added z at the end of the shape.

 

I have another question


I've attached two images, one that I would like to achieve and the one that I managed to achieve so far.


I would like to have a node shape that is colored, while some of its lines are visible and some are transparent.

You may see the example in the images that are attached, where the background is black - two lines are invisible, that is what I would like to achieve.


I've set the node shape and style, but I don't know how to set different styles (transparent for some and colored for others) for different lines.


I understand it would be possible if I could draw two different shapes with two different styles for a node, but I don't know whether it is possible.


Could you help me?


Thank you.


Tanya.



Attachment: LinesTransparent_fea85a4f.7z


KR Karkuvel Rajan Shanmugavel Syncfusion Team May 27, 2021 12:53 PM UTC

Hi Tanya, 
 
Requirement: Need to show shape with stroke on the required side alone. 
 
We have achieve your requirement by using the ContentTemplate property of the nodes. In ContentTemplate we can use the DataTemplate with the required Path. Please refer the code example. 
 
Code Example: 
 
 
<sfdiagram:NodeViewModel.ContentTemplate> 
    <DataTemplate> 
        <Path Stroke="Black" StrokeThickness="2" Fill="CornflowerBlue" Stretch="Fill"> 
            <Path.Data> 
                <PathGeometry> 
                    <PathGeometry.Figures> 
                        <PathFigureCollection> 
                            <PathFigure IsClosed="True" StartPoint="0,0"> 
                                <PathFigure.Segments> 
                                    <PathSegmentCollection> 
                                        <LineSegment Point="0,100" IsStroked="False"/> 
                                        <LineSegment Point="100,100" IsStroked="True"/> 
                                        <LineSegment Point="100,0" IsStroked="True"/> 
                                        <LineSegment Point="0,0" IsStroked="False"/> 
                                    </PathSegmentCollection> 
                                </PathFigure.Segments> 
                            </PathFigure> 
                        </PathFigureCollection> 
                    </PathGeometry.Figures> 
                </PathGeometry> 
            </Path.Data> 
        </Path> 
    </DataTemplate> 
</sfdiagram:NodeViewModel.ContentTemplate> 
 
   
For your convenience we have also created a sample that can be downloaded from the following link. 
 
 
Regards, 
Karkuvel Rajan S 
 
 



TZ Tanya Zelikman May 28, 2021 06:19 AM UTC

Hi Karkuvel Rajan S ,
Thank you for your suggestion of using ContentTemplate of the node.

1. The values of the points in my segments are variables. Before drawing the node I create a path in code behind using the variables as point values and then set the node shape to the path.Data:
Node.Shape = path.Data.
Could you help me understand how I can set ContentTemplate of the node to the Path I create in code behind?
The ContentTemplate should be set to the path only for the specific node each time it is redrawn, no need for it to be reusable.

2. Once I set ContentTemplate does it mean that I shouldn't set a value to node Shape and Shapestyle? Should I set them to null?

Thank you again for all your help.
Tanya



KR Karkuvel Rajan Shanmugavel Syncfusion Team May 30, 2021 05:15 PM UTC

Hi Tanya, 
 
Please find the response for your queries in the below table. 
 
Query 
Response 
 
The values of the points in my segments are variables. Before drawing the node I create a path in code behind using the variables as point values and then set the node shape to the path.Data: 
Node.Shape = path.Data. 
Could you help me understand how I can set ContentTemplate of the node to the Path I create in code behind? 
The ContentTemplate should be set to the path only for the specific node each time it is redrawn, no need for it to be reusable. 
 
 
We have created a simple sample for your requirement of how to create DataTemplate in code behind. Please find the code example and sample below. 
 
Code Example:  
 
 
            DataTemplate template = new DataTemplate(); 
            FrameworkElementFactory Element = new FrameworkElementFactory(typeof(Path)); 
            Element.SetValue(Path.FillProperty, new SolidColorBrush(Colors.CornflowerBlue)); 
            Element.SetValue(Path.StrokeProperty, new SolidColorBrush(Colors.Black)); 
            Element.SetValue(Path.StrokeThicknessProperty, 2.0); 
            Element.SetValue(Path.StretchProperty, Stretch.Fill); 
            Element.SetValue 
            ( 
                Path.DataProperty,new PathGeometry() 
                { 
                    Figures = new PathFigureCollection() 
                    { 
                        new PathFigure() 
                        { 
                            IsClosed = true, 
                            StartPoint = new Point(0,0), 
                            Segments = new PathSegmentCollection() 
                            { 
                                new LineSegment(new Point(0,100), false), 
                                new LineSegment(new Point(100,100), true), 
                                new LineSegment(new Point(100,0), true), 
                                new LineSegment(new Point(0,0), false), 
                            }, 
                        }, 
                    }, 
                } 
            ); 
 
            template.VisualTree = Element; 
 
            node.ContentTemplate = template; 
 
 
 
 
 
 
Once I set ContentTemplate does it mean that I shouldn't set a value to node Shape and Shapestyle? Should I set them to null? 
 
 
In our SfDiagram control, there is no need to set any value for Shape and ShapeStyle properties when we used ContentTemplate to set the shape for NodeViewModel. 
 
Regards,
Karkuvel Rajan S
 



TZ Tanya Zelikman June 3, 2021 06:41 AM UTC

Thank you so much, as always, your answers have been such a great help!

I have another question, this time about creating a 3D node.

1. Is it possible to create a 3D node in the diagram, perhaps by setting the node shape to a 3d model somehow (maybe using svg)?

2. Is it possible to use openGL to create a 2D image for the node? Maybe by saving an openGL rendering to a frame buffer and use this buffer as an image in the node annotation? Or some better way perhaps?

Thank you!

Tanya.


KR Karkuvel Rajan Shanmugavel Syncfusion Team June 4, 2021 10:56 AM UTC

Hi Tanya, 
 
Please find the response for your queries in the below table. 
 
Query 
Response 
 
Is it possible to create a 3D node in the diagram, perhaps by setting the node shape to a 3d model somehow (maybe using svg)? 
 
 
SfDiagram supports 2D drawing only. So we doesn’t have the support to create a node in 3D. We have provided our online documentation link. Please visit the below link for more details.  
 
  
 
 
Is it possible to use openGL to create a 2D image for the node? Maybe by saving an openGL rendering to a frame buffer and use this buffer as an image in the node annotation? Or some better way perhaps? 
 
 
We can achieve your requirement by creating an template with OpenGL and use the template in ViewTemplate of the Annotation. We have prepared a simple sample for this. Please find the sample below. 
 
 
 
Regards, 
Karkuvel Rajan S 



TZ Tanya Zelikman replied to Karkuvel Rajan Shanmugavel June 27, 2021 10:29 AM UTC

Hi, thank you for your answers!


I have another question about using  NodeChangedEvent when the node is resized.

What I need to do sometimes is change the node height when the user resizes it's width.

The problem is that when I change the node height as the user resizes it's widthNodeChangedEvent is called for the height change as well, and that is not what I need.

It would help me if I could:

  1. Prevent NodeChangedEvent from being invoked when the node height is resized from the code and not by the user
  2. Or: when receiving NodeChangedEvent arguments, knowing somehow that is was called by a change from the code and not by the user

    What I tried so far is unregistering to the event during the call for the function that changes node Y axis size from the code, but it didn't work, I was still getting those notifications.

    Thanks!
    Tanya.


KR Karkuvel Rajan Shanmugavel Syncfusion Team June 28, 2021 09:33 AM UTC

Hi Tanya, 
 
Requirement: Need to stop firing NodeChangedEvent while there is a change in Height. 
 
We have analyzed your requirement in our NodeChangedEvent. Currently, there is no way to stop firing the event while there is a change in Height through code behind in the same event. And for this use case, there is no option to distinguish whether change is triggered due to user interaction or from code behind. Subscribe and Unsubscribe the event is the only way to achieve your requirement. 
Based on your requirement, we assume that you want to change the width and height with the same aspect ratio. If so, you can use the NodeConstraints.AspectRatio to maintain the ratio. 
 
 
Regards, 
Karkuvel Rajan S 



TZ Tanya Zelikman June 30, 2021 12:55 PM UTC

Thank you!


Another question please.


I would like the selection area (the rectangle around the node with the 9 circles you can use to resize) to be in the location that I can specify. For example, twice as big as my node or in a totally different location.

Is that possible?


Thanks!


Tanya.



KR Karkuvel Rajan Shanmugavel Syncfusion Team July 1, 2021 05:32 AM UTC

Hi Tanya, 
 
Requirement: Need to specify location for Selector or magnify the Selector. 
 
Currently, Our SfDiagram control doesn’t have any possibilities to achieve your requirement. We have also analyzed Visio and other competitors also to check the behavior for adding your requirement as Feature Request. But your requirement is not valid due to selector behavior is used for Interaction purposes only. So, we are not adding your requirement as a Feature Request. 
 
Regards, 
Karkuvel Rajan S 


Loader.
Up arrow icon