缩放DrawingVisuals算法

时间:2014-08-27 12:38:53

标签: c# algorithm

我正在尝试实现一种算法,该算法可以找到最佳的比例系数和最佳可能的角度来定位图形,使其不与容器的边缘重叠并且它采用最佳角度(由图形尽可能宽的角度定义。我使用DrawingVisual来表示图形。

我现在想到的是一个强力检查,看起来像这样:

set scale to 1;
while (overlaps with end points(in set (verticies) { !exists vertex which position.x > window.width && position.y > window.height && position.X < 0 && position.Y < 0)){
for 0 : 360 {
   check whether at this angle all the points are aligned correctly,if yes
   save the result as the last valid tuple(angle and scale) and continue
   }
   increase scale by a constant 
}

如果有人知道已经在线提供此类实施,请分享,否则请告诉我您对我目前正在考虑的内容的看法。

编辑:您始终可以认为图形的中心点位于屏幕的中心。

2 个答案:

答案 0 :(得分:1)

如果这是你想要弄清楚的

box

然后答案(弧度)是

// Width W is the limit
θ = Math.Atan(h/w)+Math.Acos(W/Math.Sqrt(h*h+w*w));
// Height H is the limit (like picture)
θ = Math.Asin(H/Math.Sqrt(h*h+w*w))-Math.Atan(h/w);

检查最终答案中哪个Math.Abs(θ)最小。

答案 1 :(得分:1)

编辑我已编辑帖子并提出另一种解决方案。

“数字”和“容器”的边缘是不是很清楚。我假设容器是一种带有轴对齐矩形作为表面的画布。

然后你可以通过三个步骤来拟合这个数字:

  • 确定数字的convex hull。当然,这取决于你的身材的性质,但如果你的身材已经是一个多边形,这就像丢弃所有凹点一样简单。

  • 找到最佳旋转角度,见下文。 (我之前曾建议使用rotating calipers算法找到带最小面积的封闭矩形,但这并不能提供最佳解决方案。)

  • 通过缩放和翻译将旋转的图形适合您的容器。

最佳旋转角度是旋转图形的轴对齐边界框的纵横比最接近容器纵横比的角度。您对蛮力解决方案的想法是一个良好的开端,但可以进行改进:

  • 您不必成功增加规模;一旦找到了最佳角度,您就可以根据旋转图形的边界框和容器的尺寸轻松计算尺度。

  • 您无需测试高达360°的所有角度。检查四分之一圆就足够了:如果θ是一个解,那么θ+ 180°是相同的解,只能旋转。您可以通过检查θ - 90°的边界框来检查90°到180°范围内的解决方案,并更换高度和宽度。

  • 通过以一度的步长探测解决方案的范围,您将得到一个粗略的解决方案。更好的方法可能是通过减小角度步长探测更小的角度范围。 (您可以根据所需的准确度和性能拟合搜索间隔的精确值。性能应该没问题,因为凸包通常只有几个点。)

  • 无需围绕屏幕中心旋转。只需围绕原点旋转,并在最后一步中使多边形适合容器。

这是Javascript中的一个实现。 (您要求使用C#,但我对此并不熟悉。尽管如此,您可以毫不费力地将此代码用于其他语言)

/*
 *      Return left, right, top and bottom points of a polygon
 */
function bounds(poly) {
    bx = { 
        left: poly[0],
        right: poly[0],
        top: poly[0],
        bottom: poly[0]
    };

    for (var i = 1; i < poly.length; i++) {
        var p = poly[i];

        if (p.x < bx.left.x) bx.left = p;
        if (p.x > bx.right.x) bx.right = p;
        if (p.y < bx.top.y) bx.top = p;
        if (p.y > bx.bottom.y) bx.bottom = p;
    }

    return bx;
}    

/*
 *      Return a transformed (rotated, scaled, offset) polygon 
 */
function transform(poly, deg, scale, dx, dy) {
    var phi = Math.PI * deg / 180;
    var c = Math.cos(phi);
    var s = Math.sin(phi);

    var rot = [];

    for (var i = 0; i < poly.length; i++) {
        var p = poly[i];
        var x = scale * (c * p.x - s * p.y + dx);
        var y = scale * (s * p.x + c * p.y + dy);
        rot.push(new Pt(x, y));
    }

    return rot;
}

/*
 *      Return a polygon rotated by deg degrees.
 */
function rotate(poly, deg) {
    return transform(poly, deg, 1, 0, 0);
}

/*
 *      Assess a rotation of the polygon and update the optimum
 *      solution so far if necessary.
 */
function assess(opt, poly, angle, ratio) {
    var rot = rotate(poly, angle);
    var box = bounds(rot);
    var w = box.right.x - box.left.x;
    var h = box.bottom.y - box.top.y;
    var r = w / h;

    if (Math.abs(r - ratio) < opt.delta) {
        opt.delta = Math.abs(r - ratio);
        opt.angle = angle;
        opt.width = w;
        opt.height = h;
        opt.left = box.left.x;
        opt.top = box.top.y;
    }

    if (Math.abs(1/r - ratio) < opt.delta) {
        opt.delta = Math.abs(1/r - ratio);
        opt.angle = angle + 90;
        opt.width = h;
        opt.height = w;
        opt.left = -box.bottom.y;
        opt.top = box.left.x;
    }
}

/*
 *      Fit polygon inside a rectangle of width x height
 */
function fit(poly, width, height) {
    var ratio = width / height;
    var opt = { delta: 1.0e+30 };

    for (var i = 0; i < 90; i += 10) assess(opt, poly, i, ratio);

    for (d = 1; d > 0.002; d *= 0.1) {
        var a = opt.angle;

        for (var i = a - 9*d; i < a + 9.5*d; i += d) {
            assess(opt, poly, i, ratio);
        }
    }

    var sx = width / opt.width;
    var sy = height / opt.height;

    return transform(poly, 
        opt.angle, Math.min(sx, sy),
        -opt.left, -opt.top);
}  

这种迭代方法可能并不总能找到 最佳解决方案,但它会找到一个足够好的解决方案。