午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WPFで矢印線、直線(PolyLine)と矢印(Polygon)を組み合わせて表現

今回のアプリのダウンロード先
矢印線の描画
直線部分になるPolyLineと矢印になるPolygon、2つを組み合わせて表現
イメージ 1
クリックしたところを中継点にする直線の矢印線を描画
 
 
WPFには矢印がない
線を描画するコントロールのPolyLineやLineには線の開始や終了の形の
StrokeStartLineCap、StrokeEndLineCapがあるけど
 
イメージ 3
指定できる形のPenLineCapってのがFlat、Round、Triangle、Squareの4つしかない
しかもFlatとSquareはほとんど一緒なので実質3つ
 
イメージ 2
StrokeStartLineCap="Round"
StrokeEndLineCap="Triangle"
Windows Formのときには矢印(ArrowHead)があった、上の図の水色の矢印がまさにそれで、WPFにもあるんだろうと思ってたら、ない!
 
ググったけどわからんので作ることに
目的は矢印に見えればいいんだから単純に線と三角の図形を組み合わせればいいんじゃないかと
イメージ 4
矢印の三角部分はPolygonで作って、線はそのままLineとかPolyLineで作って
あとはくっついて見えるようにするんだから、三角の後ろの座標と線の始点や終点の座標を合わせれば
 
イメージ 5
矢印に見える
 
線の傾きに合わせて矢印を回転
イメージ 7
これは合っていないけど、だいたいこんな感じで合わせていく
矢印の回転の中心は矢印の先端、横は真ん中、縦は上端にしたいのでRenderTransformOriginは0.5,0
 
 
 
デザイン画面

f:id:gogowaten:20191213103752p:plain

C#コード
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace _20180615_直線に矢印2
{
    public partial class MainWindow : Window
    {
        Point OffsetFine矢印先端Point;
        double ArrowheadHeight矢印の高さ;
        public MainWindow()
        {
            InitializeComponent();
            
            MyCanvas.MouseMove += MyCanvas_MouseMove;
            MyCanvas.MouseLeftButtonDown += MyCanvas_MouseLeftButtonDown;
            MyCanvas.MouseRightButtonDown += MyCanvas_MouseRightButtonDown;
            this.Loaded += MainWindow_Loaded;
        }
//アプリ起動完了時、初期設定 private void MainWindow_Loaded(object sender, RoutedEventArgs e) { OffsetFine矢印先端Point = new Point(ArrowHead.ActualWidth / 2, 0); ArrowheadHeight矢印の高さ = ArrowHead.ActualHeight; //矢印の回転の中心は矢印先端にしたいので、横は真ん中、縦は上端に設定 ArrowHead.RenderTransformOrigin = new Point(0.5, 0.0); }
//右クリックでクリア private void MyCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { MyPolyline.Points.Clear(); }
//左クリックで座標追加 private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { PointCollection points = MyPolyline.Points; Point ima = e.GetPosition(MyCanvas);//マウスカーソル位置 //最初のクリックのときだけ座標を2個追加する if (points.Count == 0) { points.Add(ima); } //一個前の頂点座標はクリック座標を指定 points[points.Count - 1] = ima; points.Add(ima);//追加 }
//マウス移動時は矢印の移動と回転、線分の終端座標を矢印の後ろへ private void MyCanvas_MouseMove(object sender, MouseEventArgs e) { PointCollection points = MyPolyline.Points; //今の頂点、マウスカーソル位置 Point ima = e.GetPosition(MyCanvas); if (points.Count != 0) { Point pre = points[points.Count - 2];//一個前の頂点 //矢印の回転 double angle = Math.Atan2(ima.Y - pre.Y, ima.X - pre.X); angle = angle / Math.PI * 180; angle += 90; ArrowHead.RenderTransform = new RotateTransform(angle); MyLabel.Content = "Angle = " + angle.ToString(); //線分の終端座標を矢印の後ろにする points[points.Count - 1] = GetContactPoint接点(pre, ima); } //マウスカーソル位置に矢印先端を移動 ima.Offset(-OffsetFine矢印先端Point.X, -OffsetFine矢印先端Point.Y); Canvas.SetLeft(ArrowHead, ima.X); Canvas.SetTop(ArrowHead, ima.Y); }

/// <summary> /// 矢印後ろと直線の接触座標 /// </summary> /// <param name="pre">一個前の頂点</param> /// <param name="ima">マウスカーソル位置</param> /// <returns></returns> private Point GetContactPoint接点(Point pre, Point ima) { double lastLineLength = Distance(ima, pre);//今と一個前との距離 double diffLength = lastLineLength - ArrowheadHeight矢印の高さ + 1;//距離から矢印の高さの差 double ratio = diffLength / lastLineLength;//比率 double x = (ima.X - pre.X) * ratio;//今と一個前のx距離*比率 double y = (ima.Y - pre.Y) * ratio;//y Point nPoint = new Point(pre.X + x, pre.Y + y); return nPoint; }

//2点間の距離、ユークリッド距離 private double Distance(Point p1, Point p2) => Math.Sqrt(Math.Pow(p2.X - p1.X, 2) + Math.Pow(p2.Y - p1.Y, 2)); } }
太字のところ以外は 

gogowaten.hatenablog.com

とほとんど一緒
 
 
矢印の回転
イメージ 8
マウスカーソル位置と一個前の中継点の、yとxそれぞれの差(縦横の長さ)をMath.Atan2に渡すとラジアンが得られて、そこから角度に変換して、74行目で90度足しているのはMath.Atan2の角度0は右向きで、用意した三角は上向きに作ったからその差だと思う、これでちょうどよくなった
 
 
矢印と線の接点を求める
イメージ 10
左上から右下へ線を引いた場合
長さ調節しないと矢印と重なるので線の終端は青●の位置にするから
矢印の高さを引く感じで青●の座標を計算
 
イメージ 9
この辺はよく解っていないので冗長、三角関数を利用できるのかもしれないけど、線の長さとそれから矢印の高さを引いた長さの比率から計算した
103行目で+1しているのは調整で、しないと
イメージ 11
線の長さが足りなくて線と三角形の間に隙間ができてしまう
 
 
矢印の形を変えてみる
イメージ 12
Polygonの座標を少し変更して長細い三角の矢印
 
イメージ 13
できた
 
イメージ 14
イメージ 15
こういうのがWindowsFormにはあった
 
 
イメージ 16
イメージ 17
そのままだと隙間ができるけど
 
イメージ 18
さっきの長さ調節のところで+3にしたら
 
イメージ 19
できた
これもWindowsFormにはあった
アンカーって名前だったかな
 
 
イメージ 20
これも高さ自体は40だけど凹んでいるところは20だから
 
イメージ 21
隙間ができる
 
調節したら
イメージ 22
良さそうに見えたけど線の角が矢印から出ている
これは線が太すぎってことで少し細くして
 
イメージ 23
OK
 
 
イメージ 24
少し形が崩れている★
 
イメージ 25
いろいろできるねえ
 
イメージ 26
ソワソワする
 
コード
 
 
関連記事
5年後、デザイナー画面でも表示できるようになった
次、2018/06/21は翌日
WPFで矢印曲線、ベジェ曲線(Path)と矢印(Polygon)を組み合わせて表現、PolyBezierSegment ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15559602.html
 
2018/6/8は12日前
マウスクリックでCanvasに直線を描画その2、Polyline、WPFとC# ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15540488.html