Page Index Toggle Pages: 1 Send TopicPrint
Normal Topic Link label position (Read 5334 times)
tom_w
Junior Member
**
Offline


Swimlanes made our dreams
come true :-)

Posts: 79
Joined: Oct 20th, 2008
Link label position
Apr 19th, 2012 at 12:04pm
Print Post  
Hi

Some questions on link labels I'm hoping you can help with:

1) We have some multi-line labels and these labels don't appear at the middle of their links.  It looks like the label is anchored on the bottom of the text area rather than the text centre's vertical centre.  Is there anyway to change this?

2) We also have a lot of diagrams where links cross and the labels end up on top of each other.  Is there any simple way to bring the link being hovered over to the top of the z order and also to set that link's text label back colour to a solid colour so you can no longer see the other labels beneath it?

3) In general, is there anything that can be done to stop the labels being rendered on top of each other?  Ideally I guess that once the diagram has been laid out there would then be a routine that looped through the links and moved the labels about until they were no longer on top of each other.  As far as I can see we have very little control over the labels, so I don't think this is something that can be done in code by us? 

Don't want much do we...!
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Link label position
Reply #1 - Apr 19th, 2012 at 5:49pm
Print Post  
Hi,

1,2) You could try the various link.TextStyle values, or draw labels yourself from the DrawLink event. Of the standard styles, only LinkTextStyle.OverLongestSegment paints labels over solid background - you could use it along with this code to bring the pointed link to the top and hide background texts:

Code
Select All
private void diagramView_MouseMove(object sender, MouseEventArgs e)
{
	PointF mousePos = diagramView.ClientToDoc(e.Location);
	DiagramLink link = diagram.GetLinkAt(mousePos, 3);
	if (link != null)
	{
		link.ZTop();
		diagram.Invalidate();
	}
} 



You might have to disable SelectionOnTop, or otherwise selected links will still be drawn at the top of Z order.

I hope that helps,
Stoyan
  
Back to top
 
IP Logged
 
Stoyo
God Member
*****
Offline


MindFusion support

Posts: 13230
Joined: Jul 20th, 2005
Re: Link label position
Reply #2 - Apr 20th, 2012 at 7:59am
Print Post  
3) If you use attached nodes as link labels, arranging them to avoid overlapping labels and other nodes could look as below. If you need to rearrange all labels (e.g. after applying a layout algorithm), you'd better remove all label nodes and add them again, so that the area previously occupied by label nodes can be reused. This all can be done with custom drawn labels too, but then you will have to check both nodes locations and labels locations in the code, and you must store a label's location somewhere.

I hope that helps,
Stoyan

Code
Select All
private void diagram_LinkModified(object sender, LinkEventArgs e)
{
	ArrangeLabel(e.Link);
}

private void diagram_NodeModified(object sender, NodeEventArgs e)
{
	foreach (DiagramLink link in e.Node.GetAllLinks())
		ArrangeLabel(link);
}

private void diagram_LinkCreated(object sender, LinkEventArgs e)
{
	string labelText = "link label\nline 2\nline 3";
	SizeF size = diagram.MeasureString(
		labelText, diagram.Font, int.MaxValue, new StringFormat());

	ShapeNode label = new ShapeNode(diagram);
	label.Bounds = e.Link.GetBounds();
	label.Text = labelText;
	label.Resize(size.Width + 3, size.Height + 3);
	label.Obstacle = false;
	label.IgnoreLayout = true;
	label.Locked = true;
	diagram.Nodes.Add(label);

	label.ZTop();
	label.AttachTo(e.Link, AttachToLink.Point, 0);
	ArrangeLabel(e.Link);
}

class Vector
{
	public Vector(float x, float y)
	{
		X = x;
		Y = y;
	}

	public Vector(PointF p1, PointF p2)
	{
		X = p2.X - p1.X;
		Y = p2.Y - p1.Y;
	}

	public float Length
	{
		get { return (float)Math.Sqrt(X * X + Y * Y); }
	}

	static public Vector operator*(Vector v, float factor)
	{
		return new Vector(v.X * factor, v.Y * factor);
	}

