C# 三角学(或者可能是精度误差?)#
我正在尝试制作一个可以制作动画的程序——在屏幕上画一个木棍,允许用户移动手臂和腿,等等 但是,如果我想将两端相连的零件(或图形)移动到另一个零件,我也需要移动该零件,并在它们之间保持相同的角度,这样看起来会很逼真 我有这个代码(假设输入是正确的,也就是说运动部件的半径是相同的。) 问题是,在每次更新时,移动的零件(而不是基础零件)要么有其原始值(在基础零件之间),要么有一个不同的值,改变每一帧,尽管在两个帧中有一次它似乎工作正常 作为一个程序员,我可以跳过它,每帧做两次更新,但作为一个极客,我想知道我哪里出错了 谁有很好的几何知识,你能帮我吗 谢谢,马克。 图像(帧已计数):C# 三角学(或者可能是精度误差?)#,c#,math,geometry,C#,Math,Geometry,我正在尝试制作一个可以制作动画的程序——在屏幕上画一个木棍,允许用户移动手臂和腿,等等 但是,如果我想将两端相连的零件(或图形)移动到另一个零件,我也需要移动该零件,并在它们之间保持相同的角度,这样看起来会很逼真 我有这个代码(假设输入是正确的,也就是说运动部件的半径是相同的。) 问题是,在每次更新时,移动的零件(而不是基础零件)要么有其原始值(在基础零件之间),要么有一个不同的值,改变每一帧,尽管在两个帧中有一次它似乎工作正常 作为一个程序员,我可以跳过它,每帧做两次更新,但作为一个极客,我想
虽然这不是解决您问题的方法,但我想改变的是:
private double GetDistace(double x1, double y1, double x2, double y2)
{
return Math.Sqrt(Math.Pow(x1 - x2, 2) + Math.Pow(y1 - y2, 2));
}
为此:
private double GetDistace(double x1, double y1, double x2, double y2)
{
double xd = x1 - x2;
doubly yd = y1 - y2;
return Math.Sqrt(xd * xd + yd * yd);
}
它应该比Math.Pow
更快(可能)更准确。我知道这严格来说并不是对OPs问题的回答,但也许它可以证明是有趣的
所以我读了你的代码,试图找出问题所在,但我开始想:“我想知道OP真正需要的是a吗?”
现在我不是这方面的专家,但我想我可以展示一种构建复合视觉对象的不同方法
通过以这种方式构造对象,在旋转和平移时更容易建立和维护复杂的结构
首先定义一个抽象类形状
abstract class Shape
{
static readonly Shape[] s_emptyChildren = new Shape[0];
public Rect BoundingBox;
public Transform Transform;
public Shape[] Children = s_emptyChildren;
public void RenderShape (DrawingContext context)
{
context.PushTransform (Transform ?? Transform.Identity);
try
{
OnRenderShape (context);
foreach (var shape in Children ?? s_emptyChildren)
{
shape.RenderShape (context);
}
}
finally
{
context.Pop ();
}
}
protected abstract void OnRenderShape (DrawingContext context);
}
- Shape.BoundingBox是定义形状边界的矩形
- Children是所有内部形状的数组(在您的示例中,我认为您将其称为内部部分)
- Transform是一种转换,它指示此形状(及其子图形)如何与其父图形关联
- OnRenderShape实现者实现此方法来渲染形状
// Translates (Moves) Shape 100 "pixels" to right in relation to its parent
var translation = new TranslateTransform (100, 0);
// Rotates Shape 30 degrees clockwise in relation to its parent
var rotation = new RotateTransform (30);
// Composite first translates then rotates the Shape
var composite =
new TransformGroup
{
Children =
new TransformCollection
{
translation,
rotation ,
},
}
为了表达一个简单的复合对象,我们可以这样做:
static RectangleShape MakeSimpleShape()
{
return
new RectangleShape
{
BoundingBox = new Rect (-200, -200, 400, 400),
Pen = s_redPen,
Brush = null,
Children =
new Shape[]
{
new RectangleShape
{
BoundingBox = new Rect (-40, -40, 40, 40),
Transform = new TranslateTransform (100, 100),
},
},
};
}
如果您感兴趣的话,我做了一个完整的渲染示例(使用WPF)(MakeComplexShape基本上构建了一个递归形状到某个级别)
- 在VisualStudio中创建新的控制台应用程序
- 添加对WindowsBase、PresentationCore、PresentationFramework、System.Xaml的引用
- 将代码粘贴到Program.cs中
using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
abstract class Shape
{
static readonly Shape[] s_emptyChildren = new Shape[0];
public Rect BoundingBox;
public Transform Transform;
public Shape[] Children = s_emptyChildren;
public void RenderShape (DrawingContext context)
{
context.PushTransform (Transform ?? Transform.Identity);
try
{
OnRenderShape (context);
foreach (var shape in Children ?? s_emptyChildren)
{
shape.RenderShape (context);
}
}
finally
{
context.Pop ();
}
}
protected abstract void OnRenderShape (DrawingContext context);
}
sealed class RectangleShape : Shape
{
static readonly SolidColorBrush s_defaultBrush = new SolidColorBrush (Colors.Green).FreezeIfNecessary ();
public Pen Pen;
public Brush Brush = s_defaultBrush;
protected override void OnRenderShape (DrawingContext context)
{
context.DrawRectangle (Brush, Pen, BoundingBox);
}
}
static class Extensions
{
public static Color SetAlpha (this Color value, byte alpha)
{
return Color.FromArgb (alpha, value.R, value.G, value.B);
}
public static TValue FreezeIfNecessary<TValue>(this TValue value)
where TValue : Freezable
{
if (value != null && value.CanFreeze)
{
value.Freeze ();
}
return value;
}
}
class RenderShapeControl : FrameworkElement
{
public Shape Shape;
public Transform ShapeTransform;
protected override void OnRender (DrawingContext drawingContext)
{
if (Shape != null)
{
try
{
drawingContext.PushTransform (new TranslateTransform (ActualWidth / 2, ActualHeight / 2).FreezeIfNecessary ());
drawingContext.PushTransform (ShapeTransform ?? Transform.Identity);
Shape.RenderShape (drawingContext);
}
finally
{
drawingContext.Pop ();
drawingContext.Pop ();
}
}
}
}
public class MainWindow : Window
{
static readonly int[] s_childCount = new[] { 0, 5, 5, 5, 5, 5 };
static readonly Brush s_redBrush = new SolidColorBrush (Colors.Red.SetAlpha (0x80)).FreezeIfNecessary ();
static readonly Brush s_blueBrush = new SolidColorBrush (Colors.Blue.SetAlpha (0x80)).FreezeIfNecessary ();
static readonly Pen s_redPen = new Pen (Brushes.Red, 2).FreezeIfNecessary ();
static readonly Pen s_bluePen = new Pen (Brushes.Blue, 2).FreezeIfNecessary ();
static Shape MakeInnerPart (int level, int index, int count, double outerside, double angle)
{
var innerSide = outerside / 3;
return new RectangleShape
{
BoundingBox = new Rect (-innerSide / 2, -innerSide / 2, innerSide, innerSide),
Pen = index == 0 ? s_bluePen : s_redPen,
Brush = index == 0 && level > 0 ? s_redBrush : s_blueBrush,
Children = MakeInnerParts (level - 1, innerSide),
Transform =
new TransformGroup
{
Children =
new TransformCollection
{
new TranslateTransform (outerside/2, 0),
new RotateTransform (angle),
},
}.FreezeIfNecessary (),
};
}
static Shape[] MakeInnerParts (int level, double outerside)
{
var count = s_childCount[level];
return Enumerable
.Range (0, count)
.Select (i => MakeInnerPart (level, i, count, outerside, (360.0 * i) / count))
.ToArray ();
}
static RectangleShape MakeComplexShape ()
{
return new RectangleShape
{
BoundingBox = new Rect (-200, -200, 400, 400),
Pen = s_redPen,
Brush = null,
Children = MakeInnerParts (3, 400),
};
}
static RectangleShape MakeSimpleShape ()
{
return
new RectangleShape
{
BoundingBox = new Rect (-200, -200, 400, 400),
Pen = s_redPen,
Brush = null,
Children =
new Shape[]
{
new RectangleShape
{
BoundingBox = new Rect (-40, -40, 40, 40),
Transform = new TranslateTransform (100, 100),
},
},
};
}
readonly DispatcherTimer m_dispatcher;
readonly DateTime m_start = DateTime.Now;
readonly RenderShapeControl m_shapeRenderer = new RenderShapeControl ();
public MainWindow ()
{
AddChild (m_shapeRenderer);
m_dispatcher = new DispatcherTimer (
TimeSpan.FromSeconds (1 / 60),
DispatcherPriority.ApplicationIdle,
OnTimer,
Dispatcher
);
m_dispatcher.Start ();
m_shapeRenderer.Shape = MakeComplexShape ();
//m_shapeRenderer.Shape = MakeSimpleShape ();
}
void OnTimer (object sender, EventArgs e)
{
var diff = DateTime.Now - m_start;
var phase = (20 * diff.TotalSeconds) % 360.0;
m_shapeRenderer.ShapeTransform =
new TransformGroup
{
Children =
new TransformCollection
{
new TranslateTransform (100, 0),
new RotateTransform (phase),
},
}.FreezeIfNecessary ();
m_shapeRenderer.InvalidateVisual ();
}
}
class Program
{
[STAThread]
static void Main (string[] args)
{
var mainWindow = new MainWindow ();
mainWindow.ShowDialog ();
}
}
使用系统;
使用System.Linq;
使用System.Windows;
使用System.Windows.Media;
使用System.Windows.Threading;
抽象类形状
{
静态只读形状[]s_emptyChildren=新形状[0];
公共矩形边界框;
公共转型;
public Shape[]Children=s_emptyChildren;
公共空心渲染形状(DrawingContext上下文)
{
context.PushTransform(Transform??Transform.Identity);
尝试
{
OnRenderShape(上下文);
foreach(儿童中的变量形状??空儿童)
{
shape.RenderShape(上下文);
}
}
最后
{
Pop();
}
}
渲染形状上受保护的抽象无效(DrawingContext上下文);
}
密封类矩形形状:形状
{
静态只读SolidColorBrush s_defaultBrush=新的SolidColorBrush(Colors.Green);
公共钢笔;
公共画笔画笔=s_默认画笔;
受保护的覆盖在RenderShape上无效(DrawingContext上下文)
{
context.DrawRectangle(画笔、画笔、边框);
}
}
静态类扩展
{
公共静态颜色SetAlpha(此颜色值,字节alpha)
{
返回Color.FromArgb(alpha,value.R,value.G,value.B);
}
公共静态TValue冻结(如有必要)(此TValue值)
其中TValue:Freezable
{
if(value!=null&&value.CanFreeze)
{
value.Freeze();
}
返回值;
}
}
类RenderShapeControl:FrameworkElement
{
公共形态;
公共变换形状变换;
受保护的重写void OnRender(DrawingContext DrawingContext)
{
if(Shape!=null)
{
尝试
{
drawingContext.PushTransform(新的TranslateTransform(ActualWidth/2,ActualHeight/2).freezeIfEssential());
drawingContext.PushTransform(ShapeTransform??Transform.Identity);
Shape.RenderShape(drawingContext);
}
最后
{
Pop();
Pop();
}
}
}
}
公共类主窗口:窗口
{
静态只读int[]s_childCount=new[]{0,5,5,5,5};
静态只读画笔s_redBrush=新的SolidColorBrush(Colors.Red.SetAlpha(0x80)).freezeifEssential();
静态只读画笔s_blueBrush=新的SolidColorBrush(Colors.Blue.SetAlpha(0x80)).freezeifEssential();
静态只读画笔s_redPen=新画笔(brush.Red,2)。如有必要,冻结();
static readonly Pen s_bluePen=新笔(brush.Blue,2)。如有必要,冻结();
静态形状MakeInnerPart(整数级别、整数索引、整数计数、双外侧、双角度)
{
var innerSide=outerside/3;
返回新的矩形形状
{
BoundingBox=新矩形(-innerSide/2,-innerSide/2,innerSide,innerSide),
钢笔=索引==0?蓝色钢笔:红色钢笔,
画笔=索引==0&&level>0?s_红画笔:s_蓝画笔,
子项=MakeInnerParts(级别-1,内侧),
转化=
新转型集团
{
孩子们=
新变形金刚系列
using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;
abstract class Shape
{
static readonly Shape[] s_emptyChildren = new Shape[0];
public Rect BoundingBox;
public Transform Transform;
public Shape[] Children = s_emptyChildren;
public void RenderShape (DrawingContext context)
{
context.PushTransform (Transform ?? Transform.Identity);
try
{
OnRenderShape (context);
foreach (var shape in Children ?? s_emptyChildren)
{
shape.RenderShape (context);
}
}
finally
{
context.Pop ();
}
}
protected abstract void OnRenderShape (DrawingContext context);
}
sealed class RectangleShape : Shape
{
static readonly SolidColorBrush s_defaultBrush = new SolidColorBrush (Colors.Green).FreezeIfNecessary ();
public Pen Pen;
public Brush Brush = s_defaultBrush;
protected override void OnRenderShape (DrawingContext context)
{
context.DrawRectangle (Brush, Pen, BoundingBox);
}
}
static class Extensions
{
public static Color SetAlpha (this Color value, byte alpha)
{
return Color.FromArgb (alpha, value.R, value.G, value.B);
}
public static TValue FreezeIfNecessary<TValue>(this TValue value)
where TValue : Freezable
{
if (value != null && value.CanFreeze)
{
value.Freeze ();
}
return value;
}
}
class RenderShapeControl : FrameworkElement
{
public Shape Shape;
public Transform ShapeTransform;
protected override void OnRender (DrawingContext drawingContext)
{
if (Shape != null)
{
try
{
drawingContext.PushTransform (new TranslateTransform (ActualWidth / 2, ActualHeight / 2).FreezeIfNecessary ());
drawingContext.PushTransform (ShapeTransform ?? Transform.Identity);
Shape.RenderShape (drawingContext);
}
finally
{
drawingContext.Pop ();
drawingContext.Pop ();
}
}
}
}
public class MainWindow : Window
{
static readonly int[] s_childCount = new[] { 0, 5, 5, 5, 5, 5 };
static readonly Brush s_redBrush = new SolidColorBrush (Colors.Red.SetAlpha (0x80)).FreezeIfNecessary ();
static readonly Brush s_blueBrush = new SolidColorBrush (Colors.Blue.SetAlpha (0x80)).FreezeIfNecessary ();
static readonly Pen s_redPen = new Pen (Brushes.Red, 2).FreezeIfNecessary ();
static readonly Pen s_bluePen = new Pen (Brushes.Blue, 2).FreezeIfNecessary ();
static Shape MakeInnerPart (int level, int index, int count, double outerside, double angle)
{
var innerSide = outerside / 3;
return new RectangleShape
{
BoundingBox = new Rect (-innerSide / 2, -innerSide / 2, innerSide, innerSide),
Pen = index == 0 ? s_bluePen : s_redPen,
Brush = index == 0 && level > 0 ? s_redBrush : s_blueBrush,
Children = MakeInnerParts (level - 1, innerSide),
Transform =
new TransformGroup
{
Children =
new TransformCollection
{
new TranslateTransform (outerside/2, 0),
new RotateTransform (angle),
},
}.FreezeIfNecessary (),
};
}
static Shape[] MakeInnerParts (int level, double outerside)
{
var count = s_childCount[level];
return Enumerable
.Range (0, count)
.Select (i => MakeInnerPart (level, i, count, outerside, (360.0 * i) / count))
.ToArray ();
}
static RectangleShape MakeComplexShape ()
{
return new RectangleShape
{
BoundingBox = new Rect (-200, -200, 400, 400),
Pen = s_redPen,
Brush = null,
Children = MakeInnerParts (3, 400),
};
}
static RectangleShape MakeSimpleShape ()
{
return
new RectangleShape
{
BoundingBox = new Rect (-200, -200, 400, 400),
Pen = s_redPen,
Brush = null,
Children =
new Shape[]
{
new RectangleShape
{
BoundingBox = new Rect (-40, -40, 40, 40),
Transform = new TranslateTransform (100, 100),
},
},
};
}
readonly DispatcherTimer m_dispatcher;
readonly DateTime m_start = DateTime.Now;
readonly RenderShapeControl m_shapeRenderer = new RenderShapeControl ();
public MainWindow ()
{
AddChild (m_shapeRenderer);
m_dispatcher = new DispatcherTimer (
TimeSpan.FromSeconds (1 / 60),
DispatcherPriority.ApplicationIdle,
OnTimer,
Dispatcher
);
m_dispatcher.Start ();
m_shapeRenderer.Shape = MakeComplexShape ();
//m_shapeRenderer.Shape = MakeSimpleShape ();
}
void OnTimer (object sender, EventArgs e)
{
var diff = DateTime.Now - m_start;
var phase = (20 * diff.TotalSeconds) % 360.0;
m_shapeRenderer.ShapeTransform =
new TransformGroup
{
Children =
new TransformCollection
{
new TranslateTransform (100, 0),
new RotateTransform (phase),
},
}.FreezeIfNecessary ();
m_shapeRenderer.InvalidateVisual ();
}
}
class Program
{
[STAThread]
static void Main (string[] args)
{
var mainWindow = new MainWindow ();
mainWindow.ShowDialog ();
}
}
angle = angleof(part.XY relative part.baseXY) - angleof(this.baseXY relative this.XY)
angle = 2*pi - angle + angleof(this.XY relative this.baseXY)
angle = angleof(part.XY relative part.baseXY) + angleof(this.XY relative this.baseXY) - angleof(new.XY relative this.baseXY)
part.baseXY = new.XY
part.XY = part.baseXY + radius * cos/sin(angle)