【问题标题】:How to draw a Field of View of Camera using Circle in WPF?如何在 WPF 中使用 Circle 绘制相机视野?
【发布时间】:2017-11-27 04:46:07
【问题描述】:

我正在尝试使用 WPF 绘制弧线,但我不知如何做到这一点。我进行了很多搜索并通过了 WPF 的 Path 属性,但我发现这在我的情况下还不够好。 我的要求是在 wpf 上显示闭路电视摄像机的视野。

用户有以下输入: 1.圆的中心点。 2. 圆的半径。 3.角度(0-360)

我想允许用户绘制一个圆或一个完整的圆的区域,并且可以通过改变角度和半径来改变区域。 笔记: 1.中心是画布上的固定位置。 2.用户可以动态改变图形(区域)。

假设用户将提供 45 度角,那么圆将如下图所示:

【问题讨论】:

  • 您缺少一条信息。从您的图片中,Angle 确定了切片的“宽度”,但我看不到您在哪里提供切片的“方向”。什么决定了 FOV 是面向、向右、向左、向上、向下等?
  • @BradleyUffner;实际上在圆的中心,会有一个相机的图像,根据图表朝向东北方向。所以这个图中的 fov 朝向 UP-Right 并且角度将是逆时针方向
  • 我现在不打算弄清楚三角函数,但我很确定你可以通过组合多边形和椭圆来实现它。
  • 一个椭圆和一个三角形蒙版就可以了

标签: c# wpf


【解决方案1】:

在我的旧代数教科书上度过了一段美好的时光后,我为你找到了一个完全可行的解决方案。

这使用WpfLib Nuget 包(免责声明:我是这个包的作者)来支持INotifyPropertyChanged,但是您应该可以轻松地更改您想要的任何事件系统。完整的解决方案可以从BitBucket下载。

XAML:

<Window
    x:Class="ViewingAngle.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:ViewingAngle"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="525"
    Height="350"
    d:DataContext="{d:DesignInstance local:MainWindowVm}"
    mc:Ignorable="d">
    <DockPanel Margin="4">
        <StackPanel DockPanel.Dock="Bottom">
            <TextBlock Margin="4,4,4,0" Text="{Binding Path=FieldOfView, StringFormat='Field of View: {0}'}" />
            <Slider
                Margin="4"
                Maximum="360"
                Minimum="0"
                TickFrequency="5"
                TickPlacement="Both"
                Value="{Binding Path=FieldOfView}" />
            <TextBlock Margin="4,4,4,0" Text="{Binding Path=TargetAngle, StringFormat='Target Angle: {0}'}" />
            <Slider
                Margin="4"
                Maximum="360"
                Minimum="0"
                TickFrequency="5"
                TickPlacement="Both"
                Value="{Binding Path=TargetAngle}" />
        </StackPanel>

        <Viewbox Margin="4" Stretch="Uniform">
            <Canvas Width="500" Height="500">
                <Path
                    Fill="Blue"
                    Stroke="Blue"
                    StrokeThickness="1">
                    <Path.Data>
                        <PathGeometry>
                            <PathGeometry.Figures>
                                <PathFigure StartPoint="250,250">
                                    <LineSegment Point="{Binding Path=StartPoint}" />
                                    <ArcSegment
                                        IsLargeArc="{Binding Path=IsLargeArc}"
                                        Point="{Binding Path=EndPoint}"
                                        Size="250,250"
                                        SweepDirection="Clockwise" />
                                    <LineSegment Point="250,250" />
                                </PathFigure>
                            </PathGeometry.Figures>
                        </PathGeometry>
                    </Path.Data>
                </Path>
            </Canvas>
        </Viewbox>
    </DockPanel>
</Window>

视图模型:

using System;
using System.Windows;
using AgentOctal.WpfLib;

namespace ViewingAngle
{
    class MainWindowVm : ViewModel
    {
        private const int Radius = 250;
        private const int CenterX = 250;
        private const int CenterY = 250;


        public MainWindowVm()
        {
            FieldOfView = 45;
            TargetAngle = 90;
        }

        private int _fieldOfView;
        public int FieldOfView
        {
            get { return _fieldOfView; }
            set
            {
                SetValue(ref _fieldOfView, value);
                RecalculateArc();
            }
        }

        private int _targetAngle;
        public int TargetAngle
        {
            get { return _targetAngle; }
            set
            {
                SetValue(ref _targetAngle, value);
                RecalculateArc();
            }
        }