	static public PointF operator+ (PointF p, Vector v)
	{
		return new PointF(p.X + v.X, p.Y + v.Y);
	}

	public float X;
	public float Y;
}

void ArrangeLabel(DiagramLink link)
{
	DiagramNode label = link.SubordinateGroup.AttachedNodes[0];

	// the label should stay inside the diagram
	RectangleF bounds = diagram.Bounds;
	bounds.Inflate(-label.Bounds.Width / 2, -label.Bounds.Height / 2);

	float bestEval = float.MaxValue;
	float step = 4f;	// millimeters
	PointF bestPoint = label.GetCenter();

	// for all link segments
	for (int i = 0; i < link.SegmentCount; ++i)
	{
		// get the segment key points
		PointF start = link.ControlPoints[i];
		PointF end = link.ControlPoints[i + 1];
		PointF middle = InternalUtils.MidPoint(start, end);

		// and the segment direction vector
		Vector segmentDir = new Vector(start, end);
		int pointsToTry = (int)(segmentDir.Length / step);
		segmentDir = segmentDir * (step / segmentDir.Length);

		// we don't like short segments
		float shortSegmentPenalty = pointsToTry > 7 ? 0 :
			pointsToTry > 3 ? 25 : 50;

		// for every point at distance "step" along the segment
		foreach (PointF point in PointsAlongVector(start, segmentDir, pointsToTry))
		{
			// we prefer points close to the segment center
			float penalty = shortSegmentPenalty + Utilities.Distance(point, middle);
			float eval = EvalPos(label, point, penalty);
			if (eval < bestEval && bounds.Contains(point))
			{
				bestPoint = point;
				bestEval = eval;
			}
		}
	}

	label.Move(
		bestPoint.X - label.Bounds.Width / 2,
		bestPoint.Y - label.Bounds.Height / 2);
}

IEnumerable<PointF> PointsAlongVector(PointF start, Vector vector, int points)
{
	for (int i = 0; i <= points; ++i)
	{
		yield return start;
		start += vector;
	}
}

float EvalPos(DiagramNode label, PointF point, float segmentPenalty)
{
	RectangleF rect = label.Bounds;
	rect.Location = point;
	rect.Offset(-rect.Width / 2, -rect.Height / 2);

	// leave a margin so label is not placed very near to other nodes
	rect.Inflate(10, 10);

	float worstCase = 0;
	foreach (DiagramNode node in diagram.Nodes)
	{
		if (node == label)
			continue;
		if (rect.IntersectsWith(node.Bounds))
		{
			// use the area of the intersection that would occur
			// if the label is placed here as evaluation how good the location is
			RectangleF intersection = RectangleF.Intersect(rect, node.Bounds);
			float area = intersection.Width * intersection.Height;
			worstCase = Math.Max(worstCase, area);
		}
	}

	// add the segment's penalty to the total cost so we prefer
	// points near the segment center, avoid short segments, etc
	return worstCase + segmentPenalty;
}
 

  
Back to top
 
IP Logged
 
tom_w
Junior Member
**
Offline


Swimlanes made our dreams
come true :-)

Posts: 79
Joined: Oct 20th, 2008
Re: Link label position
Reply #3 - Apr 20th, 2012 at 2:43pm
Print Post  
That all looks really interesting, I will try it over the weekend and report back.  Thanks for your help.
  
Back to top
 
IP Logged
 
tom_w
Junior Member
**
Offline


Swimlanes made our dreams
come true :-)

Posts: 79
Joined: Oct 20th, 2008
Re: Link label position
Reply #4 - Apr 29th, 2012 at 12:38pm
Print Post  
Hi Stoyan

Apologies for the delay.  I finally got a chance to look at this code properly, both parts look great.  The second solution isn't working perfectly for me at the moment as the labels are being drawn some distance from the links, however this may well be due to my having to translate the code to VB or something else in my code.  I will try and work out what's malfunctioning.

The first part works perfectly, so in the meantime that has solved the problem.

Thanks for your help.
  
Back to top
 
IP Logged
 
Page Index Toggle Pages: 1
Send TopicPrint