using CoreUnit.Common; using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Text.Json; using System.Text.Json.Serialization; using System.Windows.Forms; using System.Xml; namespace CoreUnit.Control.UserControl.Canvas { interface IArc { UnitPoint Center { get; } float Radius { get; } float StartAngle { get; } float EndAngle { get; } } class NodePointArcCenter : INodePoint { protected Arc m_owner; protected Arc m_clone; protected UnitPoint m_originalPoint; protected UnitPoint m_endPoint; public NodePointArcCenter(Arc owner) { m_owner = owner; m_clone = m_owner.Clone() as Arc; m_originalPoint = m_owner.Center; } #region INodePoint Members public IDrawObject GetClone() { return m_clone; } public IDrawObject GetOriginal() { return m_owner; } public virtual void SetPosition(UnitPoint pos) { m_clone.Center = pos; } public virtual void Finish() { m_endPoint = m_clone.Center; m_owner.Center = m_clone.Center; m_owner.Radius = m_clone.Radius; m_owner.Selected = true; m_clone = null; } public void Cancel() { m_owner.Selected = true; } public virtual void Undo() { m_owner.Center = m_originalPoint; } public virtual void Redo() { m_owner.Center = m_endPoint; } public void OnKeyDown(ICanvas canvas, KeyEventArgs e) { } #endregion } class NodePointArcRadius : INodePoint { protected Arc m_owner; protected Arc m_clone; protected float m_originalValue; protected float m_endValue; public NodePointArcRadius(Arc owner) { m_owner = owner; m_clone = m_owner.Clone() as Arc; m_clone.CurrentPoint = m_owner.CurrentPoint; m_originalValue = m_owner.Radius; } #region INodePoint Members public IDrawObject GetClone() { return m_clone; } public IDrawObject GetOriginal() { return m_owner; } public virtual void SetPosition(UnitPoint pos) { m_clone.OnMouseMove(null, pos); } public virtual void Finish() { m_endValue = m_clone.Radius; m_owner.Radius = m_clone.Radius; m_owner.Selected = true; m_clone = null; } public void Cancel() { m_owner.Selected = true; } public virtual void Undo() { m_owner.Radius = m_originalValue; } public virtual void Redo() { m_owner.Radius = m_endValue; } public void OnKeyDown(ICanvas canvas, KeyEventArgs e) { } #endregion } class NodePointArcAngle : INodePoint { protected Arc m_owner; protected Arc m_clone; protected float m_originalA1; protected float m_endA1; protected float m_originalA2; protected float m_endA2; public NodePointArcAngle(Arc owner) { m_owner = owner; m_clone = m_owner.Clone() as Arc; m_clone.CurrentPoint = m_owner.CurrentPoint; m_originalA1 = m_owner.StartAngle; m_originalA2 = m_owner.EndAngle; m_owner.Selected = false; } #region INodePoint Members public IDrawObject GetClone() { return m_clone; } public IDrawObject GetOriginal() { return m_owner; } public virtual void SetPosition(UnitPoint pos) { // Node가 변경될 경우에 drawingMode로 취급함. m_clone.drawingNodeSet = true; m_clone.OnMouseMove(null, pos); } public virtual void Finish() { m_endA1 = m_clone.StartAngle; m_endA2 = m_clone.EndAngle; m_owner.Copy(m_clone); // Node가 변경이 완료될 경우에는 SweepAngle을 EndAngle - StartAngle 변경. m_owner.SweepAngle = m_clone.EndAngle - m_clone.StartAngle; m_clone = null; } public void Cancel() { m_owner.Selected = true; } public virtual void Undo() { m_owner.StartAngle = m_originalA1; m_owner.EndAngle = m_originalA2; } public virtual void Redo() { m_owner.StartAngle = m_endA1; m_owner.EndAngle = m_endA2; } public void OnKeyDown(ICanvas canvas, KeyEventArgs e) { } #endregion } public class Arc : DrawObjectBase, IArc, IDrawObject, ISerialize { public enum eDirection { CW, CCW, } UnitPoint m_center; float m_radius; float m_startAngle = 0; float m_endAngle = 0; float m_absSweepAngle = 0; float m_sweepAngle = 0; float m_length = 0; eDirection m_direction = eDirection.CCW; [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Direction", 6, 8)] [Description("unit : CW(ClockWise) / CCW(CounterClockWise)")] [XmlSerializable] public eDirection Direction { get { return m_direction; } set { eDirection beforeDirection = m_direction; m_direction = value; if (beforeDirection != value) { if ((m_direction == eDirection.CCW && SweepAngle < 0) || (m_direction == eDirection.CW && SweepAngle > 0)) { SweepAngle = -1 * SweepAngle; } } } } bool m_reverse = false; [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Reverse", 6, 8)] [Description("Reverse")] [XmlSerializable] public bool Reverse { get { return m_reverse; } set { m_reverse = value; } } public static string ObjectType { get { return "Arc"; } } public Arc() { } public Arc(eArcType type) { m_arcType = type; m_curPoint = eCurrentPoint.p1; if (m_arcType == eArcType.typeCenterRadius) m_curPoint = eCurrentPoint.center; } public override void InitializeFromModel(UnitPoint point, DrawingLayer layer, ISnapPoint snap) { Width = layer.Width; Color = layer.Color; OnMouseDown(null, point, snap); Selected = true; } public void Copy(Arc acopy) { base.Copy(acopy); // 이 부분은 Clone 시 활용되는 부분임. // Direction에 따라, StartAngle/EndAngle의 값이 달라지기 때문에 매일 처음에 초기화함. __ 210526 이준혁 Direction = acopy.Direction; Center = acopy.Center; Radius = acopy.Radius; // EndAngle 및 StartAngle의 값은 SweepAngle에 종속되있으므로 SweepAngle을 맨 처음으로 진행 필요 SweepAngle = acopy.SweepAngle; StartAngle = acopy.StartAngle; EndAngle = acopy.EndAngle; Selected = acopy.Selected; Direction = acopy.Direction; m_arcType = acopy.m_arcType; m_curPoint = acopy.m_curPoint; Type = acopy.Type; m_no = acopy.m_no; ScannerParamNo = acopy.ScannerParamNo; Enabled = acopy.Enabled; WeldingParameterID = acopy.WeldingParameterID; m_reverse = acopy.m_reverse; m_lock = acopy.m_lock; m_RCCArc = acopy.m_RCCArc; m_MadebyRCC = acopy.m_MadebyRCC; m_RCC_PowerRatio = acopy.m_RCC_PowerRatio; } #region IDrawObject Members #region 마킹객체에 대한 속성 [Browsable(true)] [ReadOnly(true)] [CustomSortedCategory("Boundary", 1, 6)] [CustomSortedDisplayName("↖Left,Bottom,↘Right,Top", 1, 1)] [Description("Boundary of Object")] [TypeConverter(typeof(RectangleFConverter))] public RectangleF ClientRect { get { List RectanglePoint = new List(); // 360도 이상일 경우, 보정되지 않았었음. if (Math.Abs(SweepAngle) >= 360) { RectanglePoint.Add(new UnitPoint(Center.X - Radius, Center.Y + Radius)); RectanglePoint.Add(new UnitPoint(Center.X - Radius, Center.Y - Radius)); RectanglePoint.Add(new UnitPoint(Center.X + Radius, Center.Y + Radius)); RectanglePoint.Add(new UnitPoint(Center.X + Radius, Center.Y - Radius)); return ScreenUtils.GetRect(RectanglePoint, 0); } RectanglePoint.Add(StartAnglePoint); RectanglePoint.Add(EndAnglePoint); int[] dx = new int[] { 0, -1, 0, 1 }; int[] dy = new int[] { 1, 0, -1, 0 }; int startQuad, endQuad; if (StartAngle >= 0) startQuad = (int)StartAngle / 90; else startQuad = (int)((StartAngle + 360) / 90); if (EndAngle >= 0) endQuad = (int)EndAngle / 90; else endQuad = (int)((EndAngle + 360) / 90); int iter = 0, dir = 0; int positiveNumberVariable = 0; if (startQuad != endQuad) { if (Direction == eDirection.CCW) { if (startQuad > endQuad) iter = endQuad - startQuad + 4; else iter = endQuad - startQuad; dir = 1; positiveNumberVariable = 4; } if (Direction == eDirection.CW) { if (startQuad < endQuad) iter = -endQuad + startQuad + 4; else iter = -endQuad + startQuad; dir = -1; positiveNumberVariable = 3; } for (int i = 0; i < iter; i++) { int index = (startQuad + dir * i + positiveNumberVariable) % 4; if (index < 0) index += 4; // 음수 값을 양수로 변환 float xAxis = (float)(Center.X) + Radius * dx[index]; float yAxis = (float)(Center.Y) + Radius * dy[index]; RectanglePoint.Add(new UnitPoint(xAxis, yAxis)); } } return ScreenUtils.GetRect(RectanglePoint, 0); //lock (this) { return ScreenUtils.GetRect(Center, Radius, 0); } } } bool m_enabled = true; [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] // 용접 파라미터 [CustomSortedDisplayName("Enable Mark", 8, 8)] // 용접 작업 유무 [Description("Enable Mark")] // 용접 작업 진행 여부 [XmlSerializable] public bool Enabled { get { return m_enabled; } set { m_enabled = value; } } bool m_lock = false; [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Lock", 7, 8)] [Description("Lock")] [XmlSerializable] public bool Lock { get { return m_lock; } set { m_lock = value; } } bool m_RCCArc = false; [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("RCC Mode", 9, 8)] [Description("RCC Arc Mode")] [XmlSerializable] public bool RCCArc { get { return m_RCCArc; } set { m_RCCArc = value; } } bool m_MadebyRCC = false; [Browsable(false)] [ReadOnly(false)] public bool MadebyRCC { // RCC 로 생성된 object 만 true 로 바꿔준다. 코드내에서만 사용함. // Ramping 에 대한 예외처리에 필요하다. get { return m_MadebyRCC; } set { m_MadebyRCC = value; } } float m_RCC_PowerRatio = 1; [Browsable(false)] [ReadOnly(false)] public float RCC_PowerRatio { // RCC 로 생성된 object 만 true 로 바꿔준다. 코드내에서만 사용함. // Ramping 에 대한 예외처리에 필요하다. get { return m_RCC_PowerRatio; } set { m_RCC_PowerRatio = value; } } int m_scannerParamNo = 0; [Browsable(false)] [ReadOnly(false)] [Category("용접 파라미터")] [DisplayName("용접 조건 번호")] [Description("용접 조건(스캔속도 및 레이저 파워 값등..) 번호 설정")] [XmlSerializable] public int ScannerParamNo { get { return m_scannerParamNo; } set { m_scannerParamNo = value; } } [Browsable(false)] [ReadOnly(false)] [Category("용접 파라미터")] [DisplayName("용접 조건 ID")] [Description("용접 조건(스캔속도 및 레이저 파워 값등..) ID 설정")] [XmlSerializable] public int ScannerParamID { get { return m_scannerParamNo + 1; } set { m_scannerParamNo = value - 1; } } [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Pen", 2, 6)] [CustomSortedDisplayName("Parameter ID", 2, 2)] [Description("Welding Paramter ID")] [Editor(typeof(WeldingParameterIDEditor.WeldingParamterNoUITypeEditor), typeof(System.Drawing.Design.UITypeEditor))] [XmlSerializable] public WeldingParameterIDEditor.eWeldingParamterID WeldingParameterID { get { return (WeldingParameterIDEditor.eWeldingParamterID)ScannerParamID; } set { ScannerParamID = (int)value; } } public enum eAlignType { XY_ANGLE, XY_OFFSET, X_OFFSET, Y_OFFSET, ABSOLUTE_XY } eAlignType m_alignType = eAlignType.ABSOLUTE_XY; [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Vision Align", 5, 6)] [CustomSortedDisplayName("Align Type", 1, 2)] [Description("Align Type")] [XmlSerializable] public eAlignType AlignType { get { return m_alignType; } set { m_alignType = value; } } int m_alignID = -1; [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Vision Align", 5, 6)] [CustomSortedDisplayName("Align ID", 2, 2)] [Description("Align ID")] [XmlSerializable] public int AlignID { get { return m_alignID; } set { m_alignID = value; } } #endregion #region 드로잉 객체에 대한 속성 int m_no; [Browsable(false)] [XmlSerializable] public int No { get { return m_no; } set { m_no = value; } } string m_type; [Browsable(false)] [ReadOnly(true)] [Category("객체")] [DisplayName("종류")] [Description("드로잉 객체 종류")] [XmlSerializable] public string Type { get { m_type = Arc.ObjectType; return m_type; } set { m_type = value; } } [Browsable(false)] [ReadOnly(false)] [JsonIgnore] public UnitPoint Center { get { return m_center; } set { m_center = value; m_lastPoint = m_center; } } [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Center[X,Y](mm)", 1, 8)] [Description("Center Of Drawing Object")] [TypeConverter(typeof(PointFConverter))] [XmlSerializable] public PointF CenterPointF { get { lock (this) { return Center.Point; } } set { lock (this) { Center = new UnitPoint(value); } } } [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Radius", 2, 8)] [Description("Radius Of Arc")] [JsonIgnore] public float Radius { get { return m_radius; } set { m_radius = value; } } [Browsable(false)] [ReadOnly(false)] [JsonIgnore] public PointF MiddlePoint { get { return m_center.Point; } set { m_center = new UnitPoint(value); } } [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Start Angle", 3, 8)] [Description("Start Angle")] [JsonIgnore] public float StartAngle { get { return m_startAngle; } set { m_startAngle = value; while (0 > m_startAngle) { m_startAngle += 360; } while (m_startAngle >= 360) { m_startAngle -= 360; } // Drawing 시에만, EndAngle 및 SweepAngle 변경가능함. (이유는 한 바퀴 동적으로 움직이기 위해서) // Drawing이 아닐 경우에는 EndAngle도 함께 변경 if (drawingNodeSet) { // 그리는 모드일 때에는 방향에 따라서 시작점 / 종료점 관계에 따라서 서로 값 동기화 필요 if (Direction == eDirection.CCW) { if (EndAngle < StartAngle) m_endAngle += 360; else if (EndAngle > StartAngle + 360) m_endAngle -= 360; else; } else if (Direction == eDirection.CW) { if (StartAngle < EndAngle) m_endAngle -= 360; else if (EndAngle - StartAngle < -360) m_endAngle += 360; else; } else; m_sweepAngle = EndAngle - StartAngle; drawingNodeSet = false; } else m_endAngle = SweepAngle + m_startAngle; } } [Browsable(false)] [ReadOnly(false)] [Category("Geometry")] [DisplayName("End Angle")] [Description("End Angle")] [JsonIgnore] public float EndAngle { get { return m_endAngle; } set { if (Direction == eDirection.CCW) { if (value < StartAngle) m_endAngle = 360 + value; else m_endAngle = value; } else if (Direction == eDirection.CW) { if (value > StartAngle) m_endAngle = -360 + value; else m_endAngle = value; } else m_endAngle = value; // Drawing 중일 때에만 Sweep Angle 변경해줄 수 있도록 변경 if (drawingNodeSet) { m_sweepAngle = m_endAngle - m_startAngle; } } } [Browsable(false)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Start Point [X,Y](mm)", 1, 3)] [Description("Start Point")] [TypeConverter(typeof(PointFConverter))] [XmlSerializable] public PointF PF1 { get { return StartAnglePoint.Point; } set { StartAnglePoint = new UnitPoint(value); StartAngle = (float)HitUtil.RadiansToDegrees(HitUtil.AngleROncircle(m_center, new UnitPoint(value))); } } [Browsable(false)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("End Point [X,Y](mm)", 2, 3)] [Description("End Point")] [TypeConverter(typeof(PointFConverter))] [XmlSerializable] public PointF PF2 { get { return EndAnglePoint.Point; } set { EndAnglePoint = new UnitPoint(value); } } #endregion float m_angle; [Browsable(false)] [JsonIgnore] public float Angle { get { return StartAngle - EndAngle; } set { m_angle = (float)value; } } public virtual IDrawObject Clone() { Arc a = new Arc(); a.Copy(this); return a; } [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Sweep Angle[deg]", 4, 8)] [Description("unit : degree / Positive Number")] [JsonIgnore] public float AbsSweepAngle { get { m_absSweepAngle = Math.Abs(SweepAngle); return m_absSweepAngle; } set { // Sweep Angle을 양수로 입력할 때만 동작 if (value > 0) { m_absSweepAngle = value; if (Direction == eDirection.CCW) SweepAngle = m_absSweepAngle; if (Direction == eDirection.CW) SweepAngle = -m_absSweepAngle; } } } [Browsable(false)] [ReadOnly(true)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Sweep Angle[deg]", 4, 8)] [Description("unit : degree")] [XmlSerializable] public float SweepAngle { get { return m_sweepAngle; } set { m_sweepAngle = value; m_length = m_radius * value * (float)Math.PI / 180; bool ispositive = m_sweepAngle >= 0 ? true : false; if (ispositive && Direction == eDirection.CCW) { if (m_sweepAngle <= 3600) m_endAngle = StartAngle + m_sweepAngle; else m_endAngle = StartAngle + 3600; } if (!ispositive && Direction == eDirection.CW) { if (-3600 <= m_sweepAngle) m_endAngle = StartAngle + m_sweepAngle; else m_endAngle = StartAngle - 3600; } } } [Browsable(true)] [ReadOnly(false)] [CustomSortedCategory("Geometry", 3, 6)] [CustomSortedDisplayName("Length[mm]", 5, 8)] [Description("unit : mm / Positive Number")] [JsonIgnore] public float Length { get { // 심플하게 보이기 위해, 소수 5번째 자리에서 반올림. m_length = (float)Math.Round(Math.Abs(SweepAngle) * (Radius * (float)Math.PI / 180), 5); return m_length; } set { if (value > 0) { m_length = Math.Abs(value); if (Direction == eDirection.CCW) SweepAngle = m_length / (Radius * (float)Math.PI / 180); if (Direction == eDirection.CW) SweepAngle = -m_length / (Radius * (float)Math.PI / 180); } } } public RectangleF GetBoundingRect(ICanvas canvas) { float thWidth = Line.ThresholdWidth(canvas, Width, ThresholdPixel); if (thWidth < Width) thWidth = Width; float r = m_radius + thWidth / 2; RectangleF rect = HitUtil.CircleBoundingRect(m_center, r); // if drawing either angle then include the mouse point in the ractangle - this is to redraw (erase) the line drawn // from center point to mouse point if (m_curPoint == eCurrentPoint.startAngle || m_curPoint == eCurrentPoint.endAngle) rect = RectangleF.Union(rect, new RectangleF(m_lastPoint.Point, new SizeF(0, 0))); return rect; } public virtual void Draw(ICanvas canvas, RectangleF unitrect) { float sweep = SweepAngle; Color color = WeldingParameterIDEditor.weldingColorList[(int)WeldingParameterID - 1]; Pen pen = canvas.CreatePen(color, Width); canvas.DrawArc(canvas, pen, m_center, m_radius, StartAngle, sweep); if (m_direction == eDirection.CCW) { DrawUtils.DrawArrowNode(canvas, EndAnglePoint, EndAngle + 90, pen); } else { DrawUtils.DrawArrowNode(canvas, EndAnglePoint, EndAngle - 90, pen); } if (Selected) { canvas.DrawArc(canvas, DrawUtils.SelectedPen, m_center, m_radius, StartAngle, sweep); DrawUtils.DrawNode(canvas, m_center); DrawNodes(canvas); } } public bool drawingNodeSet = false; public virtual void OnMouseMove(ICanvas canvas, UnitPoint point) { // 처음 drawing 할 때, (완료되면 이 곳으로 들어오지 않음.) drawingNodeSet = true; m_lastPoint = point; if (m_curPoint == eCurrentPoint.p1) { m_point = point; return; } if (m_curPoint == eCurrentPoint.p2) { StartAngle = 0; EndAngle = 360; m_center = HitUtil.LineMidpoint(m_point, point); m_radius = (float)HitUtil.Distance(m_center, point); return; } if (m_curPoint == eCurrentPoint.center) { m_center = point; } if (m_curPoint == eCurrentPoint.radius) { //StartAngle = 0; //EndAngle = 360; m_radius = (float)HitUtil.Distance(m_center, point); } double angleToRound = 0; if (System.Windows.Forms.Control.ModifierKeys == Keys.Control) angleToRound = HitUtil.DegressToRadians(45); if (m_curPoint == eCurrentPoint.startAngle) { StartAngle = (float)HitUtil.RadiansToDegrees(HitUtil.LineAngleR(m_center, point, angleToRound)); } if (m_curPoint == eCurrentPoint.endAngle) { EndAngle = (float)HitUtil.RadiansToDegrees(HitUtil.LineAngleR(m_center, point, angleToRound)); } } public virtual eDrawObjectMouseDown OnMouseDown(ICanvas canvas, UnitPoint point, ISnapPoint snappoint) { OnMouseMove(canvas, point); if (m_arcType == eArcType.type2point) { if (m_curPoint == eCurrentPoint.p1) { m_curPoint = eCurrentPoint.p2; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.p2) { m_curPoint = eCurrentPoint.startAngle; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.startAngle) { m_curPoint = eCurrentPoint.endAngle; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.endAngle) { m_curPoint = eCurrentPoint.done; OnMouseMove(canvas, point); Selected = false; // 완료되면, drawingNode로 취급하지 않음. drawingNodeSet = false; return eDrawObjectMouseDown.Done; } } if (m_arcType == eArcType.typeCenterRadius) { if (m_curPoint == eCurrentPoint.center) { m_curPoint = eCurrentPoint.radius; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.radius) { m_curPoint = eCurrentPoint.startAngle; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.startAngle) { m_curPoint = eCurrentPoint.endAngle; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.endAngle) { m_curPoint = eCurrentPoint.done; OnMouseMove(canvas, point); Selected = false; // 완료되면, drawingNode로 취급하지 않음. drawingNodeSet = false; return eDrawObjectMouseDown.Done; } } return eDrawObjectMouseDown.Done; } public virtual void OnMouseUp(ICanvas canvas, UnitPoint point, ISnapPoint snappoint) { } public virtual void OnKeyDown(ICanvas canvas, KeyEventArgs e) { if (e.KeyCode == Keys.D && (m_curPoint == eCurrentPoint.startAngle || m_curPoint == eCurrentPoint.endAngle)) { if (Direction == eDirection.CW) Direction = eDirection.CCW; else Direction = eDirection.CW; e.Handled = true; } if (e.KeyCode == Keys.C) // switch to centerRadius mode {/* m_center = UnitPoint.Empty; m_radius = 0; m_startAngle = 0; m_endAngle = 0; m_arcType = eArcType.typeCenterRadius; m_curPoint = eCurrentPoint.center; e.Handled = true; canvas.Invalidate(false);*/ } if (e.KeyCode == Keys.D2) // switch to 2 pointmode { m_center = UnitPoint.Empty; m_radius = 0; m_startAngle = 0; m_endAngle = 0; m_arcType = eArcType.type2point; m_curPoint = eCurrentPoint.p1; e.Handled = true; canvas.Invalidate(false); } } public virtual bool PointInObject(ICanvas canvas, UnitPoint point) { RectangleF boundingrect = GetBoundingRect(canvas); if (boundingrect.Contains(point.Point) == false) return false; float thWidth = Line.ThresholdWidth(canvas, Width, ThresholdPixel); if (HitUtil.PointInPoint(m_center, point, thWidth)) return true; return HitUtil.IsPointInArc(m_center, m_radius, point, thWidth / 2, ClientRect); } public virtual bool ObjectInRectangle(ICanvas canvas, RectangleF rect, bool anyPoint) { float r = m_radius + Width / 2; //RectangleF boundingrect = HitUtil.CircleBoundingRect(m_center, r); RectangleF boundingrect = ClientRect; if (anyPoint) { UnitPoint lp1 = new UnitPoint(rect.Left, rect.Top); UnitPoint lp2 = new UnitPoint(rect.Left, rect.Bottom); if (HitUtil.CircleIntersectWithLine(m_center, m_radius, lp1, lp2)) return true; lp1 = new UnitPoint(rect.Left, rect.Bottom); lp2 = new UnitPoint(rect.Right, rect.Bottom); if (HitUtil.CircleIntersectWithLine(m_center, m_radius, lp1, lp2)) return true; lp1 = new UnitPoint(rect.Left, rect.Top); lp2 = new UnitPoint(rect.Right, rect.Top); if (HitUtil.CircleIntersectWithLine(m_center, m_radius, lp1, lp2)) return true; lp1 = new UnitPoint(rect.Left, rect.Top); lp2 = new UnitPoint(rect.Right, rect.Top); if (HitUtil.CircleIntersectWithLine(m_center, m_radius, lp1, lp2)) return true; lp1 = new UnitPoint(rect.Right, rect.Top); lp2 = new UnitPoint(rect.Right, rect.Bottom); if (HitUtil.CircleIntersectWithLine(m_center, m_radius, lp1, lp2)) return true; } return rect.Contains(boundingrect); } [Browsable(false)] [ReadOnly(false)] public virtual UnitPoint RepeatStartingPoint { get { return UnitPoint.Empty; } } public virtual INodePoint NodePoint(ICanvas canvas, UnitPoint point) { float thWidth = Line.ThresholdWidth(canvas, Width, ThresholdPixel); if (HitUtil.PointInPoint(m_center, point, thWidth)) return new NodePointArcCenter(this); if (HitUtil.PointInPoint(StartAnglePoint, point, thWidth)) { m_curPoint = eCurrentPoint.startAngle; m_lastPoint = m_center; return new NodePointArcAngle(this); } if (HitUtil.PointInPoint(EndAnglePoint, point, thWidth)) { m_curPoint = eCurrentPoint.endAngle; m_lastPoint = m_center; return new NodePointArcAngle(this); } if (HitUtil.PointInPoint(RadiusPoint, point, thWidth)) { m_curPoint = eCurrentPoint.radius; m_lastPoint = m_center; return new NodePointArcRadius(this); } return null; } public virtual ISnapPoint SnapPoint(ICanvas canvas, UnitPoint point, List otherobj, Type[] runningsnaptypes, Type usersnaptype) { float thWidth = Line.ThresholdWidth(canvas, Width, ThresholdPixel); if (runningsnaptypes != null) { foreach (Type snaptype in runningsnaptypes) { if (snaptype == typeof(QuadrantSnapPoint)) { UnitPoint p = HitUtil.NearestPointOnCircle(m_center, m_radius, point, 90); if (p != UnitPoint.Empty && HitUtil.PointInPoint(p, point, thWidth)) return new QuadrantSnapPoint(canvas, this, p); } if (snaptype == typeof(DivisionSnapPoint)) { double angle = 360 / Divisions; UnitPoint p = HitUtil.NearestPointOnCircle(m_center, m_radius, point, angle); if (p != UnitPoint.Empty && HitUtil.PointInPoint(p, point, thWidth)) return new DivisionSnapPoint(canvas, this, p); } if (snaptype == typeof(CenterSnapPoint)) { if (HitUtil.PointInPoint(m_center, point, thWidth)) return new CenterSnapPoint(canvas, this, m_center); } if (snaptype == typeof(VertextSnapPoint)) { if (HitUtil.PointInPoint(StartAnglePoint, point, thWidth)) return new VertextSnapPoint(canvas, this, StartAnglePoint); if (HitUtil.PointInPoint(EndAnglePoint, point, thWidth)) return new VertextSnapPoint(canvas, this, EndAnglePoint); } } return null; } if (usersnaptype == typeof(NearestSnapPoint)) { UnitPoint p = HitUtil.NearestPointOnCircle(m_center, m_radius, point, 0); if (p != UnitPoint.Empty) return new NearestSnapPoint(canvas, this, p); } if (usersnaptype == typeof(PerpendicularSnapPoint)) { UnitPoint p = HitUtil.NearestPointOnCircle(m_center, m_radius, point, 0); if (p != UnitPoint.Empty) return new PerpendicularSnapPoint(canvas, this, p); } if (usersnaptype == typeof(QuadrantSnapPoint)) { UnitPoint p = HitUtil.NearestPointOnCircle(m_center, m_radius, point, 90); if (p != UnitPoint.Empty) return new QuadrantSnapPoint(canvas, this, p); } if (usersnaptype == typeof(DivisionSnapPoint)) { double angle = 360 / Divisions; UnitPoint p = HitUtil.NearestPointOnCircle(m_center, m_radius, point, angle); if (p != UnitPoint.Empty) return new DivisionSnapPoint(canvas, this, p); } if (usersnaptype == typeof(TangentSnapPoint)) { IDrawObject drawingObject = canvas.CurrentObject; UnitPoint p = UnitPoint.Empty; if (drawingObject is LineEdit) { UnitPoint mousepoint = point; point = ((LineEdit)drawingObject).P1; UnitPoint p1 = HitUtil.TangentPointOnCircle(m_center, m_radius, point, false); UnitPoint p2 = HitUtil.TangentPointOnCircle(m_center, m_radius, point, true); double d1 = HitUtil.Distance(mousepoint, p1); double d2 = HitUtil.Distance(mousepoint, p2); if (d1 <= d2) return new TangentSnapPoint(canvas, this, p1); else return new TangentSnapPoint(canvas, this, p2); } //if (p != PointF.Empty) return new TangentSnapPoint(canvas, this, p); } if (usersnaptype == typeof(CenterSnapPoint)) { return new CenterSnapPoint(canvas, this, m_center); } return null; } public virtual void Move(UnitPoint offset) { m_center.X += offset.X; m_center.Y += offset.Y; m_lastPoint = m_center; } public void Rotate(float angle, UnitPoint center) { double angleFromCenterToStart = 0, angleFromCenterToEnd = 0, angleFromOriginToCenter = 0; UnitPoint m_pf1 = new UnitPoint(); UnitPoint m_pf2 = new UnitPoint(); // 1. Center Line에서 StartPoint와 EndPoint 각도. UnitPoint v1 = Center - center; UnitPoint v2 = new UnitPoint(PF1.X - Center.X, PF1.Y - Center.Y); UnitPoint v3 = new UnitPoint(PF2.X - Center.X, PF2.Y - Center.Y); // 2. 중심 회전시킴. Center = HitUtil.RotatePoint(angle, Center, center); // 3. 중심 회전 후, 중심 기준으로 point 정의. angleFromOriginToCenter = Math.Atan2(Center.Y - center.Y, Center.X - center.X) - Math.PI / 2;// + Math.PI/2; if (angleFromOriginToCenter > Math.PI) { angleFromOriginToCenter -= 2 * Math.PI; } else if (angleFromOriginToCenter <= -Math.PI) { angleFromOriginToCenter += 2 * Math.PI; } //else; if (angleFromOriginToCenter != -Math.PI / 2) { angleFromCenterToStart = Math.Atan2(v2.Y, v2.X) - Math.Atan2(v1.Y, v1.X); angleFromCenterToEnd = Math.Atan2(v3.Y, v3.X) - Math.Atan2(v1.Y, v1.X); if (angleFromCenterToStart > Math.PI) { angleFromCenterToStart -= 2 * Math.PI; } else if (angleFromCenterToStart <= -Math.PI) { angleFromCenterToStart += 2 * Math.PI; } //else; if (angleFromCenterToEnd > Math.PI) { angleFromCenterToEnd -= 2 * Math.PI; } else if (angleFromCenterToEnd <= -Math.PI) { angleFromCenterToEnd += 2 * Math.PI; } //else; m_pf1 = PointToAngles(Center, Radius, angleFromOriginToCenter, angleFromCenterToStart); m_pf2 = PointToAngles(Center, Radius, angleFromOriginToCenter, angleFromCenterToEnd); } else { m_pf1 = HitUtil.RotatePoint(angle, StartAnglePoint, center); m_pf2 = HitUtil.RotatePoint(angle, EndAnglePoint, center); } PF1 = new PointF((float)m_pf1.X, (float)m_pf1.Y); PF2 = new PointF((float)m_pf2.X, (float)m_pf2.Y); } public UnitPoint PointToAngles(UnitPoint center, double length, double angle0, double angle1) { UnitPoint p1 = new UnitPoint(); p1.X = center.X - length * Math.Cos(angle0) * Math.Sin(angle1) - length * Math.Sin(angle0) * Math.Cos(angle1); p1.Y = center.Y - length * Math.Sin(angle0) * Math.Sin(angle1) + length * Math.Cos(angle0) * Math.Cos(angle1); return p1; } public void MakeLength(float distance) { } public virtual string GetInfoAsString() { return string.Format("{1:D3} {0}@{2}, r={3:f4}, A1={4:f4}, A2={5:f4}", this.Type, No + 1, Center.PosAsString(), Radius, StartAngle, EndAngle); } #endregion #region ISerialize public virtual void GetObjectData(XmlWriter wr) { wr.WriteStartElement(Type); Common.XmlUtil.WriteProperties(this, wr); wr.WriteEndElement(); } public virtual string GetObjectData() { string jsonString = string.Empty; var options = new JsonSerializerOptions { WriteIndented = true }; try { jsonString += JsonSerializer.Serialize(this, options); } catch { } return jsonString; } public void AfterSerializedIn() { } #endregion protected static int ThresholdPixel = 6; protected static int Divisions = 8; public enum eCurrentPoint { p1, p2, center, radius, startAngle, endAngle, done, } [Browsable(false)] [ReadOnly(false)] public eCurrentPoint CurrentPoint { get { return m_curPoint; } set { m_curPoint = value; } } public enum eArcType { typeCenterRadius, type2point, } protected virtual void DrawNodes(ICanvas canvas) { if (m_curPoint == eCurrentPoint.startAngle && m_lastPoint != UnitPoint.Empty) canvas.DrawLine(canvas, DrawUtils.SelectedPen, m_center, m_lastPoint); if (m_curPoint == eCurrentPoint.endAngle && m_lastPoint != UnitPoint.Empty) canvas.DrawLine(canvas, DrawUtils.SelectedPen, m_center, m_lastPoint); DrawUtils.DrawNode(canvas, StartAnglePoint); DrawUtils.DrawNode(canvas, EndAnglePoint); DrawUtils.DrawNode(canvas, RadiusPoint); } protected UnitPoint AnglePoint(float angle) { return HitUtil.PointOncircle(m_center, m_radius, HitUtil.DegressToRadians(angle)); } protected UnitPoint RadiusPoint { get { float angle = StartAngle + SweepAngle / 2; return AnglePoint(angle); } } protected UnitPoint StartAnglePoint { get { return AnglePoint(StartAngle); } set { //StartAngle = (float)HitUtil.RadiansToDegrees(HitUtil.AngleROncircle(m_center, value)); m_radius = (float)HitUtil.Distance(m_center, value); } } protected UnitPoint EndAnglePoint { get { return AnglePoint(EndAngle); } set { //EndAngle = (float)HitUtil.RadiansToDegrees(HitUtil.AngleROncircle(m_center, value)); m_radius = (float)HitUtil.Distance(m_center, value); } } protected eArcType m_arcType = eArcType.type2point; protected eCurrentPoint m_curPoint = eCurrentPoint.p1; protected UnitPoint m_lastPoint; protected UnitPoint m_point; } public class Circle : Arc { public Circle() : base() { } public Circle(Arc.eArcType type) : base(type) { } public new static string ObjectType { get { return "Circle"; } } public new string Type { get { return ObjectType; } } public override IDrawObject Clone() { Circle a = new Circle(); a.Copy(this); return a; } public override eDrawObjectMouseDown OnMouseDown(ICanvas canvas, UnitPoint point, ISnapPoint snappoint) { OnMouseMove(canvas, point); if (m_arcType == eArcType.type2point) { if (m_curPoint == eCurrentPoint.p1) { m_curPoint = eCurrentPoint.p2; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.p2) { m_curPoint = eCurrentPoint.done; OnMouseMove(canvas, point); Selected = false; return eDrawObjectMouseDown.Done; } } if (m_arcType == eArcType.typeCenterRadius) { if (m_curPoint == eCurrentPoint.center) { m_curPoint = eCurrentPoint.radius; return eDrawObjectMouseDown.Continue; } if (m_curPoint == eCurrentPoint.radius) { m_curPoint = eCurrentPoint.done; OnMouseMove(canvas, point); Selected = false; return eDrawObjectMouseDown.Done; } } return eDrawObjectMouseDown.Done; } protected override void DrawNodes(ICanvas canvas) { DrawUtils.DrawNode(canvas, AnglePoint(0)); DrawUtils.DrawNode(canvas, AnglePoint(90)); DrawUtils.DrawNode(canvas, AnglePoint(180)); DrawUtils.DrawNode(canvas, AnglePoint(270)); } public override INodePoint NodePoint(ICanvas canvas, UnitPoint point) { float thWidth = Line.ThresholdWidth(canvas, Width, ThresholdPixel); if (HitUtil.PointInPoint(Center, point, thWidth)) return new NodePointArcCenter(this); bool radiushit = HitUtil.PointInPoint(AnglePoint(0), point, thWidth); if (radiushit == false) radiushit = HitUtil.PointInPoint(AnglePoint(90), point, thWidth); if (radiushit == false) radiushit = HitUtil.PointInPoint(AnglePoint(180), point, thWidth); if (radiushit == false) radiushit = HitUtil.PointInPoint(AnglePoint(270), point, thWidth); if (radiushit) { m_curPoint = eCurrentPoint.radius; m_lastPoint = Center; return new NodePointArcRadius(this); } return null; } public override string GetInfoAsString() { return string.Format("{1:D3} {0}@{2}, r={3:f4}", this.Type, No + 1, Center.PosAsString(), Radius); } } }