        private double GetRadians(int angle)
        {
            return angle * Math.PI / 180;
        }

        private void RecalculateArc()
        {
            var targetAngle = GetRadians(_targetAngle);
            var fieldOfView = GetRadians(_fieldOfView);
            var halfFieldOfView = fieldOfView / 2;

            var startAngle = targetAngle - halfFieldOfView;
            var endAngle = targetAngle + halfFieldOfView;
            double angleDiff = endAngle - startAngle;
            IsLargeArc = angleDiff >= Math.PI;

            StartPoint = new Point(CenterX + Radius * Math.Cos(startAngle), CenterY + Radius * Math.Sin(startAngle));
            EndPoint = new Point(CenterX + Radius * Math.Cos(endAngle), CenterY + Radius * Math.Sin(endAngle));
        }

        private Point _startPoint;
        public Point StartPoint
        {
            get { return _startPoint; }
            set { SetValue(ref _startPoint, value); }
        }

        private Point _endPoint;
        public Point EndPoint
        {
            get { return _endPoint; }
            set { SetValue(ref _endPoint, value); }
        }

        private bool _isLargeArc;
        public bool IsLargeArc
        {
            get { return _isLargeArc; }
            set { SetValue(ref _isLargeArc, value); }
        }
    }
}

所有的魔法都发生在RecalculateArc 中,每当TargetAngleFieldOfView 发生变化时都会调用它。所有的数学运算都必须以弧度进行,所以它做的第一件事就是转换值。它使用一些涉及正弦和余弦的相当简单的代数为ArcSegment 计算新的StartPointEndPoint 值(尽管我仍然需要查一下数学,因为谁在高中后还记得这些东西?)。

在 XAML 中,我放置了一些绑定到 TargetAngleFieldOfView 的滑块以允许您控制角度。它还包含绘制图形的Canvas,这是在ViewBox 内,只是为了使指标填充可用空间。

Canvas 包含一个Path,它由一个PathFigure 组成。 PathFigure 从 250,250(500 x 500 Canvas 的中心)开始,将LineSegment 绘制到弧的起点,该起点绑定到StartPoint。然后添加一个ArcSegment,以EndPoint 结束。 IsLargeArc 只是用来让绘图系统知道要绘制弧的哪“一半”。添加另一个在中心结束的LineSegment 以结束PathFigure

【讨论】:

  • @Bradley_Uffner 谢谢!
  • 如果用户想减小半径,你能建议我改变吗?
  • 您可以按照我的属性模式添加Radius 属性。并添加一些绑定来替换我代码中的硬编码半径。如果您遇到问题,请告诉我,我会在今天完成工作后更新。
  • 我正在添加一堆特征,包括多个 FOV、颜色、半径和位置。它应该很快就准备好了(我被赶上了,并且玩得很开心)。
  • 我刚刚使用新功能更新了 BitBucket 存储库。事情变得有点复杂,但如果您了解 MVVM 模式,应该不会太难理解。
【解决方案2】:

这是一个简单的方法。当然,你需要自己编写代码来使其动态化,但它很容易控制:在我的示例中,我放置了两个不同的图形,因此你将拥有两个“视野”:

    <Ellipse Fill="Blue" Width="100" Height="100">
        <Ellipse.Clip>
            <PathGeometry>
                <PathGeometry.Figures>
                    <PathFigure StartPoint="50,50" IsClosed="True">
                        <LineSegment Point="100,50"/>
                        <LineSegment Point="100,0"/>
                    </PathFigure>
                    <PathFigure StartPoint="50,50" IsClosed="True">
                        <LineSegment Point="80,0"/>
                        <LineSegment Point="30,0"/>
                    </PathFigure>
                </PathGeometry.Figures>
            </PathGeometry>
        </Ellipse.Clip>
    </Ellipse>

【讨论】:

  • 我想通过角度和半径执行相同的操作,但不使用三个不同的点...
  • 我认为 cosinussinus 函数就是为此而设计的 ^^。一个简单的转换器或一些数据绑定,以及一些非常简单的数学函数将提供一个稳定的组件。
猜你喜欢
  • 2011-01-28
  • 1970-01-01
  • 2017-02-20
  • 2014-05-27
  • 1970-01-01
  • 2018-05-12
  • 2016-04-24
  • 2017-01-11
  • 1970-01-01
相关资源
最近更新 更多