Page Index Toggle Pages: 1 [2] 3  Send TopicPrint
Very Hot Topic (More than 25 Replies) Autosnap diagram nodes if within certain distance? (Read 23259 times)
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #15 - Oct 9th, 2013 at 1:09pm
Print Post  
Hi Stoyan,

Could you give an example of how the height would be updated so that one "C" shape would fit in another "C" shape? And if I added another one inside that "C", how to updated the most outer "C" shape?

Thanks in advance!

Vincent
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Autosnap diagram nodes if within certain distance?
Reply #16 - Oct 9th, 2013 at 2:01pm
Print Post  
You won't be modifying the Shape objects in such case, only the node's Bounds. E.g. if the shape's parts are 20 units thick as in the sample code, subtract 40 from Bounds.Height of each subsequent nested node (and 20 from Width).

I hope that helps,
Stoyan
  
Back to top
 
IP Logged
 
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #17 - Oct 9th, 2013 at 2:26pm
Print Post  
Hi Stoyan,

That reduces the size of each subsequent node to fit into the 'parent' node. How can I make the 'parent' node increase in size (the 'child' node keeps the same size)?

Thanks in advance!

Vincent
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Autosnap diagram nodes if within certain distance?
Reply #18 - Oct 9th, 2013 at 5:26pm
Print Post  
You will have to loop over all parent nodes starting from the new one (or call recursive function depending on how you are storing them), inflating parents' Bounds with 20 units on the left, top and bottom sides.

I hope that helps,
Stoyan
  
Back to top
 
IP Logged
 
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #19 - Oct 10th, 2013 at 1:04pm
Print Post  
Hi Stoyan,

I'm trying to enlarge the parent node by using the method Bounds.Inflate() but that doesn't seem to do anything to the parent node. And if I say something like:
Bounds = new Rect(Bounds.X, Bounds.Y, Bounds.Width + 20, Bounds.Height + 20); the node keeps expanding while I'm moving the mouse near that position.

Is there any other way to enlarge the node?

Thanks in advance!

Vincent
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Autosnap diagram nodes if within certain distance?
Reply #20 - Oct 11th, 2013 at 6:09am
Print Post  
Hi,

Rect is a value type, so Bounds returns a value in the stack instead of reference to the node's bounds field, and calling node.Bounds.Inflate() directly will modify the stack value instead the node's bounds. From your image it doesn't look like you want to inflate the rectangle in all directions as Inflate() does, so you will be assigning a new Rect anyway.

You could keep a reference to the last inflated node to prevent modifying it again from next mouse move operations:

Code
Select All
DiagramNode lastInflatedNode = null;
Rect lastBounds = new Rect();
...

// in Align method
if (lastInflatedNode != nearbyNode)
{
	if (lastInflatedNode != null)
		lastInflatedNode.Bounds = lastBounds;

	Rect r = nearbyNode.Bounds;
	lastBounds = r;
	nearbyNode.Bounds = new Rect(r.X - 20, r.Y - 20, r.Width, r.Height + 40);
	lastInflatedNode = nearbyNode;
} 



Also reset lastInflatedNode back to null in NodeCreated handler.

I hope that helps,
Stoyan
  
Back to top
 
IP Logged
 
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #21 - Oct 11th, 2013 at 6:43am
Print Post  
Hi Stoyan,

I have a Node that has the following shape:

Code
Select All
String outline = @"
                                h = Height;
                                w = Width;
                                midH = Height / 2;

                                LineTo(5, 0);

                                LineTo(10, -7);
                                LineTo(15, 0);

                                LineTo(w, 0);
                                LineTo(w, 15);
                                LineTo(35, 15);

                                LineTo(30, 8);
                                LineTo(25, 15);

                                LineTo(20, 15);
                                LineTo(20, midH-7.5);
                                LineTo(w, midH-7.5);
                                LineTo(w, midH+7.5);
                                LineTo(35, midH+7.5);

                                LineTo(30, midH+0.5);
                                LineTo(25, midH+7.5);

                                LineTo(20, midH+7.5);
                                LineTo(20, h-15);
                                LineTo(w, h-15);
                                LineTo(w, h);
                                LineTo(15, h);

                                LineTo(10, h-7);
                                LineTo(5, h);

                                LineTo(0, h);
                                LineTo(0, 0);";
 



That produces the following shape for the node:



I can snap the same node to any of the three marked positions of a previously added node with the same shape.

Here is the code that handles the snapping:
Code
Select All
protected void AlignToNearestPoint(Point current, InteractionState ist)
        {
            if (ist.AdjustmentHandle == 8) // If the node is moved
            {
                var nearbyNode = Parent.GetNearestNode(current, 30, this);

                if (nearbyNode != null)
                {
                    /* If the nearest node has multiple points to connect to, the connection Points
                     * will be stored here and used later to calculate to which point to snap to.
                     */
                    Point[] targetPoints;

                    var bounds_nearby_node = nearbyNode.Bounds;
                    double x_coord = 0;
                    double y_coord = 0;

                    var bounds_this_node = this.Bounds;
                    double x_coord2 = 0;
                    double y_coord2 = 0;

                    double dx = 0;
                    double dy = 0;

                    if (nearbyNode is If_Shape)
                    {
                        // Calculate the point of the nearby node to connect to, default is bottom
                        x_coord = bounds_nearby_node.X + bounds_nearby_node.Width / 2;
                        y_coord = bounds_nearby_node.Bottom - bounds_nearby_node.Height / 90;
                        var targetPoint = new Point(x_coord, y_coord);

                        // Calculate the point of this node to connect to the nearby node
                        x_coord2 = bounds_this_node.X + bounds_this_node.Width / 2;
                        y_coord2 = bounds_this_node.Y;
                        var pointToSnap = new Point(x_coord2, y_coord2);

                        double then_x = bounds_nearby_node.X + bounds_nearby_node.Width / 1.43;
                        double then_y = bounds_nearby_node.Bottom - bounds_nearby_node.Height / 1.18;
                        Point then_statement = new Point(then_x, then_y);

                        double else_x = bounds_nearby_node.X + bounds_nearby_node.Width / 1.43;
                        double else_y = bounds_nearby_node.Bottom - bounds_nearby_node.Height / 2.4;
                        Point else_statement = new Point(else_x, else_y);

                        Point bottom = new Point(bounds_nearby_node.X + bounds_nearby_node.Width / 2,
                            bounds_nearby_node.Bottom - bounds_nearby_node.Height / 90);
                        double minDistance = double.MaxValue;
                        targetPoints = new Point[3] { then_statement, else_statement, bottom };

                        // Check the distance of each targetpoint and the pointToSnap for the closest point
                        foreach (var s in targetPoints)
                        {
                            // Calculate the distancen between the points
                            double distance_squared = (pointToSnap.X - s.X) * (pointToSnap.X - s.X) + (pointToSnap.Y - s.Y) * (pointToSnap.Y - s.Y);
                            double distance = Math.Sqrt(distance_squared); //Utilities.Distance(pointToSnap, s);
                            if (distance < minDistance)
                            {
                                minDistance = distance;
                                targetPoint = s;
                            }
                        }

                        // Calculate the distance of the two points
                        dx = targetPoint.X - pointToSnap.X;
                        dy = targetPoint.Y - pointToSnap.Y;

                        // Move this node by the calculated distance
                        bounds_this_node.Offset(dx, dy);
                        SetBounds(bounds_this_node, true, true);

                        // Lastly, set the connectednode of the nearest node to this node
                        If_Shape nearby_Node = nearbyNode as If_Shape;
                        nearby_Node.connected_node = this;
                        //nearby_Node.resizeAfterSnapping(this, then_statement, else_statement, bottom, targetPoint);

                        if (lastInflatedNode != nearby_Node)
                        {
                            if (lastInflatedNode != null)
                                lastInflatedNode.Bounds = lastBounds;

                            Rect r = nearby_Node.Bounds;
                            lastBounds = r;
                            nearby_Node.Bounds = new Rect(r.X - 20, r.Y - 20, r.Width, r.Height + 40);
                            lastInflatedNode = nearby_Node;
                        }
                    }
}
}
 



For example, if I want to snap a node to the 1st marked spot the distance between the upper part and the middle part should become the height of the added node (100 in this case because I set the Bounds to (100, 100)). And if I want to snap it to the 2nd marked spot only the distance between the middle and lower part should increase. The snapping works but I'm still a little stuck on the re-sizing. At first I tried assigning a new shape with the calculated differences but the Bounds stay the same. Any ideas?

Thanks in advance!

Vincent
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Autosnap diagram nodes if within certain distance?
Reply #22 - Oct 11th, 2013 at 8:03am
Print Post  
Bounds does not resize automatically to fit the coordinates from your shapes, you will still need to set larger Bounds when changing the Shape property.

You will be better off overriding the Draw method and drawing the shape geometry yourself though; otherwise you will need too many Shape objects to cover all scenarios if such nodes can be nested recursively. If the nodes keep references to their If / Else children, the Draw method can use ifNode.Bounds.Height and elseNode.Bounds.Height values when deciding what to draw.

I hope that helps,
Stoyan
  
Back to top
 
IP Logged
 
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #23 - Oct 11th, 2013 at 8:49am
Print Post  
Hi Stoyan,

Could you give an example on how to override the draw method? Or specifically, how to define the DrawingContext parameter.

Thanks in advance!

Vincent
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Autosnap diagram nodes if within certain distance?
Reply #24 - Oct 11th, 2013 at 9:45am
Print Post  
Code
Select All
using System.Windows.Media;

class BR_Shape : ShapeNode
{
	DiagramNode ifPart;
	DiagramNode elsePart;

	public override void Draw(DrawingContext graphics,
		MindFusion.Diagramming.Wpf.RenderOptions options)
	{
		double totalHeight = Bounds.Height;
		double padding = 20;
		double ifHeight = ifPart != null ? ifPart.Bounds.Height : 0;
		double elseHeight = ifPart != null ? ifPart.Bounds.Height : 0;
		if (ifHeight == 0 && elseHeight == 0)
			ifHeight = elseHeight = (totalHeight - 3 * padding) / 2;
		else if (ifHeight == 0)
			ifHeight = totalHeight - elseHeight - 3 * padding;
		else if (elseHeight == 0)
			elseHeight = totalHeight - ifHeight - 3 * padding;

		var shape = new PathGeometry();
		shape.Figures.Add(new PathFigure(new Point(), new []
		{
			new LineSegment(new Point(Bounds.Width, 0), true),
			new LineSegment(new Point(Bounds.Width, padding), true),
			new LineSegment(new Point(padding, padding), true),
			new LineSegment(new Point(padding, padding + ifHeight), true),
			new LineSegment(new Point(Bounds.Width, padding + ifHeight), true),
			new LineSegment(new Point(Bounds.Width, Bounds.Height - padding - elseHeight), true),
			new LineSegment(new Point(padding, Bounds.Height - padding - elseHeight), true),
			new LineSegment(new Point(padding, Bounds.Height - padding), true),
			new LineSegment(new Point(Bounds.Width, Bounds.Height - padding), true),
			new LineSegment(new Point(Bounds.Width, Bounds.Height), true),
			new LineSegment(new Point(0, Bounds.Height), true),
			new LineSegment(new Point(0, 0), true),
		}, true));

		graphics.DrawGeometry(Brush, new Pen(Stroke, StrokeThickness), shape);
	}
	... 



I hope that helps,
Stoyan
  
Back to top
 
IP Logged
 
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #25 - Oct 11th, 2013 at 11:36am
Print Post  
Hi Stoyan,

I've overwritten the draw and now the Bounds are changing correctly. While the shape does expand, it doesn't change the form correctly.

This is the code for the draw method and the method that assigns the if and else nodes to this node.

Code
Select All
public override void Draw(DrawingContext graphics, MindFusion.Diagramming.Wpf.RenderOptions options)
        {
            Debug.WriteLine("Draw() called!");
            double totalHeight = Bounds.Height;
            double padding = 20;
            double ifHeight = Then_connected_node != null ? Then_connected_node.Bounds.Height : 0;
            double elseHeight = Then_connected_node != null ? Then_connected_node.Bounds.Height : 0;
            // Then-part and Else-part are null
            if (ifHeight == 0 && elseHeight == 0)
            {
                ifHeight = elseHeight = (totalHeight - 3 * padding) / 2;
                Debug.WriteLine("Both then and else are null..\nifHeight: " + ifHeight + "\nelseHeight: " + elseHeight);
            }
            // Only Then-part is null
            else if (ifHeight == 0)
            {
                ifHeight = totalHeight - elseHeight - 3 * padding;
                Debug.WriteLine("Only then is null..\nifHeight: " + ifHeight + "\nelseHeight: " + elseHeight);
            }
            // Only Else-part is null
            else if (elseHeight == 0)
            {
                elseHeight = totalHeight - ifHeight - 3 * padding;
                Debug.WriteLine("Only else is null..\nifHeight: " + ifHeight + "\nelseHeight: " + elseHeight);
            }

            var shape = new PathGeometry();
            shape.Figures.Add(new PathFigure(new Point(), new[]
            {
			    new LineSegment(new Point(Bounds.Width, 0), true),
			    new LineSegment(new Point(Bounds.Width, padding), true),
			    new LineSegment(new Point(padding, padding), true),
			    new LineSegment(new Point(padding, padding + ifHeight), true),
			    new LineSegment(new Point(Bounds.Width, padding + ifHeight), true),
			    new LineSegment(new Point(Bounds.Width, Bounds.Height - padding - elseHeight), true),
			    new LineSegment(new Point(padding, Bounds.Height - padding - elseHeight), true),
			    new LineSegment(new Point(padding, Bounds.Height - padding), true),
			    new LineSegment(new Point(Bounds.Width, Bounds.Height - padding), true),
			    new LineSegment(new Point(Bounds.Width, Bounds.Height), true),
			    new LineSegment(new Point(0, Bounds.Height), true),
			    new LineSegment(new Point(0, 0), true),
		    }, true));

            graphics.DrawGeometry(Brush, new Pen(Stroke, StrokeThickness), shape);
            Debug.WriteLine("Draw() finished!");
        }

        public void assignNodeToPart(BR_Shape child_shape, Point coord_then, Point coord_else, Point coord_bottom, Point snapPoint)
        {
            /* If the child snapped to the Then part then assign this then_connected_node to the child node
             * or if the child snapped to the Else part then assign this else_connected_node to the child node
             * otherwise assign it to the connected_node (child snapped to the bottom)
             */
            double height_to_add = child_shape.Bounds.Height - 20;
            double newHeight = Bounds.Height + height_to_add;
            if (snapPoint.Equals(coord_then))
            {
                this.Then_connected_node = child_shape;

                // Change the bounds of the node according to the size of the child node
                if (lastInflatedNode != this)
                {
                    if (lastInflatedNode != null)
                        lastInflatedNode.Bounds = lastBounds;

                    Rect r = this.Bounds;
                    lastBounds = r;
                    this.Bounds = new Rect(r.X, r.Y, r.Width, r.Height + height_to_add);
                    lastInflatedNode = this;
                }
            }
            else if (snapPoint.Equals(coord_else))
            {
                this.Else_connected_node = child_shape;
            }
            else
                this.connected_node = child_shape;
        }
 



When I snap a node to the if-part the debug only shows the output "Draw() called!" and "Draw() finished!". It seems like it skips the calculation of the heights.. Help? Tongue

Vincent
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Autosnap diagram nodes if within certain distance?
Reply #26 - Oct 11th, 2013 at 11:44am
Print Post  
Hi,

This part looks a bit fishy, checking the same node for both heights:

double ifHeight = Then_connected_node != null ...
double elseHeight = Then_connected_node != null ...

Don't you also have an If_connected_node to check in first statement?

Stoyan
  
Back to top
 
IP Logged
 
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #27 - Oct 11th, 2013 at 11:51am
Print Post  
xD ...

Yup, that was it.  Embarrassed

Pretty sharp there, thanks!!  Cheesy

Vincent
  
Back to top
 
IP Logged
 
Vincent
Junior Member
**
Offline


I Love MindFusion!

Posts: 62
Joined: Oct 3rd, 2013
Re: Autosnap diagram nodes if within certain distance?
Reply #28 - Oct 11th, 2013 at 1:37pm
Print Post  
Hi Stoyan,

In an If node, if I add a node to the if-part it resizes fine but if I add a node to the else-part while the previously node is still there it doesn't redraw the shape anymore. But according to me, the method example you gave me for the override of Draw() always calculates whether the if-part and/or else-part are not null (I do assign them to those variables once the nodes snaps to that position). Any ideas?

Thanks in advance!

Vincent
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Autosnap diagram nodes if within certain distance?
Reply #29 - Oct 11th, 2013 at 1:45pm
Print Post  
You still need to update Bounds of the parent node when snapping child nodes to it, are you doing that? Parent's Bounds.Height should be equal to the sum of children heights + thrice the padding value.
  
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1 [2] 3 
Send TopicPrint