4日前からの続きで今度は曲線Pathの長さを測る
GetFlattenedPathGeometryとGetPointAtFractionLengthを使って測る
半径50の円周で確認
2*3.14*50=341になればいい
ベジェ曲線の長さ測定
966.3と測定された
だいたいあっているはず
公差と分割数
GetFlattenedPathGeometryに渡すTolerance(公差)が102の場合
なのでこれを細かく測定しても
公差0で丁寧な近似直線でも分割数が少ないと
これもかなり不正確
測定値は赤の直線の長さの合計
より正確に測るには
GetFlattenedPathGeometryに渡す引数Tolerance(公差)は0を指定して
分割数を増やしてGetPointAtFractionLengthで細かく測定する
分割数をより増やしてみたら
972まで増やしたら966.7になった
200のときでも966.3だったので、あんまり変わらない
もっと長く複雑な曲線なら増やしたほうが良さそうだけど
この程度の曲線なら200で十分かな
デザイン画面
C#コード
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
//曲線のPathをGetFlattenedPathGeometryで近似直線Pathに変換して
//それをGetPointAtFractionLengthで分割した頂点同士の距離を合計する
namespace _20180614_曲線の長さを測る
{
public partial class MainWindow : Window
{
PathGeometry FlattenedPathGeometry;//曲線の近似直線に変換したPathGeometry
List<Path> StepEllipseList = new List<Path>();//分割したところの○印
List<Path> MyListLine = new List<Path>();//○印を結ぶ直線Path
public MainWindow()
{
InitializeComponent();
FlattenedPathGeometry = MyPath.Data.GetFlattenedPathGeometry();
FlattendeLine.Data = FlattenedPathGeometry;
SliderStep.ValueChanged += SliderStep_ValueChanged;
SliderTolerance.ValueChanged += SliderTolerance_ValueChanged;
SliderStep.MouseWheel += Slider_MouseWheel;
SliderTolerance.MouseWheel += Slider_MouseWheel;
}
//マウスホイール回したとき
private void Slider_MouseWheel(object sender, MouseWheelEventArgs e)
{
Slider slider = (Slider)sender;
if (e.Delta > 0) { slider.Value += slider.SmallChange; }
else { slider.Value -= slider.SmallChange; }
}
//公差変更時
private void SliderTolerance_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
ChangeFlattendePath();
Measure();
}
//分割数変更時
private void SliderStep_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Measure();
}
//長さ測定
private void Measure()
{
ClearStepEllipse();
var pg = (PathGeometry)MyPath.Data;
Point p1 = pg.Figures[0].StartPoint;
Point p2;
double pathLength = 0;//長さ測定用
double step = SliderStep.Value;
for (int i = 0; i < step + 1; i++)
{
//○印の作成と追加
//第1引数は0から1を指定、0がPathの始点になり1は終点、0.5なら中間位置を取得できる
FlattenedPathGeometry.GetPointAtFractionLength(i / step, out Point p, out Point pt);
var ellipseGeo = new EllipseGeometry(p, 4, 4);
var path = new Path();
path.Stroke = Brushes.Blue;
path.Data = ellipseGeo;
MyGrid.Children.Add(path);
StepEllipseList.Add(path);
//長さ測定
p2 = p;
pathLength += Distance(p1, p2);
//測定対象の2頂点を結ぶ直線Pathの作成と追加
var lineG = new LineGeometry(p1, p2);
p1 = p;
path = new Path();
path.Stroke = Brushes.Red;
path.Data = lineG;
MyGrid.Children.Add(path);
MyListLine.Add(path);
}
//表示更新
TextBlockMeasure.Text =
"測定値 = " + Math.Round(pathLength, 1, MidpointRounding.AwayFromZero).ToString();
}
//2点間の距離、ユークリッド距離
private double Distance(Point p1, Point p2)
{
return Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2));
}
//表示をクリア
private void ClearStepEllipse()
{
foreach (var item in StepEllipseList)
{
MyGrid.Children.Remove(item);
}
StepEllipseList.Clear();
foreach (var item in MyListLine)
{
MyGrid.Children.Remove(item);
}
MyListLine.Clear();
}
//近似直線の更新
private void ChangeFlattendePath()
{
//第1引数Toleranceは0以上を指定、0が一番曲線に近く正確だけど計算量が増える
FlattenedPathGeometry =
MyPath.Data.GetFlattenedPathGeometry(SliderTolerance.Value, ToleranceType.Absolute);
FlattendeLine.Data = FlattenedPathGeometry;
}
}
}
もっと正確に測りたいときはGetFlattenedPathGeometryで得た近似直線のPathGeometryから直接測ればいいけど、めんどくさそう
ベジェ曲線をGetFlattenedPathGeometryで
公差=0を指定で得られた近似直線のPathGeometryの頂点座標数は4096!これを計算すればかなり正確になるんだろうけど多すぎるw
だったら公差の値を大きくすればいいけどそれなら分割数で指定しても同じかなと
それに今回はPolyLineSegmentで得られたけど、元のPathによってはLineSegmentとかになることもあったり、同じ座標が何回も出てきたりすることもあったので、近似直線のPathGeometryから直接測定するのはめんどくさそうだったので、やっぱりGetPointAtFractionLengthで分割がラクでいいかなあ
Segment数が9でこの中にPolyLineSegmentとLineSegmentが交互に混じっていたはず
パスマークアップ構文で円を描く
半径50の円の場合は
PathGeometry Figures="M100,100 A50,50 0 1 1 100,200 A50,50 0 1 1 100,100"
パスマークアップ構文に円はないので、円弧を2つ組み合わせて書く
"M100,100 A50,50 0 1 1 100,200"
Mは始点、Aからが円弧で
50,50が半径のx,y
0回転角度
1よくわからんけど1か0を指定する
1よくわからん
100,200終点のx,y
これで右半分描けたので
左半分を足して
"M100,100 A50,50 0 1 1 100,200 A50,50 0 1 1 100,100"
左半分は終点が100,100になるだけで右半分と同じでいいみたい
これで円になる
なった
参照したところ
GetPointAtFractionLength – digitalnetbizz's WebLog
https://blogs.msdn.microsoft.com/digitalnetbizz/2007/04/05/getpointatfractionlength/
C#のWPFでWaitingCircleコントロールを作る - Ararami Atudioパスマークアップ構文で円を描くのはここがわかりやすかった
https://araramistudio.jimdo.com/2016/11/24/wpf%E3%81%A7waitingcircle%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E3%82%92%E4%BD%9C%E3%82%8B/
コード
関連記事
2018/6/14
曲線Pathを近似の直線PathにするGetFlattenedPathGeometry使ってみた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15549065.html
2018/6/15
Pathを等分したところに印と角度を表示してみた、GetPointAtFractionLength ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15550562.html
2018/6/16
GetPointAtFractionLengthで分割した座標からのPathの長さ測定の確認 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15551929.html