今回のアプリのダウンロード先
ベジェ曲線のアンカーポイントと制御点と方向線を表示してみた
アンカーポイントと制御点を結ぶ直線のことを方向線っていうみたい
青丸がアンカーポイントと制御点、細い赤線が方向線
どちらもPathコン
トロールを使って表示している
アンカーポイントと制御点の●はそれぞれにPathを使って、そのDataにEllipseGeometryを指定して表示している
Path[0].Data.EllipseGeometry
Path[1].Data.EllipseGeometry
Path[2].Data.EllipseGeometry
Path[…].Data.EllipseGeometry
方向線は1つのPathだけですべてを表示していて、Path.DataのGeometryのPathFigureCollectionに方向線分のLineSegmentを入れている
Path.Data.PathGeometry.PathFigureCollection.LineSegment[0]
.LineSegment[1]
.LineSegment[2]
.LineSegment[…]
●も1つのPathで表示したほうが処理は軽くなるのかなあと思ったけど、ArcSegmentの使い方がめんどくさそうだったので●1つにつきPath1つにしてみた
他の方法だとGeometryをグループ化するGeometryGroupクラスってのがあるから、それを使って複数のEllipseGeometryをまとめてPathのDataに指定すればできるのかも
デザイン画面
コード
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Collections.Generic;
using System.Windows.Shapes;
namespace _20180612_ベジェ曲線のアンカーと制御点までの直線
{
public partial class MainWindow : Window
{
PolyBezierSegment MySegment;
PathFigureCollection MyControlLines;
List<EllipseGeometry> MyEllipseGeometry;
List<Path> MyListPathControl;
public MainWindow()
{
InitializeComponent();
MyInitialize();
MyCanvas.MouseLeftButtonDown += MyCanvas_MouseLeftButtonDown;
MyCanvas.MouseMove += MyCanvas_MouseMove;
MyCanvas.MouseRightButtonDown += MyCanvas_MouseRightButtonDown;
}
private void MyCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
MySegment.Points.Clear();
MyControlLines.Clear();
MyEllipseGeometry.Clear();
foreach (var item in MyListPathControl)
{
MyCanvas.Children.Remove(item);
}
MyListPathControl.Clear();
}
private void MyInitialize()
{
var ps = new PointCollection();
MySegment = new PolyBezierSegment(ps, true);
var pf = new PathFigure();
pf.Segments.Add(MySegment);
var pg = new PathGeometry();
pg.Figures.Add(pf);
MyPath.Data = pg;
MyControlLines = new PathFigureCollection();
pg = new PathGeometry();
pg.Figures = MyControlLines;
MyPath2.Data = pg;
MyEllipseGeometry = new List<EllipseGeometry>();
MyListPathControl = new List<Path>();
}
private void MyCanvas_MouseMove(object sender, MouseEventArgs e)
{
PointCollection ps = MySegment.Points;
if (MySegment.Points.Count > 5)
{
Point p = e.GetPosition(MyCanvas);
ps[ps.Count - 1] = p;
int ellipseCount = MyEllipseGeometry.Count;
MyEllipseGeometry[ellipseCount - 1].Center = p;
Point ap1 = ps[ps.Count - 4];
Point ap2;
if (ps.Count < 7)
{
var pg = (PathGeometry)MyPath.Data;
var pathFigureCollection = pg.Figures;
ap2 = pathFigureCollection[0].StartPoint;
}
else
{
ap2 = ps[ps.Count - 7];
}
double xDiff = (p.X - ap2.X) / 4.0;
double yDiff = (p.Y - ap2.Y) / 4.0;
ps[ps.Count - 3] = new Point(ap1.X + xDiff, ap1.Y + yDiff);
ps[ps.Count - 5] = new Point(ap1.X - xDiff, ap1.Y - yDiff);
MyEllipseGeometry[ellipseCount - 3].Center = ps[ps.Count - 3];
MyEllipseGeometry[ellipseCount - 5].Center = ps[ps.Count - 5];
int cpCount = MyControlLines.Count;
MyControlLines[cpCount - 2].StartPoint = ap1;
MyControlLines[cpCount - 2].Segments[0] = new LineSegment(ps[ps.Count - 3], true);
MyControlLines[cpCount - 3].StartPoint = ap1;
MyControlLines[cpCount - 3].Segments[0] = new LineSegment(ps[ps.Count - 5], true);
xDiff = (p.X - ps[ps.Count - 3].X) / 4.0;
yDiff = (p.Y - ps[ps.Count - 3].Y) / 4.0;
ps[ps.Count - 2] = new Point(p.X - xDiff, p.Y - yDiff);
MyEllipseGeometry[ellipseCount - 2].Center = ps[ps.Count - 2];
MyControlLines[cpCount - 1].StartPoint = p;
MyControlLines[cpCount - 1].Segments[0] = new LineSegment(ps[ps.Count - 2], true);
}
else if (MySegment.Points.Count > 0)
{
ps[ps.Count - 1] = e.GetPosition(MyCanvas);
}
}
private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var p = e.GetPosition(MyCanvas);
if (MySegment.Points.Count == 0)
{
var pg = (PathGeometry)MyPath.Data;
var pathFigureCollection = pg.Figures;
pathFigureCollection[0].StartPoint = p;
AddEllipse(p);
}
MySegment.Points.Add(p);
MySegment.Points.Add(p);
MySegment.Points.Add(p);
AddLineFigure(p, p);
AddLineFigure(p, p);
AddEllipse(p);
AddEllipse(p);
AddEllipse(p);
MyEllipseGeometry[MyEllipseGeometry.Count - 4].Center = p;
}
private void AddLineFigure(Point start, Point end)
{
var pf = new PathFigure();
pf.StartPoint = start;
pf.Segments.Add(new LineSegment(end, true));
MyControlLines.Add(pf);
}
private void AddEllipse(Point p)
{
EllipseGeometry ellipseGeometry = new EllipseGeometry(p, 3, 3);
MyEllipseGeometry.Add(ellipseGeometry);
Path path = new Path();
path.Fill = Brushes.Blue;
path.Data = ellipseGeometry;
MyCanvas.Children.Add(path);
MyListPathControl.Add(path);
}
}
}
前回の
ベジェ曲線描画に方向線とアンカーポイント、制御点の表示を足しただけなんだけど、
ずいぶん長くなってしまった
今回も動作をGIFアニメ(動画)にしようと20回くらい録画を試したけど、マウスの調子が悪くてたった5,6回の連続クリックができないwどうしても途中で勝手にダブルクリックになってアンカーポイントが重なるので、まともな動作を録画できなかった
コード
関連記事