径向树图布局:修复贝塞尔曲线
Posted
技术标签:
【中文标题】径向树图布局:修复贝塞尔曲线【英文标题】:Radial tree graph layout: fix beizer curves 【发布时间】:2016-12-28 00:35:41 【问题描述】:我想渲染漂亮的径向树布局,但有点被弯曲的边缘绊倒了。问题在于源点和目标点之间的角度不同,边缘的绘制方式也不同。提供的图片来自单个图表,因此您可以看到它们对于不同的边缘方向有何不同。我认为关键在于生成贝泽曲线控制点,我只是不明白如何修复它们。
无论边缘的方向如何,我都希望它们以相同的方式绘制。
我怎样才能像在 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 计算的上一个顶点位置不准确,因此我决定将分支中所有边缘之间的平均角度作为 branchAngle。当一个分支的边缘在 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!!!很难计算一个平均值 :) 你能告诉我如何解决这个问题吗?
图片1
图2
图 3
图4
【问题讨论】:
【参考方案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)
;
快速检查 分支角度 = 30° & 补偿 = -90° -> 控制角度 = -60°
【讨论】:
感谢您的回答,但我仍然得到奇怪的结果,请参阅操作中的更新 1。 @Alexander 对此感到抱歉,在 dx 和 dy calc 中犯了一个错误。用 cosB 切换 sinB,反之亦然。 您的编辑似乎有错字,branchAngle 应该是Math.Atan2(prev.Y - source.Y, prev.X - source.X)
。
非常感谢!有用!虽然我有一个问题:请阅读更新 2。
@Alexander 好问题,为循环依赖赋予了新的含义:) 一个解决方案是在计算 branchAngle 之前取平均值。例如,只有 1 个点 1,假设有 5 个点 2。 Calc point2avg with x = (sum of all point2.x's) / 5 , y = (sum of all point2.y's) / 5。现在您可以根据 point1 和 point2avg 计算 branchAngle。以上是关于径向树图布局:修复贝塞尔曲线的主要内容,如果未能解决你的问题,请参考以下文章