/// <summary> /// 始点がfromで終点がtoのエッジを戻す。 /// </summary> private Edge FindEdge(PointInf from, PointInf to, FEOption opt) { switch (opt) { case FEOption.SamePointIdx: foreach (var e in mEdgeList) { if (e.fromPointIdx == from.Idx && e.toPointIdx == to.Idx) { return(e); } } break; case FEOption.SamePosition: { foreach (var e in mEdgeList) { var e1 = mPP.FindPointByIdx(e.fromPointIdx, PointProc.FindPointMode.FindAll); if (WWVectorD2.Distance(e1.xy, from.xy) < 1) { var e2 = mPP.FindPointByIdx(e.toPointIdx, PointProc.FindPointMode.FindAll); if (WWVectorD2.Distance(e2.xy, to.xy) < 1) { return(e); } } } } break; } return(null); }
/// <summary> /// エッジの矢印描画物作成。 /// </summary> private Polygon NewArrowPoly(WWVectorD2 pos1, WWVectorD2 pos2, Brush stroke) { var dir2to1N = WWVectorD2.Sub(pos1, pos2).Normalize(); var dir2to1S = dir2to1N.Scale(mDP.mArrowSz); // 2→1の方向と垂直のベクトル2つ。 var dirA = new WWVectorD2(-dir2to1N.Y, dir2to1N.X).Scale(mDP.mArrowSz * 0.5); var dirB = new WWVectorD2(dir2to1N.Y, -dir2to1N.X).Scale(mDP.mArrowSz * 0.5); var vecA = WWVectorD2.Add(dir2to1S, dirA); var vecB = WWVectorD2.Add(dir2to1S, dirB); var pos2a = WWVectorD2.Add(pos2, dir2to1N.Scale(mDP.mPointSz / 2)); var posA = WWVectorD2.Add(pos2a, vecA); var posB = WWVectorD2.Add(pos2a, vecB); var pc = new PointCollection(); pc.Add(new Point(posB.X, posB.Y)); pc.Add(new Point(pos2a.X, pos2a.Y)); pc.Add(new Point(posA.X, posA.Y)); var poly = new Polygon(); poly.Points = pc; poly.Fill = stroke; poly.StrokeThickness = 0; return(poly); }
/// <summary> /// 始点未決定状態で左クリック:始点を決定する。 /// </summary> private void SetFirstPointLeftClicked(WWVectorD2 pos) { mPP.TmpFromPointRemove(); // クリック地点に確定の点を作る。 var pInf = mPP.TestHit(pos, mDP.mPointSz); if (pInf == null) { // クリックした場所には点は未だ無い。 // 確定の始点を追加する → pInf。 pInf = new PointInf(pos); var cmd = new Command(Command.CommandType.AddPoint, pInf, null); CommandDo(cmd); AddCmdToUndoList(new CommandAtomic(cmd)); mPP.mFromPoint = pInf; } else { // クリック地点に確定の点あり。 mPP.mFromPoint = pInf; } // エッジ追加モードに遷移。 mMode = Mode.ModeAddEdge; UpdateDescription(); }
/// <summary> /// Edgeとposの最短距離を調べる。 /// </summary> /// <param name="margin">1.0</param> public double EdgeDistanceFromPos(Edge e, WWVectorD2 pos, double margin, PointProc.FindPointMode fpm) { var p1 = mPP.FindPointByIdx(e.fromPointIdx, fpm); var p2 = mPP.FindPointByIdx(e.toPointIdx, fpm); return(WWSegmentPointDistance.SegmentPointDistance(p1.xy, p2.xy, pos, margin)); }
/// <summary> /// エッジの描画物を作り、キャンバスに登録。 /// 描画物が無い状態で呼んで下さい。 /// </summary> public void EdgeDrawablesCreate(Edge edge, Brush brush) { System.Diagnostics.Debug.Assert(edge.line == null); System.Diagnostics.Debug.Assert(edge.arrow == null); var p1 = mPP.FindPointByIdx(edge.fromPointIdx, PointProc.FindPointMode.FindAll); var p2 = mPP.FindPointByIdx(edge.toPointIdx, PointProc.FindPointMode.FindAll); edge.line = DrawUtil.NewLine(p1.xy, p2.xy, brush); edge.arrow = NewArrowPoly(p1.xy, p2.xy, brush); Canvas.SetZIndex(edge.line, mDP.Z_Edge); Canvas.SetZIndex(edge.arrow, mDP.Z_Edge); mDP.mCanvas.Children.Add(edge.line); mDP.mCanvas.Children.Add(edge.arrow); // 文字を出す。 var xy = WWVectorD2.Add(p1.xy, p2.xy).Scale(0.5); edge.tbIdx = new TextBlock(); edge.tbIdx.Padding = new Thickness(2); edge.tbIdx.Text = EdgeDescriptionText(edge.EdgeIdx, edge.C, edge.B); edge.tbIdx.Foreground = mDP.mEdgeTextFgBrush; edge.tbIdx.Background = mDP.mEdgeTextBgBrush; edge.tbIdx.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var tbWH = edge.tbIdx.DesiredSize; Canvas.SetLeft(edge.tbIdx, xy.X - tbWH.Width / 2); Canvas.SetTop(edge.tbIdx, xy.Y - tbWH.Height / 2); Canvas.SetZIndex(edge.tbIdx, mDP.Z_Edge + 1); mDP.mCanvas.Children.Add(edge.tbIdx); }
/// <summary> /// 新しいPointInfを作り、キャンバスに追加。 /// 一時的な点用。 /// </summary> public PointInf NewPoint(WWVectorD2 pos, Brush brush) { var pInf = new PointInf(pos); PointDrawableCreate(pInf, brush); return(pInf); }
public void SegmentPointDistanceTestY2() { var edgeP1 = new WWVectorD2(0, 1); var edgeP2 = new WWVectorD2(0, 0); var point = new WWVectorD2(1, 0.5); double d = WWSegmentPointDistance.SegmentPointDistance(edgeP1, edgeP2, point, 1e-8); Assert.IsTrue(IsApproxSame(d, 1)); }
/// <summary> /// 始点決定状態で右クリック:始点が未決定の状態に遷移。 /// </summary> private void PointAddRightClicked(WWVectorD2 pos) { TmpDrawablesRemove((int)TmpDrawablesRemoveOpt.RemoveAll); if (mMode == Mode.ModeAddEdge) { // Edge追加モードのとき、始点設定モードに遷移。 mMode = Mode.ModeSetFirstPoint; UpdateDescription(); } }
public void SegmentPointDistanceTest2() { var edgeP1 = new WWVectorD2(1, 0); var edgeP2 = new WWVectorD2(1, 1); var point = new WWVectorD2(2, 2); double d = WWSegmentPointDistance.SegmentPointDistance(edgeP1, edgeP2, point, 1e-8); Assert.IsTrue(IsApproxSame(d, Math.Sqrt(2))); }
/// <summary> /// 線描画物作成。 /// </summary> public static Line NewLine(WWVectorD2 xy1, WWVectorD2 xy2, Brush stroke) { var l = new Line(); l.X1 = xy1.X; l.Y1 = xy1.Y; l.X2 = xy2.X; l.Y2 = xy2.Y; l.Stroke = stroke; return(l); }
/// <summary> /// リストに点xyが存在するか。 /// </summary> private bool PointExists(List <PointInf> points, WWVectorD2 xy) { foreach (var p in points) { if (WWVectorD2.Distance(p.xy, xy) < 1) { return(true); } } return(false); }
/// <returns>既存の点と当たったら既存点のPointInfを戻す。</returns> public PointInf TestHit(WWVectorD2 xy, double threshold) { foreach (var p in mPointList) { if (WWVectorD2.Distance(p.xy, xy) < threshold) { return(p); } } return(null); }
/// <summary> /// マウス移動イベントハンドラー。 /// </summary> private void mCanvas_MouseMove(object sender, MouseEventArgs e) { var posExact = e.GetPosition(mCanvas); if (mPrevPos != null && WWVectorD2.Distance(mPrevPos, new WWVectorD2(posExact.X, posExact.Y)) < 1.0) { // マウス移動量が小さい場合、何もしない。 return; } mPrevPos = new WWVectorD2(posExact.X, posExact.Y); CanvasMouseMove(e); }
/// <summary> /// エッジの係数が変更された。 /// </summary> public void EdgeParamChanged(Edge edge, double newC, double newB) { edge.tbIdx.Text = EdgeDescriptionText(edge.EdgeIdx, newC, newB); // 表示位置を調整する。 var p1 = mPP.FindPointByIdx(edge.fromPointIdx, PointProc.FindPointMode.FindAll); var p2 = mPP.FindPointByIdx(edge.toPointIdx, PointProc.FindPointMode.FindAll); var xy = WWVectorD2.Add(p1.xy, p2.xy).Scale(0.5); edge.tbIdx.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); var tbWH = edge.tbIdx.DesiredSize; Canvas.SetLeft(edge.tbIdx, xy.X - tbWH.Width / 2); Canvas.SetTop(edge.tbIdx, xy.Y - tbWH.Height / 2); }
/// <summary> /// 始点か終点がposのエッジを戻す。 /// </summary> private Edge FindEdge(WWVectorD2 pos) { foreach (var e in mEdgeList) { var p1 = mPP.FindPointByIdx(e.fromPointIdx, PointProc.FindPointMode.FindAll); var p2 = mPP.FindPointByIdx(e.toPointIdx, PointProc.FindPointMode.FindAll); if (WWVectorD2.Distance(pos, p1.xy) < 1) { return(e); } if (WWVectorD2.Distance(pos, p2.xy) < 1) { return(e); } } return(null); }
/// <summary> /// posに近い場所を横切るEdgeを調べる。 /// </summary> public Edge FindNearestEdge(WWVectorD2 pos) { Edge nearestEdge = null; double nearestDistance = double.MaxValue; double margin = 1.0; foreach (var e in mEdgeList) { double distance = EdgeDistanceFromPos(e, pos, margin, PointProc.FindPointMode.FindAll); if (distance < nearestDistance) { // 現時点で最も距離が近いエッジ。 nearestDistance = distance; nearestEdge = e; } } return(nearestEdge); }
private void DeletePointEdgeMouseMove(WWVectorD2 pos) { // 選択状態色を一旦通常色に戻します。 DeletePointEdgeCancel(); var e = mEP.FindNearestEdge(pos); var p = mPP.TestHit(pos, mDP.mPointSz); if (e != null) { if (p != null) { // 近いほうがヒット。 if (mEP.EdgeDistanceFromPos(e, pos, 1.0, PointProc.FindPointMode.FindFromPointList) < WWVectorD2.Distance(p.xy, pos)) { // eのほうが近い。 mEP.EdgeChangeColor(e, mDP.mBrightBrush); mEP.mTmpEdge = e; } else { // pのほうが近い。 mPP.PointChangeColor(p, mDP.mBrightBrush); mPP.mFromPoint = p; } } else { // エッジ。 mEP.EdgeChangeColor(e, mDP.mBrightBrush); mEP.mTmpEdge = e; } } else if (p != null) { // p。 mPP.PointChangeColor(p, mDP.mBrightBrush); mPP.mFromPoint = p; } }
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ // イベント。 public void MouseMoveUpdateTmpPoint(WWVectorD2 pos) { if (mFromPoint == null) { // 始点が無い。 Console.WriteLine("MMP FP none"); return; } // 始点有り。 Console.WriteLine("MMP ({0:0.0} {0:0.0})", pos.X, pos.Y); var toPoint = TestHit(pos, mDP.mPointSz); if (toPoint == null) { // 始点が存在し、マウスポインタ位置に確定の終点が無い。 if (mToPoint != null && WWVectorD2.Distance(mToPoint.xy, pos) < 1) { // マウスポインタ位置に仮の終点mToPointが存在。 Console.WriteLine("MMP already toPoint"); } else { // 仮の終点位置が異なるので作り直す。 PointDrawableRemove(mToPoint); mToPoint = null; mToPoint = NewPoint(pos, mDP.mBrightBrush); Console.WriteLine("MMP create toPoint"); } } else { // マウスポインタ位置に確定の終点が存在する。 // 仮の終点は不要。 PointDrawableRemove(mToPoint); mToPoint = null; } }
/// <summary> /// 削除モード:点またはエッジを消す /// </summary> private void DeletePointEdgeLeftClicked(WWVectorD2 pos) { DeletePointEdgeCancel(); var e = mEP.FindNearestEdge(pos); var p = mPP.TestHit(pos, mDP.mPointSz); if (e != null) { if (p != null) { // 近いほうがヒット。 if (mEP.EdgeDistanceFromPos(e, pos, 1.0, PointProc.FindPointMode.FindFromPointList) < WWVectorD2.Distance(p.xy, pos)) { // eのほうが近い。 DeleteEdge(e); } else { // pのほうが近い。 DeletePoint(p); } } else { // エッジ。 DeleteEdge(e); } } else if (p != null) { // p。 DeletePoint(p); } }
/// <summary> /// 始点がidxFromで終点がidxToのエッジを戻す。 /// </summary> public Edge FindEdge(int idxFrom, int idxTo, FEOption opt) { switch (opt) { case FEOption.SamePointIdx: foreach (var e in mEdgeList) { if (e.fromPointIdx == idxFrom && e.toPointIdx == idxTo) { return(e); } } break; case FEOption.SamePosition: { var p1 = mPP.FindPointByIdx(idxFrom, PointProc.FindPointMode.FindAll); var p2 = mPP.FindPointByIdx(idxTo, PointProc.FindPointMode.FindAll); foreach (var e in mEdgeList) { var e1 = mPP.FindPointByIdx(e.fromPointIdx, PointProc.FindPointMode.FindAll); if (WWVectorD2.Distance(e1.xy, p1.xy) < 1) { var e2 = mPP.FindPointByIdx(e.toPointIdx, PointProc.FindPointMode.FindAll); if (WWVectorD2.Distance(e2.xy, p2.xy) < 1) { return(e); } } } } break; } return(null); }
/// <summary> /// 始点決定状態で左クリック:終点を決定する。始点から終点に向かうエッジを追加。終点が新たに始点となる。 /// 既存点を左クリックしたとき、点の追加を行わずエッジを追加する。 /// </summary> private void PointAddLeftClicked(WWVectorD2 pos) { if (pos.X < 0 || mCanvas.ActualWidth <= pos.X || pos.Y < 0 || mCanvas.ActualHeight <= pos.Y) { // Canvas外のクリック。 return; } // 始点決定状態。 System.Diagnostics.Debug.Assert(mPP.mFromPoint != null); // 始点決定状態で左クリック:終点を決定する。始点から終点に向かうエッジを追加。終点が新たに始点となる。 // 既存点を左クリックしたとき、点の追加を行わずエッジを追加する。 var ca = new CommandAtomic(); // クリック地点に確定点が存在するか? var pInf = mPP.TestHit(pos, mDP.mPointSz); if (pInf == null) { // クリックした場所には確定点は未だ無い。 // 仮の終点を削除。 TmpDrawablesRemove((int)TmpDrawablesRemoveOpt.RemoveToPoint); mPP.mToPoint = null; // 確定の終点を追加する。 pInf = new PointInf(pos); var cmd = new Command(Command.CommandType.AddPoint, pInf, null); CommandDo(cmd); ca.Add(cmd); } else if (WWVectorD2.Distance(pInf.xy, mPP.mFromPoint.xy) < 0.5) { // クリックした点が、始点と同じ点。 // 特に何もしないで戻る。 return; } // クリック地点に始点とは異なる終点pInfが存在する状態。 // 始点の色を通常色にする。 mPP.PointChangeColor(mPP.mFromPoint, mDP.mBrush); var edge = mEP.FindEdge(mPP.mFromPoint.Idx, pInf.Idx, EdgeProc.FEOption.SamePosition); if (edge == null) { // 始点→終点のエッジが無いので追加。 var cmd = new Command(Command.CommandType.AddEdge, null, new Edge(mPP.mFromPoint.Idx, pInf.Idx)); CommandDo(cmd); ca.Add(cmd); } // コマンドが集まったのでアンドゥーリストに足す。 if (0 < ca.commandList.Count) { AddCmdToUndoList(ca); } // クリックした点を新たな始点にする。 mPP.mFromPoint = pInf; mPP.PointChangeColor(mPP.mFromPoint, mDP.mBrightBrush); }
/// <summary> /// 始点が存在し、マウスがホバーしているとき、一時的エッジの描画位置を更新する。 /// </summary> private void TmpEdgeRedrawMouseMove(WWVectorD2 pos) { mPP.MouseMoveUpdateTmpPoint(pos); mEP.MouseMoveUpdateTmpEdge(pos); }
/// <summary> /// 点。Idx, 位置aXYと描画物がある。 /// </summary> public PointInf(WWVectorD2 aXY) { idx = mNextPointIdx++; xy = aXY; F = 0; }
/// <summary> /// 始点が未決定の状態でマウスがホバーしている。 /// </summary> public void SetFirstPointMouseMove(WWVectorD2 pos) { var point = TestHit(pos, mDP.mPointSz); if (mFromPoint == null) { // 始点が無い。 Console.WriteLine("SFPMM FP none"); if (point == null) { // 始点mFromPointが無く、マウスホバー位置に確定の点も無い。 // マウスポインタ位置に仮の始点を作る。 mFromPoint = NewPoint(pos, mDP.mBrightBrush); Console.WriteLine("SFPMM create fromPoint"); return; } // 始点が無く、マウスホバー位置に確定の点が有る。 // 確定の点の色をハイライト色に変更。 // mFromPointをセットする。 mFromPoint = point; PointChangeColor(mFromPoint, mDP.mBrightBrush); return; } // 始点mFromPoint有り。 Console.WriteLine("SFPMM ({0:0.0} {0:0.0})", pos.X, pos.Y); if (point == null) { // 始点mFromPointが存在し、マウスポインタ位置に確定の点が無い。 if (WWVectorD2.Distance(mFromPoint.xy, pos) < 1) { // マウスポインタ位置に仮の始点mFromPointが存在。 Console.WriteLine("SFPMM no need to change tmp FromPoint"); } else { // 仮の始点位置が異なるので作り直す。 TmpFromPointRemove(); mFromPoint = NewPoint(pos, mDP.mBrightBrush); Console.WriteLine("SFPMM create FromPoint"); } } else { // 始点mFromPointが存在し、マウスホバー位置に確定の始点pointが存在する。 Console.WriteLine("SFPMM remove tmp drawable and set point"); // 始点mFromPointが仮の点のときは消す。 TmpFromPointRemove(); // マウスホバー位置の確定の点をmFromPointにセットする。 // 確定の点の色をハイライト色に変更。 // mFromPointをセットする。 mFromPoint = point; PointChangeColor(mFromPoint, mDP.mBrightBrush); return; } }
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ // イベント。 public void MouseMoveUpdateTmpEdge(WWVectorD2 pos) { if (mPP.mFromPoint == null) { // 始点が無い。 Console.WriteLine("MME FP none"); return; } // 始点mFromPoint有り。 Console.WriteLine("MME ({0:0.0} {0:0.0})", pos.X, pos.Y); // TmpEdgeの始点p1と終点p2 PointInf p1 = null; PointInf p2 = null; if (mTmpEdge != null) { p1 = mPP.FindPointByIdx(mTmpEdge.fromPointIdx, PointProc.FindPointMode.FindAll); p2 = mPP.FindPointByIdx(mTmpEdge.toPointIdx, PointProc.FindPointMode.FindAll); } // マウスポインタ位置に確定の終点toPointがあるか。 var toPoint = mPP.TestHit(pos, mDP.mPointSz); if (toPoint == null) { // マウスポインタ位置に確定の終点が無い。 // この場合、確定のエッジは無い。 if (mPP.mToPoint != null && WWVectorD2.Distance(mPP.mToPoint.xy, pos) < 1) { // マウスポインタ位置に仮の終点mToPointが存在する。 Console.WriteLine("MME already toPoint"); if (p1 == mPP.mFromPoint && p2 == mPP.mToPoint) { // mFromPoint → mToPoint // 仮のエッジが既に引かれている。 return; } // mFromPoint → mToPointのエッジを引く必要がある。 p1 = mPP.mFromPoint; p2 = mPP.mToPoint; } else { // マウスポインタ位置に確定の終点も仮の終点も無い。 // 画面外にマウスが行った場合? // 仮のエッジがあれば消す。 EdgeDrawablesRemove(mTmpEdge); return; } } else { // 確定の終点toPointがある。 if (null != FindEdge(mPP.mFromPoint.Idx, toPoint.Idx, FEOption.SamePosition)) { // 確定のエッジが既に引かれている。 // 仮のエッジがあれば消す。 EdgeDrawablesRemove(mTmpEdge); return; } if (p1 == mPP.mFromPoint && p2 == toPoint) { // mFromPoint → toPoint // 仮のエッジが既に引かれている。 return; } // mFromPoint → toPointのエッジを引く必要がある。 p1 = mPP.mFromPoint; p2 = toPoint; } // Edgeを作り直す。 EdgeDrawablesRemove(mTmpEdge); mTmpEdge = NewEdge(p1, p2, mDP.mBrightBrush); Console.WriteLine("MME created edge"); return; }