ArcSegmentを使って円弧🌙、🍕パイ形、🍩ドーナツ型(アーチ形)を表示してみた
名前は円弧と扇形はいいと思うけど
ドーナツ型なのかアーチ形なのか、また別の名前があるのかも
でも🍩がいいなあってことで関数名は
Donutにした
MainWindow.
xaml.cs
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
namespace _20190331_円弧arcSegment
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
double radian = 50;
Point center = new Point(radian, radian);
double distance = 50;
PathGeometry arcPathGeo;
arcPathGeo = ArcGeometry(center, distance, 0, 250, SweepDirection.Clockwise);
MyStackPanel1.Children.Add(MakePath(arcPathGeo));
arcPathGeo = ArcGeometry(center, 25, 0, 250, SweepDirection.Clockwise);
MyStackPanel1.Children.Add(MakePath(arcPathGeo));
arcPathGeo = ArcGeometry(new Point(100, 100), distance, 0, 250, SweepDirection.Clockwise);
MyStackPanel1.Children.Add(MakePath(arcPathGeo));
arcPathGeo = ArcGeometry(new Point(100, 100), 25, 0, 250, SweepDirection.Clockwise);
MyStackPanel1.Children.Add(MakePath(arcPathGeo));
arcPathGeo = ArcGeometry(center, radian, 100, 330, SweepDirection.Counterclockwise);
MyStackPanel1.Children.Add(MakePath(arcPathGeo));
PathGeometry piePahtGeo;
piePahtGeo = PieGeometry(center, distance, 330, 30, SweepDirection.Clockwise);
MyStackPanel1.Children.Add(MakePath(piePahtGeo));
piePahtGeo = PieGeometry(center, distance, 330, 30, SweepDirection.Counterclockwise);
MyStackPanel1.Children.Add(MakePath(piePahtGeo));
PathGeometry donut;
donut = DonutGeometry(center, 20, distance, 20, 300, SweepDirection.Clockwise);
MyStackPanel1.Children.Add(MakePath(donut));
donut = DonutGeometry(center, 20, distance, 20, 300, SweepDirection.Counterclockwise);
MyStackPanel1.Children.Add(MakePath(donut));
}
private Path MakePath(PathGeometry geo)
{
Path path;
path = new Path
{
Data = geo,
Stroke = Brushes.PaleVioletRed,
StrokeThickness = 4,
Margin = new Thickness(4)
};
return path;
}
<summary>
</summary>
<param name="center"></param>
<param name="width"></param>
<param name="distance"></param>
<param name="startDeg"></param>
<param name="stopDeg"></param>
<param name="direction"></param>
<returns></returns>
private PathGeometry DonutGeometry(Point center, double width, double distance, double startDeg, double stopDeg, SweepDirection direction)
{
Point outSideStart = MakePoint(startDeg, center, distance);
Point outSideStop = MakePoint(stopDeg, center, distance);
Point inSideStart = MakePoint(stopDeg, center, distance - width);
Point inSideStop = MakePoint(startDeg, center, distance - width);
double diffDegrees = (direction == SweepDirection.Clockwise) ? stopDeg - startDeg : startDeg - stopDeg;
if (diffDegrees < 0) { diffDegrees += 360.0; }
bool isLarge = (diffDegrees > 180) ? true : false;
var outSideArc = new ArcSegment(outSideStop, new Size(distance, distance), 0, isLarge, direction, true);
var inDirection = (direction == SweepDirection.Clockwise) ? SweepDirection.Counterclockwise : SweepDirection.Clockwise;
var inSideArc = new ArcSegment(inSideStop, new Size(distance - width, distance - width), 0, isLarge, inDirection, true);
var fig = new PathFigure();
fig.StartPoint = outSideStart;
fig.Segments.Add(outSideArc);
fig.Segments.Add(new LineSegment(inSideStart, true));
fig.Segments.Add(inSideArc);
fig.Segments.Add(new LineSegment(outSideStart, true));
fig.IsClosed = true;
var pg = new PathGeometry();
pg.Figures.Add(fig);
return pg;
}
<summary>
</summary>
<param name="center"></param>
<param name="distance"></param>
<param name="startDegrees"></param>
<param name="stopDegrees"></param>
<param name="direction"></param>
<returns></returns>
private PathGeometry ArcGeometry(Point center, double distance, double startDegrees, double stopDegrees, SweepDirection direction)
{
Point stop = MakePoint(stopDegrees, center, distance);
double diffDegrees = (direction == SweepDirection.Clockwise) ? stopDegrees - startDegrees : startDegrees - stopDegrees;
if (diffDegrees < 0) { diffDegrees += 360.0; }
bool isLarge = (diffDegrees > 180) ? true : false;
var arc = new ArcSegment(stop, new Size(distance, distance), 0, isLarge, direction, true);
var fig = new PathFigure();
Point start = MakePoint(startDegrees, center, distance);
fig.StartPoint = start;
fig.Segments.Add(arc);
var pg = new PathGeometry();
pg.Figures.Add(fig);
return pg;
}
<summary>
</summary>
<param name="center"></param>
<param name="distance"></param>
<param name="startDegrees"></param>
<param name="stopDegrees"></param>
<param name="direction"></param>
<returns></returns>
private PathGeometry PieGeometry(Point center, double distance, double startDegrees, double stopDegrees, SweepDirection direction)
{
Point start = MakePoint(startDegrees, center, distance);
Point stop = MakePoint(stopDegrees, center, distance);
double diffDegrees = (direction == SweepDirection.Clockwise) ? stopDegrees - startDegrees : startDegrees - stopDegrees;
if (diffDegrees < 0) { diffDegrees += 360.0; }
bool isLarge = (diffDegrees > 180) ? true : false;
var arc = new ArcSegment(stop, new Size(distance, distance), 0, isLarge, direction, true);
var fig = new PathFigure();
fig.StartPoint = start;
fig.Segments.Add(arc);
fig.Segments.Add(new LineSegment(center, true));
fig.Segments.Add(new LineSegment(start, true));
fig.IsClosed = true;
var pg = new PathGeometry();
pg.Figures.Add(fig);
return pg;
}
<summary>
</summary>
<param name="degrees"></param>
<param name="center"></param>
<param name="distance"></param>
<returns></returns>
private Point MakePoint(double degrees, Point center, double distance)
{
if (degrees >= 360) { degrees = 359.99; }
var rad = Radian(degrees);
var cos = Math.Cos(rad);
var sin = Math.Sin(rad);
var x = center.X + cos * distance;
var y = center.Y + sin * distance;
return new Point(x, y);
}
private double Radian(double degree)
{
return Math.PI / 180.0 * degree;
}
}
}
ArcSegment
public ArcSegment (
point, 円弧の終点
size, 円弧の半径
rotationAngle, 回転?今回は0で固定
isLargeArc, 円弧が180度を超えるかどうか
sweepDirection, Clockwiseが時計回り、Counterclockwiseが反時計回り
isStroked 描くかどうか、今回はtrueで固定);
このArcSegmentを使って
円弧のPathGeometryを作成するArcGeometry
指定するのは
中心座標、
中心座標からの距離(半径)、
開始角度、
終了角度、
回転方向
半径 50
中心座標(50,50)
開始角度 0
終了角度 250
回転方向 時計回り
を指定したとき
こうなればいい
左上が(0,0)、半径が50なので中心座標は(50,50)
ArcSegmentには始点と終点の座標が必要なので
それを求めるMakePoint
終点座標
角度250、中心座標(50,50)、距離は半径と同じ50
を渡すと
同じように円弧の始点も計算
開始角度は0だから真横に半径のぶん移動しただけの(100,50)
始点と終点座標
ArcSegment作成
作成時に渡すパラメータをもう一度見ると
public ArcSegment (
point, 円弧の終点
size, 円弧の半径
rotationAngle, 回転?今回は0で固定
isLargeArc, 円弧が180度を超えるかどうか
sweepDirection, Clockwiseが時計回り、Counterclockwiseが反時計回り
isStroked 描くかどうか、今回はtrueで固定);
Pointは終点なので(32.9,3.0)
sizeは半径の50でおk
sweepDirectionは時計回りなのでClockwise
あとは
IsLargeは回転角度は250-0=250で180以上なのでtrue
143行目~
SweepDirection回転方向によって引き算の向きを変えて計算して
マイナスだったら360足した値で判定している
これは冗長な気もするけどわからん
開始が30で終了が200で
時計回りの場合
200-30=170は180以上なのでIsLargeはfalse
反時計回りの場合
30-200=-170
-170+360=190は180以上なのでIsLargeをtrue
これでArcSegmentを作成できる
ArcSegment作成できたので、それを入れるPathFigure作成
PathFigure作成
PathFigureのStartPointにはさっき求めた始点座標
PathFigureのSegmentsにArcSegmentを追加
PathGeometry作成
PathGeometryのFiguresにPathFigureを追加して完成、やっとできた
これでで円弧のPathGeometryができたので、27行目
あとはPathを作って、そのDataに指定して表示
パイ型🍕のPathGeometry作成
円弧の両端に中心座標から伸びる直線
LineSegmentを足せばいい
ArcSegmentを作成するまでは円弧作成と全く同じなので
PathFigureにSegmentを加えていくところから
191行目まで全く同じ、ここまでだと円弧の状態
192行目で円弧の終点から中心への直線を追加、ここまでだと
終点から中心への直線追加
中心から円弧の始点への直線追加、193行目
最後にPathを閉じると194行目
ドーナツ型🍩
🍕(パイ型)とほとんど同じ
2つの円弧ArcSegmentと直線のLineSegmentで作成
一筆書きで作ったので2つの円弧の開始角度と終了角度が逆になる
外側の円弧から開始
外側の円弧
外側の円弧の終点から内側の円弧の始点への直線
内側の円弧
内側の円弧の終点から外側の円弧の始点への直線
Pathを閉じて完了
20度から300度、時計回り
widthは外側と内側の差
内側の円弧は開始角度と終了角度が逆になるのと
中心点からの距離がwidthのぶん短くなる、100,101行目
IsLargeの判定や
外側のArcSegment作成も普通の円弧と同じ、105~110行目
内側のArcSegmentは回転方向が逆にして、112行目
sizeもwidth幅のぶんだけ小さくして作成、113行目
//ハ゜ックマン
System.Windows.Controls.WrapPanel wrap = new System.Windows.Controls.WrapPanel();
MyStackPanel1.Children.Add(wrap);
pacman.Data = PieGeometry(new Point(100, 100), 100, 30, 330, SweepDirection.Clockwise);
MyStackPanel1.Background = Brushes.Black;
//エサ
for (int i = 0; i < 3; i++)
{
esa.Data = new EllipseGeometry(new Rect(new Size(20, 20)));
esa.Fill = Brushes.Yellow;
esa.Margin = new Thickness(-20, 0, 100, 0);
esa.VerticalAlignment = VerticalAlignment.Center;
}
参照したところ
WPFにはEllipseGeometryとRectangleGeometryがあるから円と四角形は簡単に表示できるだけどねえ
円弧にはArcSegmentがあるけどこれがさっぱりわからん
ぐぐったらさっきのリンク先がわかりやすかったので助かった
これで
中心点座標と半径、開始角度、終了角度、回転方向を指定して円弧を表示することができた
もっと省略するなら中心点座標と回転方向は要らないかな、回転方向は固定でいいし中心もあとからオフセットすれば良さそう
ギットハブ
関連記事
2019/04/04は明日