using ComPDFKit.Tool.SettingParam; using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; namespace ComPDFKit.Tool.DrawTool { public enum MulitiDrawMoveType { Default, Alone } public class MultiSelectedRect : DrawingVisual { /// <summary> /// Re-layout child elements /// </summary> public void Arrange() { foreach (Visual child in Children) { if (!(child is UIElement)) { continue; } UIElement checkChild = child as UIElement; try { double left = Canvas.GetLeft(checkChild); double top = Canvas.GetTop(checkChild); double width = (double)checkChild.GetValue(FrameworkElement.WidthProperty); double height = (double)checkChild.GetValue(FrameworkElement.HeightProperty); checkChild.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); checkChild.Arrange(new Rect( double.IsNaN(left) ? 0 : left, double.IsNaN(top) ? 0 : top, double.IsNaN(width) ? checkChild.DesiredSize.Width : width, double.IsNaN(height) ? checkChild.DesiredSize.Height : height)); } catch (Exception ex) { } } } protected DefaultDrawParam drawParam = new DefaultDrawParam(); protected DrawingContext drawDC { get; set; } /// <summary> /// DataChanging event /// </summary> public event EventHandler<Point> DataChanging; /// <summary> /// DataChanged event /// </summary> public event EventHandler<Point> DataChanged; protected SelectedType selectedType = SelectedType.None; /// <summary> /// Minimum width of the rectangle /// </summary> protected int rectMinWidth { get; set; } = 10; /// <summary> /// Minimum height of the rectangle /// </summary> protected int rectMinHeight { get; set; } = 10; /// <summary> /// Identify whether the mouse is pressed /// </summary> protected bool isMouseDown { get; set; } /// <summary> /// Current hit control point /// </summary> protected PointControlType hitControlType { get; set; } /// <summary> /// Location information recorded when the mouse is pressed /// </summary> protected Point mouseDownPoint { get; set; } /// <summary> /// Current set ignore points /// </summary> protected List<PointControlType> ignorePoints { get; set; } = new List<PointControlType>(); /// <summary> /// Current control point coordinates /// </summary> protected List<Point> controlPoints { get; set; } = new List<Point>(); /// <summary> /// Move offset /// </summary> protected Point moveOffset { get; set; } = new Point(0, 0); /// <summary> /// Current PDFVIewer's actual display width /// </summary> protected double PDFViewerActualWidth { get; set; } = 0; /// <summary> /// Current PDFVIewer's actual display height /// </summary> protected double PDFViewerActualHeight { get; set; } = 0; /// <summary> /// Current control point drawing style /// </summary> protected DrawPointType currentDrawPointType { get; set; } /// <summary> /// Current drag drawing style /// </summary> protected DrawMoveType currentDrawMoveType { get; set; } /// <summary> /// Current multi-select drawing style /// </summary> protected MulitiDrawMoveType currentDrawType { get; set; } = MulitiDrawMoveType.Default; /// <summary> /// Control point size /// </summary> protected int pointSize { get; set; } = 4; /// <summary> /// Current drawing rectangle (calculated during operation) /// </summary> protected Rect drawRect { get; set; } = new Rect(0, 0, 0, 0); /// <summary> /// Default outermost rectangle of the drawing style (calculated during operation) /// </summary> protected Rect drawDefaultRect { get; set; } = new Rect(0, 0, 0, 0); /// <summary> /// Padding between the border and the content /// </summary> protected double rectPadding = 5; /// <summary> /// Mouse down cache rectangle /// </summary> protected Rect cacheRect { get; set; } = new Rect(0, 0, 0, 0); /// <summary> /// Current set drawing rectangle (original data) /// </summary> protected Rect setDrawRect { get; set; } = new Rect(0, 0, 0, 0); /// <summary> /// Maximum drawable range /// </summary> protected Rect maxRect { get; set; } = new Rect(0, 0, 0, 0); /// <summary> /// Identify whether the mouse is pressed /// </summary> protected bool isProportionalScaling { get; set; } = false; /// <summary> /// Array passed from outside for multiple selection /// </summary> internal List<SelectedRect> selectedRects = new List<SelectedRect>(); protected Dictionary<SelectedRect,KeyValuePair<int,int>> RelationDict=new Dictionary<SelectedRect, KeyValuePair<int, int>>(); protected bool isHover = false; protected bool isSelected = false; public void SetIsHover(bool hover) { isHover = hover; } public bool GetIsHover() { return isHover; } public void SetIsSelected(bool selected) { isSelected = selected; } public bool GetIsSelected() { return isSelected; } public MultiSelectedRect(DefaultDrawParam defaultDrawParam, SelectedType type) : base() { drawParam = defaultDrawParam; currentDrawPointType = DrawPointType.Circle; selectedType = type; } public void SetMulitSelectedRect(SelectedRect selectedobject,int pageIndex,int editIndex) { selectedRects.Add(selectedobject); RelationDict[selectedobject] = new KeyValuePair<int, int>(pageIndex, editIndex); } public bool GetRelationKey(SelectedRect selectedobject,out int pageIndex,out int editIndex) { pageIndex = -1; editIndex = -1; if(RelationDict!=null && RelationDict.ContainsKey(selectedobject)) { KeyValuePair<int, int> relateData = RelationDict[selectedobject]; pageIndex = relateData.Key; editIndex = relateData.Value; return true; } return false; } /// <summary> /// delete /// </summary> /// <param name="selectedobject"></param> public void DelMulitSelectedRect(SelectedRect selectedobject) { selectedRects.Remove(selectedobject); RelationDict.Remove(selectedobject); } /// <summary> /// get selectedRects Index /// </summary> /// <param name="selectedobject"></param> /// <returns></returns> public int GetMulitSelectedRectIndex(SelectedRect selectedobject) { return selectedRects.IndexOf(selectedobject); } public List<SelectedRect> GetMulitSelectList() { return selectedRects==null ? new List<SelectedRect>() : selectedRects; } public SelectedType GetSelectedType() { return selectedType; } public void SetSelectedType(SelectedType type) { if (selectedType != type) { selectedRects.Clear(); RelationDict.Clear(); } selectedType = type; } public void CleanMulitSelectedRect() { selectedRects.Clear(); RelationDict.Clear(); } public virtual void OnMouseLeftButtonDown(Point downPoint) { isMouseDown = true; hitControlType = PointControlType.None; mouseDownPoint = downPoint; moveOffset = new Point(0, 0); HitTestResult hitResult = VisualTreeHelper.HitTest(this, downPoint); if (hitResult != null && hitResult.VisualHit is DrawingVisual) { hitControlType = GetHitControlIndex(downPoint); if (hitControlType != PointControlType.None) { cacheRect = drawRect; } } } public virtual void OnMouseLeftButtonUp(Point upPoint) { if (isMouseDown && hitControlType != PointControlType.None) { isMouseDown = false; cacheRect = setDrawRect = drawRect; Draw(); if ((int)upPoint.X != (int)mouseDownPoint.X || (int)upPoint.Y != (int)mouseDownPoint.Y) { InvokeDataChangEvent(true); } } moveOffset = new Point(0, 0); } public virtual void OnMouseMove(Point mousePoint, out bool Tag, double width, double height) { PDFViewerActualWidth = width; PDFViewerActualHeight = height; Tag = false; if (isMouseDown && hitControlType != PointControlType.None) { Tag = isMouseDown; if (CalcHitPointMove(mousePoint)) { Draw(); if ((int)mousePoint.X != (int)mouseDownPoint.X || (int)mousePoint.Y != (int)mouseDownPoint.Y) { InvokeDataChangEvent(false); } } } } public float GetZoomX() { return (float)(drawRect.Width / drawDefaultRect.Width); } public float GetZoomY() { return (float)(drawRect.Height / drawDefaultRect.Height); } /// <summary> /// Multiple selection of movement distance /// </summary> /// <returns></returns> public float GetChangeX() { return (float)(drawRect.Width - drawDefaultRect.Width); } /// <summary> /// Multiple selection of movement distance /// </summary> /// <returns></returns> public float GetChangeY() { return (float)(drawRect.Height - drawDefaultRect.Height); } public void Draw() { switch (currentDrawType) { case MulitiDrawMoveType.Default: Dispatcher.Invoke(() => { if (drawDefaultRect.IsEmpty == false && drawDefaultRect.Width > 0 && drawDefaultRect.Height > 0) { drawDC = RenderOpen(); CalcControlPoint(drawDefaultRect); SolidColorBrush solidColorBrush = drawParam.SPDFEditMultiRectFillBrush; Pen pen = drawParam.SPDFEditMultiRectLinePen; GetBrushAndPen(ref solidColorBrush, ref pen); foreach (SelectedRect item in selectedRects) { if (!item.IsPath) { Rect rect = item.GetRect(); rect.X -= rectPadding; rect.Y -= rectPadding; rect.Width += rectPadding; rect.Height += rectPadding; drawDC?.DrawRectangle(solidColorBrush, pen, rect); } } SolidColorBrush PointBrush = drawParam.PDFEditMultiPointBorderBrush; Pen PointPen = drawParam.PDFEditMultiPointPen; GetPointBrushAndPen(ref PointBrush, ref PointPen); switch (currentDrawMoveType) { case DrawMoveType.kDefault: break; case DrawMoveType.kReferenceLine: if (isMouseDown == true) { SolidColorBrush moveBrush = drawParam.PDFEditMultiMoveBrush; Pen movepen = drawParam.PDFEditMultiMovePen; GetMoveBrushAndPen(ref moveBrush, ref movepen); if (selectedType == SelectedType.PDFEdit) { DrawMoveBounds(drawDC, hitControlType, movepen, moveBrush, drawRect, drawParam.PDFEditMultiMoveRectPen); } else { DrawMoveBounds(drawDC, hitControlType, movepen, moveBrush, drawRect); } } drawDC?.DrawRectangle(solidColorBrush, pen, drawDefaultRect); break; default: break; } switch (currentDrawPointType) { case DrawPointType.Circle: if (selectedType == SelectedType.PDFEdit) { //Edit Settings Frame DrawCirclePoint(drawDC, GetIgnorePoints(), pointSize, PointPen, PointBrush); } else { DrawCirclePoint(drawDC, GetIgnorePoints(), pointSize, PointPen, PointBrush); } break; case DrawPointType.Square: DrawSquarePoint(drawDC, GetIgnorePoints(), pointSize, PointPen, PointBrush); break; } drawDC?.Close(); drawDC = null; } }); break; case MulitiDrawMoveType.Alone: CalcControlPoint(drawDefaultRect); foreach (SelectedRect selectRect in selectedRects) { selectRect.Draw(); } break; default: break; } } private void GetMoveBrushAndPen(ref SolidColorBrush colorBrush, ref Pen pen) { switch (selectedType) { case SelectedType.None: break; case SelectedType.Annot: //colorBrush = DrawParam.AnnotMoveBrush; //pen = DrawParam.AnnotMovePen; break; case SelectedType.PDFEdit: colorBrush = drawParam.PDFEditMultiMoveBrush; pen = drawParam.PDFEditMultiMovePen; break; default: break; } } private void GetBrushAndPen(ref SolidColorBrush colorBrush, ref Pen pen) { switch (selectedType) { case SelectedType.None: break; case SelectedType.Annot: //if (isHover) //{ // colorBrush = DrawParam.AnnotRectFillBrush; // pen = DrawParam.AnnotRectHoverPen; //} //else //{ // colorBrush = DrawParam.AnnotRectFillBrush; // pen = DrawParam.AnnotRectLinePen; //} break; case SelectedType.PDFEdit: if (isHover) { colorBrush = drawParam.PDFEditMultiRectFillHoverBrush; pen = drawParam.PDFEditMultiRectLineHoverPen; } else { if (isSelected) { colorBrush = drawParam.SPDFEditMultiRectFillBrush; pen = drawParam.SPDFEditMultiRectLinePen; } else { colorBrush = drawParam.PDFEditMultiRectFillBrush; pen = drawParam.PDFEditMultiRectLinePen; } } break; default: break; } } private void GetPointBrushAndPen(ref SolidColorBrush colorBrush, ref Pen pen) { switch (selectedType) { case SelectedType.None: break; case SelectedType.Annot: //colorBrush = DrawParam.AnnotPointBorderBrush; //pen = DrawParam.AnnotPointPen; break; case SelectedType.PDFEdit: if (isHover) { colorBrush = drawParam.PDFEditMultiRectFillHoverBrush; pen = drawParam.PDFEditMultiPointHoverPen; } else { if (isSelected) { colorBrush = drawParam.SPDFEditMultiPointBorderBrush; pen = drawParam.SPDFEditMultiPointPen; } else { colorBrush = drawParam.PDFEditMultiPointBorderBrush; pen = drawParam.PDFEditMultiPointPen; } } break; default: break; } } /// <summary> /// Internal drawing circle point /// </summary> /// <param name="drawingContext"> /// Drawing context /// </param> /// <param name="ignoreList"> ///Collection of positions where points need to be drawn /// </param> /// <param name="PointSize"> /// Point size /// </param> /// <param name="PointPen"> /// Drawing point brush /// </param> /// <param name="BorderBrush"> /// Brush for drawing point border /// </param> protected void DrawCirclePoint(DrawingContext drawingContext, List<PointControlType> ignoreList, int PointSize, Pen PointPen, SolidColorBrush BorderBrush) { GeometryGroup controlGroup = new GeometryGroup(); controlGroup.FillRule = FillRule.Nonzero; List<Point> ignorePointsList = new List<Point>(); // Get specific points foreach (PointControlType type in ignoreList) { if ((int)type < controlPoints.Count) { ignorePointsList.Add(controlPoints[(int)type]); } } for (int i = 0; i < controlPoints.Count; i++) { Point controlPoint = controlPoints[i]; if (ignorePointsList.Contains(controlPoint)) { continue; } EllipseGeometry circlPoint = new EllipseGeometry(controlPoint, PointSize, PointSize); controlGroup.Children.Add(circlPoint); } drawingContext?.DrawGeometry(BorderBrush, PointPen, controlGroup); } /// <summary> /// Internal drawing square /// </summary> /// <param name="drawingContext"> /// Drawing context /// </param> /// <param name="ControlPoints"> /// Collection of positions where points need to be drawn /// </param> /// <param name="PointSize"> /// Size of the point /// </param> /// <param name="PointPen"> /// Brush for drawing point /// </param> /// <param name="BorderBrush"> /// Border brush for drawing points /// </param> protected void DrawSquarePoint(DrawingContext drawingContext, List<PointControlType> ignoreList, int PointSize, Pen PointPen, SolidColorBrush BorderBrush) { GeometryGroup controlGroup = new GeometryGroup(); controlGroup.FillRule = FillRule.Nonzero; List<Point> ignorePointsList = new List<Point>(); // Get specific points foreach (PointControlType type in ignoreList) { if ((int)type < controlPoints.Count) { ignorePointsList.Add(controlPoints[(int)type]); } } for (int i = 0; i < controlPoints.Count; i++) { Point controlPoint = controlPoints[i]; if (ignorePointsList.Contains(controlPoint)) { continue; } RectangleGeometry rectPoint = new RectangleGeometry(new Rect(controlPoint.X - PointSize, controlPoint.Y - PointSize, PointSize * 2, PointSize * 2), 1, 1); controlGroup.Children.Add(rectPoint); } drawingContext?.DrawGeometry(BorderBrush, PointPen, controlGroup); } /// <summary> /// Draw the reference line in the moving state /// </summary> /// <param name="drawDc"> /// Drawing context /// </param> /// <param name="controltype"> /// Drawing context /// </param> /// <param name="activePen"> /// Drawing context /// </param> /// <param name="moveBrush"> /// Move brush /// </param> /// <param name="moveRect"> /// Move rectangle /// </param> protected void DrawMoveBounds(DrawingContext drawDc, PointControlType controltype, Pen activePen, Brush moveBrush, Rect moveRect, Pen RectPen = null) { switch (controltype) { case PointControlType.LeftTop: drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top)); drawDc?.DrawLine(activePen, new Point(moveRect.Left, 0), new Point(moveRect.Left, PDFViewerActualHeight)); break; case PointControlType.LeftMiddle: drawDc?.DrawLine(activePen, new Point(moveRect.Left, 0), new Point(moveRect.Left, PDFViewerActualHeight)); break; case PointControlType.LeftBottom: drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom)); drawDc?.DrawLine(activePen, new Point(moveRect.Left, 0), new Point(moveRect.Left, PDFViewerActualHeight)); break; case PointControlType.MiddleBottom: drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom)); break; case PointControlType.RightBottom: drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom)); drawDc?.DrawLine(activePen, new Point(moveRect.Right, 0), new Point(moveRect.Right, PDFViewerActualHeight)); break; case PointControlType.RightMiddle: drawDc?.DrawLine(activePen, new Point(moveRect.Right, 0), new Point(moveRect.Right, PDFViewerActualHeight)); break; case PointControlType.RightTop: drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top)); drawDc?.DrawLine(activePen, new Point(moveRect.Right, 0), new Point(moveRect.Right, PDFViewerActualHeight)); break; case PointControlType.MiddleTop: drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top)); break; case PointControlType.Rotate: break; case PointControlType.Body: case PointControlType.Line: drawDc?.DrawLine(activePen, new Point(0, moveRect.Top), new Point(moveRect.Left, moveRect.Top)); drawDc?.DrawLine(activePen, new Point(moveRect.Right, moveRect.Top), new Point(PDFViewerActualWidth, moveRect.Top)); drawDc?.DrawLine(activePen, new Point(moveRect.Left, moveRect.Top), new Point(moveRect.Left, 0)); drawDc?.DrawLine(activePen, new Point(moveRect.Right, moveRect.Top), new Point(moveRect.Right, 0)); drawDc?.DrawLine(activePen, new Point(0, moveRect.Bottom), new Point(moveRect.Left, moveRect.Bottom)); drawDc?.DrawLine(activePen, new Point(moveRect.Right, moveRect.Bottom), new Point(PDFViewerActualWidth, moveRect.Bottom)); drawDc?.DrawLine(activePen, new Point(moveRect.Left, moveRect.Bottom), new Point(moveRect.Left, PDFViewerActualHeight)); drawDc?.DrawLine(activePen, new Point(moveRect.Right, moveRect.Bottom), new Point(moveRect.Right, PDFViewerActualHeight)); break; default: break; } drawDc?.DrawRectangle(moveBrush, RectPen, moveRect); } public virtual void ClearDraw() { drawDefaultRect = setDrawRect = drawRect = new Rect(); drawDC = RenderOpen(); drawDC?.Close(); drawDC = null; } /// <summary> /// Calculate the current control point /// </summary> /// <param name="currentRect"> /// Target rectangle where the control point is located /// </param> protected void CalcControlPoint(Rect currentRect) { controlPoints.Clear(); int centerX = (int)(currentRect.Left + currentRect.Right) / 2; int centerY = (int)(currentRect.Top + currentRect.Bottom) / 2; controlPoints.Add(new Point(currentRect.Left, currentRect.Top)); controlPoints.Add(new Point(currentRect.Left, centerY)); controlPoints.Add(new Point(currentRect.Left, currentRect.Bottom)); controlPoints.Add(new Point(centerX, currentRect.Bottom)); controlPoints.Add(new Point(currentRect.Right, currentRect.Bottom)); controlPoints.Add(new Point(currentRect.Right, centerY)); controlPoints.Add(new Point(currentRect.Right, currentRect.Top)); controlPoints.Add(new Point(centerX, currentRect.Top)); } /// <summary> /// Get the original set Rect, not the one that has been calculated for padding /// </summary> public Rect GetRect() { Rect rect = new Rect(drawRect.X + rectPadding, drawRect.Y + rectPadding, Math.Max(0, drawRect.Width - 2 * rectPadding), Math.Max(0, drawRect.Height - 2 * rectPadding)); return rect; } public void SetRect(Rect newRect) { newRect = new Rect(newRect.X - rectPadding, newRect.Y - rectPadding, newRect.Width + 2 * rectPadding, newRect.Height + 2 * rectPadding); if (drawDefaultRect != new Rect()) { newRect.Union(drawDefaultRect); newRect.Intersect(maxRect); } drawDefaultRect = newRect; setDrawRect = drawRect = newRect; } public void SetRectPadding(double rectPadding) { this.rectPadding = rectPadding; } public double GetRectPadding() { return rectPadding; } public Rect GetDrawRect() { return drawRect; } public void SetMaxRect(Rect rect) { maxRect = rect; } public Rect GetMaxRect() { return maxRect; } public void SetIsProportionalScaling(bool isProportionalScaling) { this.isProportionalScaling = isProportionalScaling; } public void SetDrawType(DrawPointType drawType) { currentDrawPointType = drawType; } public void SetDrawMoveType(DrawMoveType drawType) { currentDrawMoveType = drawType; } /// <summary> /// Set the type that needs to be ignored /// </summary> /// <param name="types"> /// Collection of point types that need to be shielded /// </param> public void SetIgnorePoints(List<PointControlType> types) { ignorePoints.Clear(); foreach (PointControlType type in types) { ignorePoints.Add(type); } } /// <summary> /// Set all points to be ignored /// </summary> public void SetIgnorePointsAll() { ignorePoints.Clear(); ignorePoints.Add(PointControlType.LeftTop); ignorePoints.Add(PointControlType.LeftMiddle); ignorePoints.Add(PointControlType.LeftBottom); ignorePoints.Add(PointControlType.MiddleBottom); ignorePoints.Add(PointControlType.RightBottom); ignorePoints.Add(PointControlType.RightMiddle); ignorePoints.Add(PointControlType.RightTop); ignorePoints.Add(PointControlType.MiddleTop); } /// <summary> /// Disable all functions /// </summary> public void DisableAll() { ignorePoints.Clear(); ignorePoints.Add(PointControlType.LeftTop); ignorePoints.Add(PointControlType.LeftMiddle); ignorePoints.Add(PointControlType.LeftBottom); ignorePoints.Add(PointControlType.MiddleBottom); ignorePoints.Add(PointControlType.RightBottom); ignorePoints.Add(PointControlType.RightMiddle); ignorePoints.Add(PointControlType.RightTop); ignorePoints.Add(PointControlType.MiddleTop); ignorePoints.Add(PointControlType.Rotate); ignorePoints.Add(PointControlType.Body); ignorePoints.Add(PointControlType.Line); } /// <summary> /// Calculate the movement of the hit point /// </summary> /// <param name="mousePoint"> /// Current mouse position /// </param> /// <returns></returns> protected bool CalcHitPointMove(Point mousePoint) { if (isMouseDown == false || hitControlType == PointControlType.None) { return false; } return NormalScaling(mousePoint); } /// <summary> /// Draw the algorithm of the normal scaling form (drag a point, only scale in one direction) /// </summary> /// <param name="mousePoint"> /// Current mouse position /// </param> /// <returns></returns> protected bool NormalScaling(Point mousePoint) { double mLeft = cacheRect.Left; double mRight = cacheRect.Right; double mUp = cacheRect.Top; double mDown = cacheRect.Bottom; double TmpLeft = mLeft, TmpRight = mRight, TmpUp = mUp, TmpDown = mDown; Point centerPoint = new Point((cacheRect.Right + cacheRect.Left) / 2, (cacheRect.Bottom + cacheRect.Top) / 2); Point moveVector = (Point)(mousePoint - centerPoint); moveVector = ProportionalScalingOffsetPos(moveVector); switch (hitControlType) { case PointControlType.LeftTop: TmpLeft = centerPoint.X + moveVector.X; TmpRight = cacheRect.Right; if (TmpLeft + rectMinWidth > TmpRight) { TmpLeft = TmpRight - rectMinWidth; } TmpUp = centerPoint.Y + moveVector.Y; TmpDown = cacheRect.Bottom; if (TmpUp + rectMinHeight > TmpDown) { TmpUp = TmpDown - rectMinHeight; } break; case PointControlType.LeftMiddle: TmpLeft = centerPoint.X + moveVector.X; TmpRight = cacheRect.Right; if (TmpLeft + rectMinWidth > TmpRight) { TmpLeft = TmpRight - rectMinWidth; } TmpUp = cacheRect.Top; TmpDown = cacheRect.Bottom; break; case PointControlType.LeftBottom: TmpLeft = centerPoint.X + moveVector.X; TmpRight = cacheRect.Right; if (TmpLeft + rectMinWidth > TmpRight) { TmpLeft = TmpRight - rectMinWidth; } TmpUp = cacheRect.Top; TmpDown = centerPoint.Y + moveVector.Y; if (TmpUp + rectMinHeight > TmpDown) { TmpDown = TmpUp + rectMinHeight; } break; case PointControlType.MiddleBottom: TmpLeft = cacheRect.Left; TmpRight = cacheRect.Right; TmpUp = cacheRect.Top; TmpDown = centerPoint.Y + moveVector.Y; if (TmpUp + rectMinHeight > TmpDown) { TmpDown = TmpUp + rectMinHeight; } break; case PointControlType.RightBottom: TmpLeft = cacheRect.Left; TmpRight = centerPoint.X + moveVector.X; if (TmpLeft + rectMinWidth > TmpRight) { TmpRight = TmpLeft + rectMinWidth; } TmpUp = cacheRect.Top; TmpDown = centerPoint.Y + moveVector.Y; if (TmpUp + rectMinHeight > TmpDown) { TmpDown = TmpUp + rectMinHeight; } break; case PointControlType.RightMiddle: TmpLeft = cacheRect.Left; TmpRight = centerPoint.X + moveVector.X; if (TmpLeft + rectMinWidth > TmpRight) { TmpRight = TmpLeft + rectMinWidth; } TmpUp = cacheRect.Top; TmpDown = cacheRect.Bottom; break; case PointControlType.RightTop: TmpLeft = cacheRect.Left; TmpRight = centerPoint.X + moveVector.X; if (TmpLeft + rectMinWidth > TmpRight) { TmpRight = TmpLeft + rectMinWidth; } TmpUp = centerPoint.Y + moveVector.Y; TmpDown = cacheRect.Bottom; if (TmpUp + rectMinHeight > TmpDown) { TmpUp = TmpDown - rectMinHeight; } break; case PointControlType.MiddleTop: TmpLeft = cacheRect.Left; TmpRight = cacheRect.Right; TmpUp = centerPoint.Y + moveVector.Y; TmpDown = cacheRect.Bottom; if (TmpUp + rectMinHeight > TmpDown) { TmpUp = TmpDown - rectMinHeight; } break; case PointControlType.Body: case PointControlType.Line: Point OffsetPos = CalcMoveBound(cacheRect, ((Point)(mousePoint - mouseDownPoint)), maxRect); TmpLeft = cacheRect.Left + OffsetPos.X; TmpRight = cacheRect.Right + OffsetPos.X; TmpUp = cacheRect.Top + OffsetPos.Y; TmpDown = cacheRect.Bottom + OffsetPos.Y; break; default: break; } if (TmpLeft < maxRect.Left) { TmpLeft = maxRect.Left; } if (TmpUp < maxRect.Top) { TmpUp = maxRect.Top; } if (TmpRight > maxRect.Right) { TmpRight = maxRect.Right; } if (TmpDown > maxRect.Bottom) { TmpDown = maxRect.Bottom; } if (TmpRight - TmpLeft < 0.0 || TmpDown - TmpUp < 0.0) { return false; } drawRect = new Rect(TmpLeft, TmpUp, TmpRight - TmpLeft, TmpDown - TmpUp); moveOffset = new Point(drawRect.X - cacheRect.X, drawRect.Y - cacheRect.Y); return true; } /// <summary> /// Proportional scaling offset calibration /// </summary> /// <param name="movePoint"> /// Offset value /// </param> /// <returns> /// Offset value after calibration /// </returns> protected Point ProportionalScalingOffsetPos(Point movePoint) { if (isProportionalScaling) { Point offsetPos = movePoint; double ratioX = cacheRect.Width > 0 ? cacheRect.Height / cacheRect.Width : 1; double ratioY = cacheRect.Height > 0 ? cacheRect.Width / cacheRect.Height : 1; switch (hitControlType) { case PointControlType.LeftTop: case PointControlType.RightBottom: offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? -1 : 1)); break; case PointControlType.LeftBottom: case PointControlType.RightTop: offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? 1 : -1)); break; case PointControlType.LeftMiddle: offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? 1 : -1)); break; case PointControlType.RightMiddle: offsetPos = new Point(movePoint.X, Math.Abs(movePoint.X) * ratioX * (movePoint.X < 0 ? -1 : 1)); break; case PointControlType.MiddleBottom: offsetPos = new Point(Math.Abs(movePoint.Y) * ratioY * (movePoint.Y < 0 ? 1 : -1), movePoint.Y); break; case PointControlType.MiddleTop: offsetPos = new Point(Math.Abs(movePoint.Y) * ratioY * (movePoint.Y < 0 ? -1 : 1), movePoint.Y); break; default: break; } return offsetPos; } else { return movePoint; } } /// <summary> /// Set left alignment within the set maximum rectangle /// </summary> public virtual void SetAlignLeftForMaxRect() { DrawAlignRect(AlignmentsHelp.SetAlignLeft(drawRect, maxRect)); } /// <summary> /// Set horizontal center alignment within the set maximum rectangle /// </summary> public virtual void SetAlignHorizonCenterForMaxRect() { DrawAlignRect(AlignmentsHelp.SetAlignHorizonCenter(drawRect, maxRect)); } /// <summary> /// Set right alignment within the set maximum rectangle /// </summary> public virtual void SetAlignRightForMaxRect() { DrawAlignRect(AlignmentsHelp.SetAlignRight(drawRect, maxRect)); } /// <summary> /// Set top alignment within the set maximum rectangle /// </summary> public virtual void SetAlignTopForMaxRect() { DrawAlignRect(AlignmentsHelp.SetAlignTop(drawRect, maxRect)); } /// <summary> /// Set vertical center alignment within the set maximum rectangle /// </summary> public virtual void SetAlignVerticalCenterForMaxRect() { DrawAlignRect(AlignmentsHelp.SetAlignVerticalCenter(drawRect, maxRect)); } /// <summary> /// Set Align center within the set maximum rectangle /// </summary> public virtual void SetAlignHorizonVerticalCenterForMaxRect() { DrawAlignRect(AlignmentsHelp.SetAlignHorizonVerticalCenter(drawRect, maxRect)); } /// <summary> /// Set bottom alignment within the set maximum rectangle /// </summary> public virtual void SetAlignBottomForMaxRect() { DrawAlignRect(AlignmentsHelp.SetAlignBottom(drawRect, maxRect)); } /// <summary> /// Draw the rectangle of the alignment function /// </summary> /// <param name="RectMovePoint"> /// Move distance required for the rectangle obtained by the alignment algorithm /// </param> private void DrawAlignRect(Point RectMovePoint) { double TmpLeft, TmpRight, TmpUp, TmpDown; Point OffsetPos = CalcMoveBound(drawRect, RectMovePoint, maxRect); TmpLeft = drawRect.Left + OffsetPos.X; TmpRight = drawRect.Right + OffsetPos.X; TmpUp = drawRect.Top + OffsetPos.Y; TmpDown = drawRect.Bottom + OffsetPos.Y; setDrawRect = drawRect = new Rect(TmpLeft, TmpUp, TmpRight - TmpLeft, TmpDown - TmpUp); Draw(); } /// <summary> /// Calculate the offset of the current rectangle within the maximum rectangle range /// </summary> /// <param name="currentRect"> /// The rectangle cached when pressed /// </param> /// <param name="offsetPoint"> /// The offset value equivalent to when pressed /// </param> /// <param name="maxRect"> /// The maximum rectangle range /// </param> /// <returns> /// Offset value after calculation /// </returns> protected Point CalcMoveBound(Rect currentRect, Point offsetPoint, Rect maxRect) { double cLeft = currentRect.Left; double cRight = currentRect.Right; double cUp = currentRect.Top; double cDown = currentRect.Bottom; double TmpLeft = cLeft + offsetPoint.X; double TmpRight = cRight + offsetPoint.X; double TmpUp = cUp + offsetPoint.Y; double TmpDown = cDown + offsetPoint.Y; if (TmpLeft < maxRect.Left) { TmpRight = (cRight - cLeft) + maxRect.Left; TmpLeft = maxRect.Left; } if (TmpUp < maxRect.Top) { TmpDown = (cDown - cUp) + maxRect.Top; TmpUp = maxRect.Top; } if (TmpRight > maxRect.Right) { TmpLeft = maxRect.Right - (cRight - cLeft); TmpRight = maxRect.Right; } if (TmpDown > maxRect.Bottom) { TmpUp = maxRect.Bottom - (cDown - cUp); TmpDown = maxRect.Bottom; } offsetPoint = new Point(TmpLeft - cLeft, TmpUp - cUp); return offsetPoint; } /// <summary> /// Used for notification events during the drawing data process/completion. /// </summary> /// <param name="isFinish"> /// Identifies whether the data has been changed /// </param> protected void InvokeDataChangEvent(bool isFinish) { if (isFinish) { DataChanged?.Invoke(this, moveOffset); } else { DataChanging?.Invoke(this, moveOffset); } } /// <summary> /// Get the current set of ignored points /// </summary> /// <returns> /// Dataset of ignored points /// </returns> private List<PointControlType> GetIgnorePoints() { List<PointControlType> IgnorePointsList = new List<PointControlType>(); foreach (PointControlType type in ignorePoints) { IgnorePointsList.Add(type); } return IgnorePointsList; } /// <summary> /// Get which control point the coordinate is on /// </summary> /// <param name="clickPoint"> /// Coordinate point /// </param> /// <returns> /// Control point type /// </returns> public PointControlType GetHitControlIndex(Point point) { HitTestResult hitResult = VisualTreeHelper.HitTest(this, point); if (hitResult != null && hitResult.VisualHit is DrawingVisual) { List<PointControlType> ignoreList = GetIgnorePoints(); List<Point> IgnorePointsList = new List<Point>(); foreach (PointControlType type in ignoreList) { if ((int)type < controlPoints.Count) { IgnorePointsList.Add(controlPoints[(int)type]); } } for (int i = 0; i < controlPoints.Count; i++) { Point checkPoint = controlPoints[i]; if (IgnorePointsList.Contains(checkPoint)) { continue; } switch (currentDrawPointType) { case DrawPointType.Circle: if (IgnorePointsList.Contains(checkPoint)) { continue; } Vector checkVector = checkPoint - point; double wlen = drawRect.Width; if (wlen > 50) { wlen = 20; } else { wlen = wlen / 3; } double hlen = drawRect.Height; if (hlen > 50) { hlen = 20; } else { hlen = wlen / 3; } if ((PointControlType)i == PointControlType.RightMiddle) { if (Math.Abs(point.X - checkPoint.X) < wlen && checkVector.Length < drawRect.Height / 3) { return (PointControlType)i; } } if ((PointControlType)i == PointControlType.LeftMiddle) { if (Math.Abs(point.X - checkPoint.X) < wlen && checkVector.Length < drawRect.Height / 3) { return (PointControlType)i; } } if ((PointControlType)i == PointControlType.MiddleTop) { if (Math.Abs(point.Y - checkPoint.Y) < hlen && checkVector.Length < drawRect.Width / 3) { return (PointControlType)i; } } if ((PointControlType)i == PointControlType.MiddleBottom) { if (Math.Abs(point.Y - checkPoint.Y) < hlen && checkVector.Length < drawRect.Width / 3) { return (PointControlType)i; } } if (checkVector.Length < pointSize) { return (PointControlType)i; } break; case DrawPointType.Square: Rect checkRect = new Rect(Math.Max(checkPoint.X - pointSize, 0), Math.Max(checkPoint.Y - pointSize, 0), pointSize * 2, pointSize * 2); if (checkRect.Contains(point)) { return (PointControlType)i; } break; default: break; } } Rect defrect = drawRect; defrect.X -= rectPadding; defrect.Y -= rectPadding; defrect.Width += rectPadding; defrect.Height += rectPadding; if (drawRect.Contains(point)) { Rect rect = new Rect( Math.Max(drawRect.X + rectPadding, 0), Math.Max(drawRect.Y + rectPadding, 0), drawRect.Width - 2 * rectPadding, drawRect.Height - 2 * rectPadding); if (rect.Contains(point)) { if (!ignoreList.Contains(PointControlType.Body)) { return PointControlType.Body; } } if (!ignoreList.Contains(PointControlType.Body)) { return PointControlType.Line; } } } return PointControlType.None; } /// <summary> /// Get the rectangle where the current point is located /// </summary> /// <param name="clickPoint"> /// Coordinate point /// </param> /// <returns> /// Control point type /// </returns> public SelectedRect GetHitControlRect(Point point) { HitTestResult hitResult = VisualTreeHelper.HitTest(this, point); if (hitResult != null && hitResult.VisualHit is DrawingVisual) { foreach (SelectedRect selectedRect in selectedRects) { Rect defrect = selectedRect.GetRect(); defrect.X -= rectPadding; defrect.Y -= rectPadding; defrect.Width += rectPadding; defrect.Height += rectPadding; if (defrect.Contains(point)) { Rect rect = new Rect( Math.Max(defrect.X + rectPadding, 0), Math.Max(defrect.Y + rectPadding, 0), defrect.Width - 2 * rectPadding, defrect.Height - 2 * rectPadding); if (rect.Contains(point)) { return selectedRect; } } } } return null; } } }