目录
实现思路
收集路径点集。
平均采样路径点集。
将路径点集转为 LineB。
把 LineB 数据传给 Path。
实现效果
实现代码
1) Vector2D.cs 代码如下 - usingSystem;
- usingSystem.Collections.Generic;
- usingSystem.Linq;
- usingSystem.Text;
- namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
- {
- publicclassVector2D
- {
- publicdoubleX{get;set;}=0;
- publicdoubleY{get;set;}=0;
- ///<summary>
- ///向量的模
- ///</summary>
- publicdoubleMold
- {
- get
- {
- //自身各分量平方运算.
- doubleX=this.X*this.X;
- doubleY=this.Y*this.Y;
- returnMath.Sqrt(X+Y);//开根号,最终返回向量的长度/模/大小.
- }
- }
- ///<summary>
- ///单位向量
- ///</summary>
- publicVector2DUnitVector
- {
- get
- {
- doublesumSquares=(X*X)+(Y*Y);
- returnnewVector2D(X/Math.Sqrt(sumSquares),Y/Math.Sqrt(sumSquares));
- }
- }
- publicVector2D()
- {
- }
- publicVector2D(doublex,doubley)
- {
- X=x;
- Y=y;
- }
- publicVector2D(System.Windows.Pointpoint)
- {
- X=point.X;
- Y=point.Y;
- }
- publicvoidOffset(doubleangle,doubledistance,AngleTypeangleType=AngleType.Radian)
- {
- varvector2D=Vector2D.CalculateVectorOffset(this,angle,distance,angleType);
- X=vector2D.X;
- Y=vector2D.Y;
- }
- publicvoidRotate(doubleangle,Vector2DvectorCenter=null,AngleTypeangleType=AngleType.Radian)
- {
- vectorCenter=vectorCenter==null?this:vectorCenter;
- varvector2D=Vector2D.CalculateVectorRotation(this,vectorCenter,angle,angleType);
- X=vector2D.X;
- Y=vector2D.Y;
- }
- #region静态方法
- ///<summary>
- ///计算两个向量之间的距离
- ///</summary>
- publicstaticdoubleCalculateVectorDistance(Vector2Dvector2DA,Vector2Dvector2DB)
- {
- Vector2Dvector2D=vector2DA-vector2DB;
- returnvector2D.Mold;
- }
- ///<summary>
- ///计算两点夹角,右侧X轴线为0度,向下为正,向上为负
- ///</summary>
- publicstaticdoubleIncludedAngleXAxis(Vector2Dvector2DA,Vector2Dvector2DB,AngleTypeangleType=AngleType.Radian)
- {
- doubleradian=Math.Atan2(vector2DB.Y-vector2DA.Y,vector2DB.X-vector2DA.X);//弧度:1.1071487177940904
- returnangleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian);
- }
- ///<summary>
- ///计算两点夹角,下侧Y轴线为0度,向右为正,向左为负
- ///</summary>
- publicstaticdoubleIncludedAngleYAxis(Vector2Dvector2DA,Vector2Dvector2DB,AngleTypeangleType=AngleType.Radian)
- {
- doubleradian=Math.Atan2(vector2DB.X-vector2DA.X,vector2DB.Y-vector2DA.Y);//弧度:0.46364760900080609
- returnangleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian);
- }
- ///<summary>
- ///偏移向量到指定角度,指定距离
- ///</summary>
- publicstaticVector2DCalculateVectorOffset(Vector2Dvector2D,doubleangle,doubledistance,AngleTypeangleType=AngleType.Radian)
- {
- Vector2DpointVector2D=newVector2D();
- if(angleType==AngleType.Angle)
- {
- angle=angle/(180/Math.PI);//角度转弧度
- }
- doublewidth=Math.Cos(Math.Abs(angle))*distance;
- doubleheight=Math.Sin(Math.Abs(angle))*distance;
- if(angle<=Math.PI&&angle>=0)
- //if(angleis<=Math.PIand>=0)
- {
- pointVector2D.X=vector2D.X-width;
- pointVector2D.Y=vector2D.Y-height;
- }
- if(angle>=(-Math.PI)&&angle<=0)
- //if(angleis>=(-Math.PI)and<=0)
- {
- pointVector2D.X=vector2D.X-width;
- pointVector2D.Y=vector2D.Y+height;
- }
- returnpointVector2D;
- }
- ///<summary>
- ///围绕一个中心点,旋转一个向量,相对旋转
- ///</summary>
- publicstaticVector2DCalculateVectorRotation(Vector2Dvector2D,Vector2DvectorCenter,doubleradian,AngleTypeangleType=AngleType.Radian)
- {
- radian=angleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian);
- doublex1=(vector2D.X-vectorCenter.X)*Math.Sin(radian)+(vector2D.Y-vectorCenter.Y)*Math.Cos(radian)+vectorCenter.X;
- doubley1=-(vector2D.X-vectorCenter.X)*Math.Cos(radian)+(vector2D.Y-vectorCenter.Y)*Math.Sin(radian)+vectorCenter.Y;
- returnnewVector2D(x1,y1);
- }
- publicstaticVector2DCalculateVectorCenter(Vector2Dvector2DA,Vector2Dvector2DB)
- {
- returnnewVector2D((vector2DA.X+vector2DB.X)/2,(vector2DA.Y+vector2DB.Y)/2);
- }
- ///<summary>
- ///判断坐标点是否在多边形区域内,射线法
- ///</summary>
- publicstaticboolIsPointPolygonalArea(Vector2Dvector2D,List<Vector2D>aolygonaArrayList)
- {
- varN=aolygonaArrayList.Count;
- varboundOrVertex=true;//如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
- varcrossNumber=0;//x的交叉点计数
- varprecision=2e-10;//浮点类型计算时候与0比较时候的容差
- Vector2Dp1,p2;//neighbourboundvertices
- varp=vector2D;//测试点
- p1=aolygonaArrayList[0];//leftvertex
- for(vari=1;i<=N;++i)
- {
- //checkallrays
- if(p.X.Equals(p1.X)&&p.Y.Equals(p1.Y))
- {
- returnboundOrVertex;//pisanvertex
- }
- p2=aolygonaArrayList[i%N];//rightvertex
- if(p.X<Math.Min(p1.X,p2.X)||p.X>Math.Max(p1.X,p2.X))
- {
- //rayisoutsideofourinterests
- p1=p2;
- continue;//nextrayleftpoint
- }
- if(p.X>Math.Min(p1.X,p2.X)&&p.X<Math.Max(p1.X,p2.X))
- {
- //rayiscrossingoverbythealgorithm(commonpartof)
- if(p.Y<=Math.Max(p1.Y,p2.Y))
- {
- //xisbeforeofray
- if(p1.X==p2.X&&p.Y>=Math.Min(p1.Y,p2.Y))
- {
- //overliesonahorizontalray
- returnboundOrVertex;
- }
- if(p1.Y==p2.Y)
- {
- //rayisvertical
- if(p1.Y==p.Y)
- {
- //overliesonaverticalray
- returnboundOrVertex;
- }
- else
- {
- //beforeray
- ++crossNumber;
- }
- }
- else
- {
- //crosspointontheleftside
- varxinters=
- (p.X-p1.X)*(p2.Y-p1.Y)/(p2.X-p1.X)+
- p1.Y;//crosspointofY
- if(Math.Abs(p.Y-xinters)<precision)
- {
- //overliesonaray
- returnboundOrVertex;
- }
- if(p.Y<xinters)
- {
- //beforeray
- ++crossNumber;
- }
- }
- }
- }
- else
- {
- //specialcasewhenrayiscrossingthroughthevertex
- if(p.X==p2.X&&p.Y<=p2.Y)
- {
- //pcrossingoverp2
- varp3=aolygonaArrayList[(i+1)%N];//nextvertex
- if(p.X>=Math.Min(p1.X,p3.X)&&p.X<=Math.Max(p1.X,p3.X))
- {
- //p.Xliesbetweenp1.X&p3.X
- ++crossNumber;
- }
- else
- {
- crossNumber+=2;
- }
- }
- }
- p1=p2;//nextrayleftpoint
- }
- if(crossNumber%2==0)
- {
- //偶数在多边形外
- returnfalse;
- }
- else
- {
- //奇数在多边形内
- returntrue;
- }
- }
- ///<summary>
- ///判断一个点是否在一条边内
- ///</summary>
- publicstaticboolIsPointEdge(Vector2Dpoint,Vector2DstartPoint,Vector2DendPoint)
- {
- return(point.X-startPoint.X)*(endPoint.Y-startPoint.Y)==(endPoint.X-startPoint.X)*(point.Y-startPoint.Y)
- &&Math.Min(startPoint.X,endPoint.X)<=point.X&&point.X<=Math.Max(startPoint.X,endPoint.X)
- &&Math.Min(startPoint.Y,endPoint.Y)<=point.Y&&point.Y<=Math.Max(startPoint.Y,endPoint.Y);
- }
- #endregion静态方法
- #region运算符重载
- ///<summary>
- ///重载运算符,和运算,可以用来计算两向量距离
- ///</summary>
- publicstaticVector2Doperator+(Vector2Dvector2DA,Vector2Dvector2DB)
- {
- Vector2Dvector2D=newVector2D();
- vector2D.X=vector2DA.X+vector2DB.X;
- vector2D.Y=vector2DA.Y+vector2DB.Y;
- returnvector2D;
- }
- ///<summary>
- ///重载运算符,差运算,可以用来计算两向量距离
- ///</summary>
- publicstaticVector2Doperator-(Vector2Dvector2DA,Vector2Dvector2DB)
- {
- Vector2Dvector2D=newVector2D();
- vector2D.X=vector2DA.X-vector2DB.X;
- vector2D.Y=vector2DA.Y-vector2DB.Y;
- returnvector2D;
- }
- ///<summary>
- ///重载运算符,差运算,可以用来计算两向量距离
- ///</summary>
- publicstaticVector2Doperator-(Vector2Dvector2D,double_float)
- {
- returnnewVector2D(vector2D.X-_float,vector2D.Y-_float);
- }
- ///<summary>
- ///重载运算符,点积运算,可以用来计算两向量夹角
- ///</summary>
- publicstaticdoubleoperator*(Vector2Dvector2DA,Vector2Dvector2DB)
- {
- return(vector2DA.X*vector2DB.X)+(vector2DA.Y*vector2DB.Y);
- }
- publicstaticdoubleoperator*(Vector2Dvector2D,double_float)
- {
- return(vector2D.X*_float)+(vector2D.Y*_float);
- }
- ///<summary>
- ///重载运算符,点积运算,可以用来计算两向量夹角
- ///</summary>
- publicstaticdoubleoperator/(Vector2Dvector2D,doublepara)
- {
- return(vector2D.X/para)+(vector2D.Y/para);
- }
- ///<summary>
- ///重载运算符
- ///</summary>
- publicstaticbooloperator>=(Vector2Dvector2D,doublepara)
- {
- if(vector2D.Mold>=para)
- {
- returntrue;
- }
- else
- {
- returnfalse;
- }
- }
- publicstaticbooloperator<=(Vector2Dvector2D,doublepara)
- {
- if(vector2D.Mold<=para)
- {
- returntrue;
- }
- else
- {
- returnfalse;
- }
- }
- publicstaticbooloperator>(Vector2Dvector2D,doublepara)
- {
- if(vector2D.Mold>para)
- {
- returntrue;
- }
- else
- {
- returnfalse;
- }
- }
- publicstaticbooloperator<(Vector2Dvector2D,doublepara)
- {
- if(vector2D.Mold<para)
- {
- returntrue;
- }
- else
- {
- returnfalse;
- }
- }
- #endregion运算符重载
- #region隐式转换
- ///<summary>
- ///重载隐式转换,可以直接使用Point
- ///</summary>
- ///<paramname="v"></param>
- publicstaticimplicitoperatorVector2D(System.Windows.Pointv)//隐式转换
- {
- returnnewVector2D(v.X,v.Y);
- }
- ///<summary>
- ///重载隐式转换,可以直接使用Point
- ///</summary>
- ///<paramname="v"></param>
- publicstaticimplicitoperatorSystem.Windows.Point(Vector2Dv)//隐式转换
- {
- returnnewSystem.Windows.Point(v.X,v.Y);
- }
- ///<summary>
- ///重载隐式转换,可以直接使用double
- ///</summary>
- ///<paramname="v"></param>
- publicstaticimplicitoperatorVector2D(doublev)//隐式转换
- {
- returnnewVector2D(v,v);
- }
- #endregion隐式转换
- #regionToString
- publicoverridestringToString()
- {
- returnX.ToString()+","+Y.ToString();
- }
- publicstringToString(stringsymbol)
- {
- returnX.ToString()+symbol+Y.ToString();
- }
- publicstringToString(stringsender,stringsymbol)
- {
- returnX.ToString(sender)+symbol+Y.ToString(sender);
- }
- #endregion
- }
- publicenumAngleType
- {
- Angle,
- Radian
- }
- }
复制代码2) ComputingHelper.cs 代码如下 - usingSystem;
- usingSystem.Collections.Generic;
- usingSystem.Linq;
- usingSystem.Text;
- namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
- {
- publicstaticclassComputingHelper
- {
- publicstaticdoubleAngleToRadian(doubleangle)
- {
- returnangle*(Math.PI/180);
- }
- publicstaticdoubleRadianToAngle(doubleradian)
- {
- returnradian*(180/Math.PI);
- }
- ///<summary>
- ///将一个值从一个范围映射到另一个范围
- ///</summary>
- publicstaticdoubleRangeMapping(doubleinputValue,doubleenterLowerLimit,doubleenterUpperLimit,doubleoutputLowerLimit,doubleOutputUpperLimit,CurveTypecurveType=CurveType.None)
- {
- varpercentage=(enterUpperLimit-inputValue)/(enterUpperLimit-enterLowerLimit);
- switch(curveType)
- {
- caseCurveType.Sine:
- percentage=Math.Sin(percentage);
- break;
- caseCurveType.CoSine:
- percentage=Math.Cos(percentage);
- break;
- caseCurveType.Tangent:
- percentage=Math.Tan(percentage);
- break;
- caseCurveType.Cotangent:
- percentage=Math.Atan(percentage);
- break;
- default:
- break;
- }
- doubleoutputValue=OutputUpperLimit-((OutputUpperLimit-outputLowerLimit)*percentage);
- returnoutputValue;
- }
- publicstaticstringByteToKB(double_byte)
- {
- List<string>unit=newList<string>(){"B","KB","MB","GB","TB","P","PB"};
- inti=0;
- while(_byte>1024)
- {
- _byte/=1024;
- i++;
- }
- _byte=Math.Round(_byte,3);//保留三位小数
- return_byte+unit[i];
- }
- ///<summary>
- ///缩短一个数组,对其进行平均采样
- ///</summary>
- publicstaticdouble[]AverageSampling(double[]sourceArray,intnumber)
- {
- if(sourceArray.Length<=number)
- {
- returnsourceArray;
- //thrownewException("新的数组必须比原有的要小!");
- }
- double[]arrayList=newdouble[number];
- doublestride=(double)sourceArray.Length/number;
- for(inti=0,jIndex=0;i<number;i++,jIndex++)
- {
- doublestrideIncrement=i*stride;
- strideIncrement=Math.Round(strideIncrement,6);
- doublesum=0;
- intfirstIndex=(int)(strideIncrement);
- doublefirstDecimal=strideIncrement-firstIndex;
- inttailIndex=(int)(strideIncrement+stride);
- doubletailDecimal=(strideIncrement+stride)-tailIndex;
- if(firstDecimal!=0)
- sum+=sourceArray[firstIndex]*(1-firstDecimal);
- if(tailDecimal!=0&&tailIndex!=sourceArray.Length)
- sum+=sourceArray[tailIndex]*(tailDecimal);
- intstartIndex=firstDecimal==0?firstIndex:firstIndex+1;
- intendIndex=tailIndex;
- for(intj=startIndex;j<endIndex;j++)
- sum+=sourceArray[j];
- arrayList[jIndex]=sum/stride;
- }
- returnarrayList;
- }
- publicstaticList<Vector2D>AverageSampling(List<Vector2D>sourceArray,intnumber)
- {
- if(sourceArray.Count<=number-2)
- {
- returnsourceArray;
- }
- double[]x=newdouble[sourceArray.Count];
- double[]y=newdouble[sourceArray.Count];
- for(inti=0;i<sourceArray.Count;i++)
- {
- x[i]=sourceArray[i].X;
- y[i]=sourceArray[i].Y;
- }
- double[]X=AverageSampling(x,number-2);
- double[]Y=AverageSampling(y,number-2);
- List<Vector2D>arrayList=newList<Vector2D>();
- for(inti=0;i<number-2;i++)
- {
- arrayList.Add(newVector2D(X[i],Y[i]));
- }
- arrayList.Insert(0,sourceArray[0]);//添加首
- arrayList.Add(sourceArray[sourceArray.Count-1]);//添加尾
- returnarrayList;
- }
- }
- publicenumCurveType
- {
- Sine,
- CoSine,
- Tangent,
- Cotangent,
- None
- }
- }
复制代码3) LineB.cs 代码如下 - usingSystem;
- usingSystem.Collections.Generic;
- usingSystem.Linq;
- usingSystem.Text;
- usingSystem.Windows.Media;
- namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
- {
- publicclassLineB
- {
- privateList<Vector2D>_vector2DList=newList<Vector2D>();
- publicList<Vector2D>Vector2DList
- {
- get{return_vector2DList;}
- set
- {
- _vector2DList=value;
- }
- }
- privateList<BezierCurve>_bezierCurveList=newList<BezierCurve>();
- publicList<BezierCurve>BezierCurveList
- {
- get{return_bezierCurveList;}
- privateset{_bezierCurveList=value;}
- }
- privatedouble_tension=0.618;
- publicdoubleTension
- {
- get{return_tension;}
- set
- {
- _tension=value;
- if(_tension>10)
- _tension=10;
- if(_tension<0)
- _tension=0;
- }
- }
- privatebool_isClosedCurve=true;
- publicboolIsClosedCurve
- {
- get{return_isClosedCurve;}
- set{_isClosedCurve=value;}
- }
- privatestring_pathData=string.Empty;
- publicstringPathData
- {
- get
- {
- if(_pathData==string.Empty)
- {
- _pathData=Vector2DToBezierCurve();
- }
- return_pathData;
- }
- }
- privatestringVector2DToBezierCurve()
- {
- if(Vector2DList.Count<3)
- returnstring.Empty;
- BezierCurveList.Clear();
- for(inti=0;i<Vector2DList.Count;i++)
- {
- intpointTwoIndex=i+1<Vector2DList.Count?i+1:0;
- intpointThreeIndex=i+2<Vector2DList.Count?i+2:i+2-Vector2DList.Count;
- Vector2Dvector2D1=Vector2DList[i];
- Vector2Dvector2D2=Vector2DList[pointTwoIndex];
- Vector2Dvector2D3=Vector2DList[pointThreeIndex];
- Vector2DstartVector2D=Vector2D.CalculateVectorCenter(vector2D1,vector2D2);
- doublestartAngle=Vector2D.IncludedAngleXAxis(vector2D1,vector2D2);
- doublestartDistance=Vector2D.CalculateVectorDistance(startVector2D,vector2D2)*(1-Tension);
- Vector2DstartControlPoint=Vector2D.CalculateVectorOffset(vector2D2,startAngle,startDistance);
- Vector2DendVector2D=Vector2D.CalculateVectorCenter(vector2D2,vector2D3);
- doubleendAngle=Vector2D.IncludedAngleXAxis(endVector2D,vector2D2);
- doubleendDistance=Vector2D.CalculateVectorDistance(endVector2D,vector2D2)*(1-Tension);
- Vector2DendControlPoint=Vector2D.CalculateVectorOffset(endVector2D,endAngle,endDistance);
- BezierCurvebezierCurve=newBezierCurve();
- bezierCurve.StartVector2D=startVector2D;
- bezierCurve.StartControlPoint=startControlPoint;
- bezierCurve.EndVector2D=endVector2D;
- bezierCurve.EndControlPoint=endControlPoint;
- BezierCurveList.Add(bezierCurve);
- }
- if(!IsClosedCurve)
- {
- BezierCurveList[0].StartVector2D=Vector2DList[0];
- BezierCurveList.RemoveAt(BezierCurveList.Count-1);
- BezierCurveList[BezierCurveList.Count-1].EndVector2D=Vector2DList[Vector2DList.Count-1];
- BezierCurveList[BezierCurveList.Count-1].EndControlPoint=BezierCurveList[BezierCurveList.Count-1].EndVector2D;
- }
- stringpath=$"M{BezierCurveList[0].StartVector2D.ToString()}";
- foreach(variteminBezierCurveList)
- {
- path+=$"C{item.StartControlPoint.ToString("")},{item.EndControlPoint.ToString("")},{item.EndVector2D.ToString("")}";
- }
- returnpath;
- }
- publicLineB()
- {
- }
- publicLineB(List<Vector2D>verVector2DList,boolisClosedCurve=true)
- {
- this.Vector2DList=verVector2DList;
- this.IsClosedCurve=isClosedCurve;
- }
- ///<summary>
- ///重载隐式转换,可以直接使用Point
- ///</summary>
- ///<paramname="v"></param>
- publicstaticimplicitoperatorGeometry(LineBlineB)//隐式转换
- {
- returnGeometry.Parse(lineB.PathData);
- }
- }
- publicclassBezierCurve
- {
- privateVector2D_startVector2D=newVector2D(0,0);
- publicVector2DStartVector2D
- {
- get{return_startVector2D;}
- set{_startVector2D=value;}
- }
- privateVector2D_startControlPoint=newVector2D(0,100);
- publicVector2DStartControlPoint
- {
- get{return_startControlPoint;}
- set{_startControlPoint=value;}
- }
- privateVector2D_endControlPoint=newVector2D(100,0);
- publicVector2DEndControlPoint
- {
- get{return_endControlPoint;}
- set{_endControlPoint=value;}
- }
- privateVector2D_endVector2D=newVector2D(100,100);
- publicVector2DEndVector2D
- {
- get{return_endVector2D;}
- set{_endVector2D=value;}
- }
- }
- }
复制代码4) CanvasHandWritingExample.xaml 代码如下 - <UserControlx:Class="WPFDevelopers.Samples.ExampleViews.CanvasHandWriting.CanvasHandWritingExample"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews.CanvasHandWriting"
- mc:Ignorable="d"
- d:DesignHeight="450"d:DesignWidth="800">
- <UserControl.Resources>
- <StyleTargetType="{x:TypeTextBlock}">
- <SetterProperty="Foreground"Value="{StaticResourcePrimaryTextSolidColorBrush}"/>
- </Style>
- </UserControl.Resources>
- <Grid>
- <Grid.RowDefinitions>
- <RowDefinitionHeight="auto"/>
- <RowDefinition/>
- </Grid.RowDefinitions>
- <StackPanelOrientation="Horizontal"Margin="4">
- <TextBlockText="张力:"VerticalAlignment="Center"/>
- <TextBoxText="{BindingTension,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"/>
- <SliderWidth="100"SmallChange="0.01"
- Value="{BindingTension,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"Maximum="1"
- VerticalAlignment="Center"
- Margin="5,0"/>
- <TextBlockText="平滑采样:"VerticalAlignment="Center"/>
- <TextBoxText="{BindingSmoothSampling,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"
- Margin="5,0"/>
- <SliderValue="{BindingSmoothSampling,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"
- Width="100"
- VerticalAlignment="Center"
- SmallChange="0.01"Maximum="1"
- TickFrequency="0.1"/>
- <CheckBoxContent="橡皮擦"
- VerticalAlignment="Center"
- Margin="5,0"
- IsChecked="{BindingIsEraser,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"/>
- <ButtonContent="清空画布"Click="btnClertCanvas_Click"/>
- </StackPanel>
- <Canvasx:Name="drawingCanvas"
- Grid.Row="1"Background="Black"
- PreviewMouseLeftButtonDown="DrawingCanvas_PreviewMouseLeftButtonDown"
- PreviewMouseMove="DrawingCanvas_PreviewMouseMove"
- PreviewMouseLeftButtonUp="DrawingCanvas_PreviewMouseLeftButtonUp"/>
- </Grid>
- </UserControl>
复制代码5) CanvasHandWritingExample.xaml.cs 代码如下 - usingSystem;
- usingSystem.Collections.Generic;
- usingSystem.Threading;
- usingSystem.Threading.Tasks;
- usingSystem.Windows;
- usingSystem.Windows.Controls;
- usingSystem.Windows.Input;
- usingSystem.Windows.Media;
- usingSystem.Windows.Shapes;
- namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
- {
- ///<summary>
- ///CanvasHandWritingExample.xaml的交互逻辑
- ///</summary>
- publicpartialclassCanvasHandWritingExample:UserControl
- {
- publicstaticreadonlyDependencyPropertyTensionProperty=
- DependencyProperty.Register("Tension",typeof(double),typeof(CanvasHandWritingExample),
- newPropertyMetadata(0.618));
- publicstaticreadonlyDependencyPropertySmoothSamplingProperty=
- DependencyProperty.Register("SmoothSampling",typeof(double),typeof(CanvasHandWritingExample),
- newUIPropertyMetadata(OnSmoothSamplingChanged));
- publicstaticreadonlyDependencyPropertyIsEraserProperty=
- DependencyProperty.Register("IsEraser",typeof(bool),typeof(CanvasHandWritingExample),
- newPropertyMetadata(false));
- privatereadonlyDictionary<Path,List<Vector2D>>_PathVector2DDictionary;
- volatilebool_IsStart=false;
- Path_DrawingPath=default;
- privatestaticvoidOnSmoothSamplingChanged(DependencyObjectd,DependencyPropertyChangedEventArgse)
- {
- varmWindow=(CanvasHandWritingExample)d;
- foreach(variteminmWindow._PathVector2DDictionary.Keys)
- {
- mWindow.DrawLine(item);
- }
- }
- publicCanvasHandWritingExample()
- {
- InitializeComponent();
- _PathVector2DDictionary=newDictionary<Path,List<Vector2D>>();
- SmoothSampling=0.8;
- }
- privatevoidDrawingCanvas_PreviewMouseLeftButtonDown(objectsender,MouseButtonEventArgse)
- {
- _IsStart=true;
- _DrawingPath=newPath()
- {
- StrokeDashCap=PenLineCap.Round,
- StrokeStartLineCap=PenLineCap.Round,
- StrokeEndLineCap=PenLineCap.Round,
- StrokeLineJoin=PenLineJoin.Round,
- };
- if(IsEraser)
- {
- _DrawingPath.Stroke=newSolidColorBrush(Colors.Black);
- _DrawingPath.StrokeThickness=40;
- }
- else
- {
- varrandom=newRandom();
- varstrokeBrush=newSolidColorBrush(Color.FromRgb((byte)random.Next(200,255),(byte)random.Next(0,255),(byte)random.Next(0,255)));
- _DrawingPath.Stroke=strokeBrush;
- _DrawingPath.StrokeThickness=10;
- }
- _PathVector2DDictionary.Add(_DrawingPath,newList<Vector2D>());
- drawingCanvas.Children.Add(_DrawingPath);
- }
- privatevoidDrawingCanvas_PreviewMouseLeftButtonUp(objectsender,MouseButtonEventArgse)
- {
- _IsStart=false;
- _DrawingPath=default;
- }
- privatevoidDrawingCanvas_PreviewMouseMove(objectsender,MouseEventArgse)
- {
- if(!_IsStart)
- return;
- if(_DrawingPathisnull)
- return;
- Vector2DcurrenPoint=e.GetPosition(drawingCanvas);
- if(currenPoint.X<0||currenPoint.Y<0)
- return;
- if(currenPoint.X>drawingCanvas.ActualWidth||currenPoint.Y>drawingCanvas.ActualHeight)
- return;
- if(_PathVector2DDictionary[_DrawingPath].Count>0)
- {
- if(Vector2D.CalculateVectorDistance(currenPoint,_PathVector2DDictionary[_DrawingPath][_PathVector2DDictionary[_DrawingPath].Count-1])>1)
- _PathVector2DDictionary[_DrawingPath].Add(e.GetPosition(drawingCanvas));
- }
- else
- _PathVector2DDictionary[_DrawingPath].Add(e.GetPosition(drawingCanvas));
- DrawLine(_DrawingPath);
- }
- publicdoubleTension
- {
- get=>(double)GetValue(TensionProperty);
- set=>SetValue(TensionProperty,value);
- }
- publicdoubleSmoothSampling
- {
- get=>(double)GetValue(SmoothSamplingProperty);
- set=&g;SetValue(SmoothSamplingProperty,value);
- }
- publicboolIsEraser
- {
- get=>(bool)GetValue(IsEraserProperty);
- set=>SetValue(IsEraserProperty,value);
- }
- privatevoidDrawLine(Pathpath)
- {
- if(_PathVector2DDictionary[path].Count>2)
- {
- varpathVector2Ds=_PathVector2DDictionary[path];
- varsmoothNum=(int)(_PathVector2DDictionary[path].Count*SmoothSampling);
- if(smoothNum>1)
- pathVector2Ds=ComputingHelper.AverageSampling(_PathVector2DDictionary[path],smoothNum);
- varlineB=newLineB(pathVector2Ds,false);
- lineB.Tension=Tension;
- path.Data=lineB;
- }
- }
- privatevoidbtnClertCanvas_Click(objectsender,RoutedEventArgse)
- {
- drawingCanvas.Children.Clear();
- _PathVector2DDictionary.Clear();
- }
- }
- }
复制代码到此这篇关于WPF+Canvas实现平滑笔迹的示例代码的文章就介绍到这了,更多相关WPF Canvas平滑笔迹内容请搜索中国红客联盟以前的文章或继续浏览下面的相关文章希望大家以后多多支持中国红客联盟! |