径向树图布局:修复beizer曲线

时间:2016-08-21 09:51:20

标签: c# wpf layout graph visualization

我想呈现漂亮的径向树布局,并且有点弯曲的边缘。问题在于,源点和目标点之间的角度不同,边缘的绘制方式也不同。提供的图片来自单个图表,因此您可以看到它们对于不同的边缘方向有何不同。我认为关键在于beizer曲线控制点的生成,我只是无法理解如何修复它们。

无论边缘的方向是什么,我希望它们以相同的方式绘制。

我如何在Pic1中实现这一目标? 我如何在Pic2中实现这一目标?

像这里:https://bl.ocks.org/mbostock/4063550

谢谢!

代码:

//draw using DrawingContext of the DrawingVisual

//gen 2 control points
double dx = target.X - source.X, dy = target.Y - source.Y;
var pts = new[]
{
    new Point(source.X + 2*dx/3, source.Y),
    new Point(target.X - dx/8, target.Y - dy/8)
};

//get geometry
var geometry = new StreamGeometry { FillRule = FillRule.EvenOdd };
using (var ctx = geometry.Open())
{
    ctx.BeginFigure(START_POINT, false /* is filled */, false /* is closed */);
    ctx.BezierTo(pts[0], pts[1], END_POINT, true, false);
}
geometry.Freeze();

//draw it
dc.DrawGeometry(DrawingBrush, DrawingPen, geometry);

更新1: 我使用以下公式得到前一个顶点和源之间的角度: Math.Atan2(prev.Y - source.Y,source.X - prev.X); 但我仍然可以像Pic.4中那样获得优势。

更新2 branchAngle 计算的prev vertex pos是不准确的,所以我决定采用分支中所有边缘之间的平均角度作为 branchAngle 。当一个brach的边缘在180度标记附近并且分支可以具有像175,176的边缘角度时,这种方法失败了。-176!我使用这段代码使它们都是正面的:

        var angle =  Math.Atan2(point1.Y - point2.Y, point1.X - point2.X);
        while (angle < 0d)
            angle += Math.PI*2;

但现在角度可以是350,359 .. 2 !!!非常难以计算平均值:)你能告诉我如何解决这个问题吗?

PIC1 Beizer curve edges - up

PIC2 Beizer curve edges - left

PIC3 enter image description here

PIC4 enter image description here

1 个答案:

答案 0 :(得分:1)

从您提供的链接查看图表,树中的每个分支都有自己的角度,用于声明分支的控制点。此branchAngle与从第一个节点到前一个节点的向量相同(每个分支可以依次生成多个分支)。第一个分支(第一个节点=前一个节点=中心)的角度似乎在-60°左右。

设置曲线类型可以通过补偿树中所有分支的分支角度(0°, - 90°,-180°,...)来完成。导致用于布置控制点的controlAngle

在考虑角度的同时生成控制点:

//gen per branch
 double branchAngle = 30 * Math.PI / 180; //e.g., calc vector angle here
 double cosB = Math.Cos(branchAngle);
 double sinB = Math.Sin(branchAngle);     
 //depending on the desired curve compensate -90°, -180°,...
 double controlAngle = branchAngle - (90 * Math.PI / 180); 
 double cosA = Math.Cos(controlAngle);
 double sinA = Math.Sin(controlAngle);

//gen 2 control points
 //calculate dx dy after rotation with branchAngle
 double dxbase = target.X - source.X, dybase = target.Y - source.Y;
 double dx = dxbase*sinB - dybase*cosB
 double dy = dxbase*cosB + dybase*sinB
 //control points based on controlAngle
 var pts = new[]
 {
    new Point(source.X + (2*dx/3)*cosA , source.Y + (2*dx/3)*sinA),
    new Point(target.X - (dx/8)*cosA + (dy/8)*sinA, target.Y - (dx/8)*sinA - (dy/8)*cosA)
 };

Branch

快速检查 branchAngle = 30°&amp; 补偿= -90° - &gt; controlAngle = -60°