[C.C++] WPF+Canvas实现平滑笔迹的示例代码

2050 0
Honkers 2022-11-9 09:42:12 | 显示全部楼层 |阅读模式
目录

    实现思路实现效果实现代码


实现思路

收集路径点集。
平均采样路径点集。
将路径点集转为 LineB
LineB 数据传给 Path

实现效果




实现代码

1)Vector2D.cs 代码如下
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Text;
  5. namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
  6. {
  7. publicclassVector2D
  8. {
  9. publicdoubleX{get;set;}=0;
  10. publicdoubleY{get;set;}=0;
  11. ///<summary>
  12. ///向量的模
  13. ///</summary>
  14. publicdoubleMold
  15. {
  16. get
  17. {
  18. //自身各分量平方运算.
  19. doubleX=this.X*this.X;
  20. doubleY=this.Y*this.Y;
  21. returnMath.Sqrt(X+Y);//开根号,最终返回向量的长度/模/大小.
  22. }
  23. }
  24. ///<summary>
  25. ///单位向量
  26. ///</summary>
  27. publicVector2DUnitVector
  28. {
  29. get
  30. {
  31. doublesumSquares=(X*X)+(Y*Y);
  32. returnnewVector2D(X/Math.Sqrt(sumSquares),Y/Math.Sqrt(sumSquares));
  33. }
  34. }
  35. publicVector2D()
  36. {
  37. }
  38. publicVector2D(doublex,doubley)
  39. {
  40. X=x;
  41. Y=y;
  42. }
  43. publicVector2D(System.Windows.Pointpoint)
  44. {
  45. X=point.X;
  46. Y=point.Y;
  47. }
  48. publicvoidOffset(doubleangle,doubledistance,AngleTypeangleType=AngleType.Radian)
  49. {
  50. varvector2D=Vector2D.CalculateVectorOffset(this,angle,distance,angleType);
  51. X=vector2D.X;
  52. Y=vector2D.Y;
  53. }
  54. publicvoidRotate(doubleangle,Vector2DvectorCenter=null,AngleTypeangleType=AngleType.Radian)
  55. {
  56. vectorCenter=vectorCenter==null?this:vectorCenter;
  57. varvector2D=Vector2D.CalculateVectorRotation(this,vectorCenter,angle,angleType);
  58. X=vector2D.X;
  59. Y=vector2D.Y;
  60. }
  61. #region静态方法
  62. ///<summary>
  63. ///计算两个向量之间的距离
  64. ///</summary>
  65. publicstaticdoubleCalculateVectorDistance(Vector2Dvector2DA,Vector2Dvector2DB)
  66. {
  67. Vector2Dvector2D=vector2DA-vector2DB;
  68. returnvector2D.Mold;
  69. }
  70. ///<summary>
  71. ///计算两点夹角,右侧X轴线为0度,向下为正,向上为负
  72. ///</summary>
  73. publicstaticdoubleIncludedAngleXAxis(Vector2Dvector2DA,Vector2Dvector2DB,AngleTypeangleType=AngleType.Radian)
  74. {
  75. doubleradian=Math.Atan2(vector2DB.Y-vector2DA.Y,vector2DB.X-vector2DA.X);//弧度:1.1071487177940904
  76. returnangleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian);
  77. }
  78. ///<summary>
  79. ///计算两点夹角,下侧Y轴线为0度,向右为正,向左为负
  80. ///</summary>
  81. publicstaticdoubleIncludedAngleYAxis(Vector2Dvector2DA,Vector2Dvector2DB,AngleTypeangleType=AngleType.Radian)
  82. {
  83. doubleradian=Math.Atan2(vector2DB.X-vector2DA.X,vector2DB.Y-vector2DA.Y);//弧度:0.46364760900080609
  84. returnangleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian);
  85. }
  86. ///<summary>
  87. ///偏移向量到指定角度,指定距离
  88. ///</summary>
  89. publicstaticVector2DCalculateVectorOffset(Vector2Dvector2D,doubleangle,doubledistance,AngleTypeangleType=AngleType.Radian)
  90. {
  91. Vector2DpointVector2D=newVector2D();
  92. if(angleType==AngleType.Angle)
  93. {
  94. angle=angle/(180/Math.PI);//角度转弧度
  95. }
  96. doublewidth=Math.Cos(Math.Abs(angle))*distance;
  97. doubleheight=Math.Sin(Math.Abs(angle))*distance;
  98. if(angle<=Math.PI&&angle>=0)
  99. //if(angleis<=Math.PIand>=0)
  100. {
  101. pointVector2D.X=vector2D.X-width;
  102. pointVector2D.Y=vector2D.Y-height;
  103. }
  104. if(angle>=(-Math.PI)&&angle<=0)
  105. //if(angleis>=(-Math.PI)and<=0)
  106. {
  107. pointVector2D.X=vector2D.X-width;
  108. pointVector2D.Y=vector2D.Y+height;
  109. }
  110. returnpointVector2D;
  111. }
  112. ///<summary>
  113. ///围绕一个中心点,旋转一个向量,相对旋转
  114. ///</summary>
  115. publicstaticVector2DCalculateVectorRotation(Vector2Dvector2D,Vector2DvectorCenter,doubleradian,AngleTypeangleType=AngleType.Radian)
  116. {
  117. radian=angleType==AngleType.Radian?radian:ComputingHelper.RadianToAngle(radian);
  118. doublex1=(vector2D.X-vectorCenter.X)*Math.Sin(radian)+(vector2D.Y-vectorCenter.Y)*Math.Cos(radian)+vectorCenter.X;
  119. doubley1=-(vector2D.X-vectorCenter.X)*Math.Cos(radian)+(vector2D.Y-vectorCenter.Y)*Math.Sin(radian)+vectorCenter.Y;
  120. returnnewVector2D(x1,y1);
  121. }
  122. publicstaticVector2DCalculateVectorCenter(Vector2Dvector2DA,Vector2Dvector2DB)
  123. {
  124. returnnewVector2D((vector2DA.X+vector2DB.X)/2,(vector2DA.Y+vector2DB.Y)/2);
  125. }
  126. ///<summary>
  127. ///判断坐标点是否在多边形区域内,射线法
  128. ///</summary>
  129. publicstaticboolIsPointPolygonalArea(Vector2Dvector2D,List<Vector2D>aolygonaArrayList)
  130. {
  131. varN=aolygonaArrayList.Count;
  132. varboundOrVertex=true;//如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
  133. varcrossNumber=0;//x的交叉点计数
  134. varprecision=2e-10;//浮点类型计算时候与0比较时候的容差
  135. Vector2Dp1,p2;//neighbourboundvertices
  136. varp=vector2D;//测试点
  137. p1=aolygonaArrayList[0];//leftvertex
  138. for(vari=1;i<=N;++i)
  139. {
  140. //checkallrays
  141. if(p.X.Equals(p1.X)&&p.Y.Equals(p1.Y))
  142. {
  143. returnboundOrVertex;//pisanvertex
  144. }
  145. p2=aolygonaArrayList[i%N];//rightvertex
  146. if(p.X<Math.Min(p1.X,p2.X)||p.X>Math.Max(p1.X,p2.X))
  147. {
  148. //rayisoutsideofourinterests
  149. p1=p2;
  150. continue;//nextrayleftpoint
  151. }
  152. if(p.X>Math.Min(p1.X,p2.X)&&p.X<Math.Max(p1.X,p2.X))
  153. {
  154. //rayiscrossingoverbythealgorithm(commonpartof)
  155. if(p.Y<=Math.Max(p1.Y,p2.Y))
  156. {
  157. //xisbeforeofray
  158. if(p1.X==p2.X&&p.Y>=Math.Min(p1.Y,p2.Y))
  159. {
  160. //overliesonahorizontalray
  161. returnboundOrVertex;
  162. }
  163. if(p1.Y==p2.Y)
  164. {
  165. //rayisvertical
  166. if(p1.Y==p.Y)
  167. {
  168. //overliesonaverticalray
  169. returnboundOrVertex;
  170. }
  171. else
  172. {
  173. //beforeray
  174. ++crossNumber;
  175. }
  176. }
  177. else
  178. {
  179. //crosspointontheleftside
  180. varxinters=
  181. (p.X-p1.X)*(p2.Y-p1.Y)/(p2.X-p1.X)+
  182. p1.Y;//crosspointofY
  183. if(Math.Abs(p.Y-xinters)<precision)
  184. {
  185. //overliesonaray
  186. returnboundOrVertex;
  187. }
  188. if(p.Y<xinters)
  189. {
  190. //beforeray
  191. ++crossNumber;
  192. }
  193. }
  194. }
  195. }
  196. else
  197. {
  198. //specialcasewhenrayiscrossingthroughthevertex
  199. if(p.X==p2.X&&p.Y<=p2.Y)
  200. {
  201. //pcrossingoverp2
  202. varp3=aolygonaArrayList[(i+1)%N];//nextvertex
  203. if(p.X>=Math.Min(p1.X,p3.X)&&p.X<=Math.Max(p1.X,p3.X))
  204. {
  205. //p.Xliesbetweenp1.X&p3.X
  206. ++crossNumber;
  207. }
  208. else
  209. {
  210. crossNumber+=2;
  211. }
  212. }
  213. }
  214. p1=p2;//nextrayleftpoint
  215. }
  216. if(crossNumber%2==0)
  217. {
  218. //偶数在多边形外
  219. returnfalse;
  220. }
  221. else
  222. {
  223. //奇数在多边形内
  224. returntrue;
  225. }
  226. }
  227. ///<summary>
  228. ///判断一个点是否在一条边内
  229. ///</summary>
  230. publicstaticboolIsPointEdge(Vector2Dpoint,Vector2DstartPoint,Vector2DendPoint)
  231. {
  232. return(point.X-startPoint.X)*(endPoint.Y-startPoint.Y)==(endPoint.X-startPoint.X)*(point.Y-startPoint.Y)
  233. &&Math.Min(startPoint.X,endPoint.X)<=point.X&&point.X<=Math.Max(startPoint.X,endPoint.X)
  234. &&Math.Min(startPoint.Y,endPoint.Y)<=point.Y&&point.Y<=Math.Max(startPoint.Y,endPoint.Y);
  235. }
  236. #endregion静态方法
  237. #region运算符重载
  238. ///<summary>
  239. ///重载运算符,和运算,可以用来计算两向量距离
  240. ///</summary>
  241. publicstaticVector2Doperator+(Vector2Dvector2DA,Vector2Dvector2DB)
  242. {
  243. Vector2Dvector2D=newVector2D();
  244. vector2D.X=vector2DA.X+vector2DB.X;
  245. vector2D.Y=vector2DA.Y+vector2DB.Y;
  246. returnvector2D;
  247. }
  248. ///<summary>
  249. ///重载运算符,差运算,可以用来计算两向量距离
  250. ///</summary>
  251. publicstaticVector2Doperator-(Vector2Dvector2DA,Vector2Dvector2DB)
  252. {
  253. Vector2Dvector2D=newVector2D();
  254. vector2D.X=vector2DA.X-vector2DB.X;
  255. vector2D.Y=vector2DA.Y-vector2DB.Y;
  256. returnvector2D;
  257. }
  258. ///<summary>
  259. ///重载运算符,差运算,可以用来计算两向量距离
  260. ///</summary>
  261. publicstaticVector2Doperator-(Vector2Dvector2D,double_float)
  262. {
  263. returnnewVector2D(vector2D.X-_float,vector2D.Y-_float);
  264. }
  265. ///<summary>
  266. ///重载运算符,点积运算,可以用来计算两向量夹角
  267. ///</summary>
  268. publicstaticdoubleoperator*(Vector2Dvector2DA,Vector2Dvector2DB)
  269. {
  270. return(vector2DA.X*vector2DB.X)+(vector2DA.Y*vector2DB.Y);
  271. }
  272. publicstaticdoubleoperator*(Vector2Dvector2D,double_float)
  273. {
  274. return(vector2D.X*_float)+(vector2D.Y*_float);
  275. }
  276. ///<summary>
  277. ///重载运算符,点积运算,可以用来计算两向量夹角
  278. ///</summary>
  279. publicstaticdoubleoperator/(Vector2Dvector2D,doublepara)
  280. {
  281. return(vector2D.X/para)+(vector2D.Y/para);
  282. }
  283. ///<summary>
  284. ///重载运算符
  285. ///</summary>
  286. publicstaticbooloperator>=(Vector2Dvector2D,doublepara)
  287. {
  288. if(vector2D.Mold>=para)
  289. {
  290. returntrue;
  291. }
  292. else
  293. {
  294. returnfalse;
  295. }
  296. }
  297. publicstaticbooloperator<=(Vector2Dvector2D,doublepara)
  298. {
  299. if(vector2D.Mold<=para)
  300. {
  301. returntrue;
  302. }
  303. else
  304. {
  305. returnfalse;
  306. }
  307. }
  308. publicstaticbooloperator>(Vector2Dvector2D,doublepara)
  309. {
  310. if(vector2D.Mold>para)
  311. {
  312. returntrue;
  313. }
  314. else
  315. {
  316. returnfalse;
  317. }
  318. }
  319. publicstaticbooloperator<(Vector2Dvector2D,doublepara)
  320. {
  321. if(vector2D.Mold<para)
  322. {
  323. returntrue;
  324. }
  325. else
  326. {
  327. returnfalse;
  328. }
  329. }
  330. #endregion运算符重载
  331. #region隐式转换
  332. ///<summary>
  333. ///重载隐式转换,可以直接使用Point
  334. ///</summary>
  335. ///<paramname="v"></param>
  336. publicstaticimplicitoperatorVector2D(System.Windows.Pointv)//隐式转换
  337. {
  338. returnnewVector2D(v.X,v.Y);
  339. }
  340. ///<summary>
  341. ///重载隐式转换,可以直接使用Point
  342. ///</summary>
  343. ///<paramname="v"></param>
  344. publicstaticimplicitoperatorSystem.Windows.Point(Vector2Dv)//隐式转换
  345. {
  346. returnnewSystem.Windows.Point(v.X,v.Y);
  347. }
  348. ///<summary>
  349. ///重载隐式转换,可以直接使用double
  350. ///</summary>
  351. ///<paramname="v"></param>
  352. publicstaticimplicitoperatorVector2D(doublev)//隐式转换
  353. {
  354. returnnewVector2D(v,v);
  355. }
  356. #endregion隐式转换
  357. #regionToString
  358. publicoverridestringToString()
  359. {
  360. returnX.ToString()+","+Y.ToString();
  361. }
  362. publicstringToString(stringsymbol)
  363. {
  364. returnX.ToString()+symbol+Y.ToString();
  365. }
  366. publicstringToString(stringsender,stringsymbol)
  367. {
  368. returnX.ToString(sender)+symbol+Y.ToString(sender);
  369. }
  370. #endregion
  371. }
  372. publicenumAngleType
  373. {
  374. Angle,
  375. Radian
  376. }
  377. }
复制代码
2)ComputingHelper.cs 代码如下
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Text;
  5. namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
  6. {
  7. publicstaticclassComputingHelper
  8. {
  9. publicstaticdoubleAngleToRadian(doubleangle)
  10. {
  11. returnangle*(Math.PI/180);
  12. }
  13. publicstaticdoubleRadianToAngle(doubleradian)
  14. {
  15. returnradian*(180/Math.PI);
  16. }
  17. ///<summary>
  18. ///将一个值从一个范围映射到另一个范围
  19. ///</summary>
  20. publicstaticdoubleRangeMapping(doubleinputValue,doubleenterLowerLimit,doubleenterUpperLimit,doubleoutputLowerLimit,doubleOutputUpperLimit,CurveTypecurveType=CurveType.None)
  21. {
  22. varpercentage=(enterUpperLimit-inputValue)/(enterUpperLimit-enterLowerLimit);
  23. switch(curveType)
  24. {
  25. caseCurveType.Sine:
  26. percentage=Math.Sin(percentage);
  27. break;
  28. caseCurveType.CoSine:
  29. percentage=Math.Cos(percentage);
  30. break;
  31. caseCurveType.Tangent:
  32. percentage=Math.Tan(percentage);
  33. break;
  34. caseCurveType.Cotangent:
  35. percentage=Math.Atan(percentage);
  36. break;
  37. default:
  38. break;
  39. }
  40. doubleoutputValue=OutputUpperLimit-((OutputUpperLimit-outputLowerLimit)*percentage);
  41. returnoutputValue;
  42. }
  43. publicstaticstringByteToKB(double_byte)
  44. {
  45. List<string>unit=newList<string>(){"B","KB","MB","GB","TB","P","PB"};
  46. inti=0;
  47. while(_byte>1024)
  48. {
  49. _byte/=1024;
  50. i++;
  51. }
  52. _byte=Math.Round(_byte,3);//保留三位小数
  53. return_byte+unit[i];
  54. }
  55. ///<summary>
  56. ///缩短一个数组,对其进行平均采样
  57. ///</summary>
  58. publicstaticdouble[]AverageSampling(double[]sourceArray,intnumber)
  59. {
  60. if(sourceArray.Length<=number)
  61. {
  62. returnsourceArray;
  63. //thrownewException("新的数组必须比原有的要小!");
  64. }
  65. double[]arrayList=newdouble[number];
  66. doublestride=(double)sourceArray.Length/number;
  67. for(inti=0,jIndex=0;i<number;i++,jIndex++)
  68. {
  69. doublestrideIncrement=i*stride;
  70. strideIncrement=Math.Round(strideIncrement,6);
  71. doublesum=0;
  72. intfirstIndex=(int)(strideIncrement);
  73. doublefirstDecimal=strideIncrement-firstIndex;
  74. inttailIndex=(int)(strideIncrement+stride);
  75. doubletailDecimal=(strideIncrement+stride)-tailIndex;
  76. if(firstDecimal!=0)
  77. sum+=sourceArray[firstIndex]*(1-firstDecimal);
  78. if(tailDecimal!=0&&tailIndex!=sourceArray.Length)
  79. sum+=sourceArray[tailIndex]*(tailDecimal);
  80. intstartIndex=firstDecimal==0?firstIndex:firstIndex+1;
  81. intendIndex=tailIndex;
  82. for(intj=startIndex;j<endIndex;j++)
  83. sum+=sourceArray[j];
  84. arrayList[jIndex]=sum/stride;
  85. }
  86. returnarrayList;
  87. }
  88. publicstaticList<Vector2D>AverageSampling(List<Vector2D>sourceArray,intnumber)
  89. {
  90. if(sourceArray.Count<=number-2)
  91. {
  92. returnsourceArray;
  93. }
  94. double[]x=newdouble[sourceArray.Count];
  95. double[]y=newdouble[sourceArray.Count];
  96. for(inti=0;i<sourceArray.Count;i++)
  97. {
  98. x[i]=sourceArray[i].X;
  99. y[i]=sourceArray[i].Y;
  100. }
  101. double[]X=AverageSampling(x,number-2);
  102. double[]Y=AverageSampling(y,number-2);
  103. List<Vector2D>arrayList=newList<Vector2D>();
  104. for(inti=0;i<number-2;i++)
  105. {
  106. arrayList.Add(newVector2D(X[i],Y[i]));
  107. }
  108. arrayList.Insert(0,sourceArray[0]);//添加首
  109. arrayList.Add(sourceArray[sourceArray.Count-1]);//添加尾
  110. returnarrayList;
  111. }
  112. }
  113. publicenumCurveType
  114. {
  115. Sine,
  116. CoSine,
  117. Tangent,
  118. Cotangent,
  119. None
  120. }
  121. }
复制代码
3)LineB.cs 代码如下
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Linq;
  4. usingSystem.Text;
  5. usingSystem.Windows.Media;
  6. namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
  7. {
  8. publicclassLineB
  9. {
  10. privateList<Vector2D>_vector2DList=newList<Vector2D>();
  11. publicList<Vector2D>Vector2DList
  12. {
  13. get{return_vector2DList;}
  14. set
  15. {
  16. _vector2DList=value;
  17. }
  18. }
  19. privateList<BezierCurve>_bezierCurveList=newList<BezierCurve>();
  20. publicList<BezierCurve>BezierCurveList
  21. {
  22. get{return_bezierCurveList;}
  23. privateset{_bezierCurveList=value;}
  24. }
  25. privatedouble_tension=0.618;
  26. publicdoubleTension
  27. {
  28. get{return_tension;}
  29. set
  30. {
  31. _tension=value;
  32. if(_tension>10)
  33. _tension=10;
  34. if(_tension<0)
  35. _tension=0;
  36. }
  37. }
  38. privatebool_isClosedCurve=true;
  39. publicboolIsClosedCurve
  40. {
  41. get{return_isClosedCurve;}
  42. set{_isClosedCurve=value;}
  43. }
  44. privatestring_pathData=string.Empty;
  45. publicstringPathData
  46. {
  47. get
  48. {
  49. if(_pathData==string.Empty)
  50. {
  51. _pathData=Vector2DToBezierCurve();
  52. }
  53. return_pathData;
  54. }
  55. }
  56. privatestringVector2DToBezierCurve()
  57. {
  58. if(Vector2DList.Count<3)
  59. returnstring.Empty;
  60. BezierCurveList.Clear();
  61. for(inti=0;i<Vector2DList.Count;i++)
  62. {
  63. intpointTwoIndex=i+1<Vector2DList.Count?i+1:0;
  64. intpointThreeIndex=i+2<Vector2DList.Count?i+2:i+2-Vector2DList.Count;
  65. Vector2Dvector2D1=Vector2DList[i];
  66. Vector2Dvector2D2=Vector2DList[pointTwoIndex];
  67. Vector2Dvector2D3=Vector2DList[pointThreeIndex];
  68. Vector2DstartVector2D=Vector2D.CalculateVectorCenter(vector2D1,vector2D2);
  69. doublestartAngle=Vector2D.IncludedAngleXAxis(vector2D1,vector2D2);
  70. doublestartDistance=Vector2D.CalculateVectorDistance(startVector2D,vector2D2)*(1-Tension);
  71. Vector2DstartControlPoint=Vector2D.CalculateVectorOffset(vector2D2,startAngle,startDistance);
  72. Vector2DendVector2D=Vector2D.CalculateVectorCenter(vector2D2,vector2D3);
  73. doubleendAngle=Vector2D.IncludedAngleXAxis(endVector2D,vector2D2);
  74. doubleendDistance=Vector2D.CalculateVectorDistance(endVector2D,vector2D2)*(1-Tension);
  75. Vector2DendControlPoint=Vector2D.CalculateVectorOffset(endVector2D,endAngle,endDistance);
  76. BezierCurvebezierCurve=newBezierCurve();
  77. bezierCurve.StartVector2D=startVector2D;
  78. bezierCurve.StartControlPoint=startControlPoint;
  79. bezierCurve.EndVector2D=endVector2D;
  80. bezierCurve.EndControlPoint=endControlPoint;
  81. BezierCurveList.Add(bezierCurve);
  82. }
  83. if(!IsClosedCurve)
  84. {
  85. BezierCurveList[0].StartVector2D=Vector2DList[0];
  86. BezierCurveList.RemoveAt(BezierCurveList.Count-1);
  87. BezierCurveList[BezierCurveList.Count-1].EndVector2D=Vector2DList[Vector2DList.Count-1];
  88. BezierCurveList[BezierCurveList.Count-1].EndControlPoint=BezierCurveList[BezierCurveList.Count-1].EndVector2D;
  89. }
  90. stringpath=$"M{BezierCurveList[0].StartVector2D.ToString()}";
  91. foreach(variteminBezierCurveList)
  92. {
  93. path+=$"C{item.StartControlPoint.ToString("")},{item.EndControlPoint.ToString("")},{item.EndVector2D.ToString("")}";
  94. }
  95. returnpath;
  96. }
  97. publicLineB()
  98. {
  99. }
  100. publicLineB(List<Vector2D>verVector2DList,boolisClosedCurve=true)
  101. {
  102. this.Vector2DList=verVector2DList;
  103. this.IsClosedCurve=isClosedCurve;
  104. }
  105. ///<summary>
  106. ///重载隐式转换,可以直接使用Point
  107. ///</summary>
  108. ///<paramname="v"></param>
  109. publicstaticimplicitoperatorGeometry(LineBlineB)//隐式转换
  110. {
  111. returnGeometry.Parse(lineB.PathData);
  112. }
  113. }
  114. publicclassBezierCurve
  115. {
  116. privateVector2D_startVector2D=newVector2D(0,0);
  117. publicVector2DStartVector2D
  118. {
  119. get{return_startVector2D;}
  120. set{_startVector2D=value;}
  121. }
  122. privateVector2D_startControlPoint=newVector2D(0,100);
  123. publicVector2DStartControlPoint
  124. {
  125. get{return_startControlPoint;}
  126. set{_startControlPoint=value;}
  127. }
  128. privateVector2D_endControlPoint=newVector2D(100,0);
  129. publicVector2DEndControlPoint
  130. {
  131. get{return_endControlPoint;}
  132. set{_endControlPoint=value;}
  133. }
  134. privateVector2D_endVector2D=newVector2D(100,100);
  135. publicVector2DEndVector2D
  136. {
  137. get{return_endVector2D;}
  138. set{_endVector2D=value;}
  139. }
  140. }
  141. }
复制代码
4)CanvasHandWritingExample.xaml 代码如下
  1. <UserControlx:Class="WPFDevelopers.Samples.ExampleViews.CanvasHandWriting.CanvasHandWritingExample"
  2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:WPFDevelopers.Samples.ExampleViews.CanvasHandWriting"
  7. mc:Ignorable="d"
  8. d:DesignHeight="450"d:DesignWidth="800">
  9. <UserControl.Resources>
  10. <StyleTargetType="{x:TypeTextBlock}">
  11. <SetterProperty="Foreground"Value="{StaticResourcePrimaryTextSolidColorBrush}"/>
  12. </Style>
  13. </UserControl.Resources>
  14. <Grid>
  15. <Grid.RowDefinitions>
  16. <RowDefinitionHeight="auto"/>
  17. <RowDefinition/>
  18. </Grid.RowDefinitions>
  19. <StackPanelOrientation="Horizontal"Margin="4">
  20. <TextBlockText="张力:"VerticalAlignment="Center"/>
  21. <TextBoxText="{BindingTension,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"/>
  22. <SliderWidth="100"SmallChange="0.01"
  23. Value="{BindingTension,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"Maximum="1"
  24. VerticalAlignment="Center"
  25. Margin="5,0"/>
  26. <TextBlockText="平滑采样:"VerticalAlignment="Center"/>
  27. <TextBoxText="{BindingSmoothSampling,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"
  28. Margin="5,0"/>
  29. <SliderValue="{BindingSmoothSampling,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"
  30. Width="100"
  31. VerticalAlignment="Center"
  32. SmallChange="0.01"Maximum="1"
  33. TickFrequency="0.1"/>
  34. <CheckBoxContent="橡皮擦"
  35. VerticalAlignment="Center"
  36. Margin="5,0"
  37. IsChecked="{BindingIsEraser,RelativeSource={RelativeSourceAncestorType=local:CanvasHandWritingExample}}"/>
  38. <ButtonContent="清空画布"Click="btnClertCanvas_Click"/>
  39. </StackPanel>
  40. <Canvasx:Name="drawingCanvas"
  41. Grid.Row="1"Background="Black"
  42. PreviewMouseLeftButtonDown="DrawingCanvas_PreviewMouseLeftButtonDown"
  43. PreviewMouseMove="DrawingCanvas_PreviewMouseMove"
  44. PreviewMouseLeftButtonUp="DrawingCanvas_PreviewMouseLeftButtonUp"/>
  45. </Grid>
  46. </UserControl>
复制代码
5)CanvasHandWritingExample.xaml.cs 代码如下
  1. usingSystem;
  2. usingSystem.Collections.Generic;
  3. usingSystem.Threading;
  4. usingSystem.Threading.Tasks;
  5. usingSystem.Windows;
  6. usingSystem.Windows.Controls;
  7. usingSystem.Windows.Input;
  8. usingSystem.Windows.Media;
  9. usingSystem.Windows.Shapes;
  10. namespaceWPFDevelopers.Samples.ExampleViews.CanvasHandWriting
  11. {
  12. ///<summary>
  13. ///CanvasHandWritingExample.xaml的交互逻辑
  14. ///</summary>
  15. publicpartialclassCanvasHandWritingExample:UserControl
  16. {
  17. publicstaticreadonlyDependencyPropertyTensionProperty=
  18. DependencyProperty.Register("Tension",typeof(double),typeof(CanvasHandWritingExample),
  19. newPropertyMetadata(0.618));
  20. publicstaticreadonlyDependencyPropertySmoothSamplingProperty=
  21. DependencyProperty.Register("SmoothSampling",typeof(double),typeof(CanvasHandWritingExample),
  22. newUIPropertyMetadata(OnSmoothSamplingChanged));
  23. publicstaticreadonlyDependencyPropertyIsEraserProperty=
  24. DependencyProperty.Register("IsEraser",typeof(bool),typeof(CanvasHandWritingExample),
  25. newPropertyMetadata(false));
  26. privatereadonlyDictionary<Path,List<Vector2D>>_PathVector2DDictionary;
  27. volatilebool_IsStart=false;
  28. Path_DrawingPath=default;
  29. privatestaticvoidOnSmoothSamplingChanged(DependencyObjectd,DependencyPropertyChangedEventArgse)
  30. {
  31. varmWindow=(CanvasHandWritingExample)d;
  32. foreach(variteminmWindow._PathVector2DDictionary.Keys)
  33. {
  34. mWindow.DrawLine(item);
  35. }
  36. }
  37. publicCanvasHandWritingExample()
  38. {
  39. InitializeComponent();
  40. _PathVector2DDictionary=newDictionary<Path,List<Vector2D>>();
  41. SmoothSampling=0.8;
  42. }
  43. privatevoidDrawingCanvas_PreviewMouseLeftButtonDown(objectsender,MouseButtonEventArgse)
  44. {
  45. _IsStart=true;
  46. _DrawingPath=newPath()
  47. {
  48. StrokeDashCap=PenLineCap.Round,
  49. StrokeStartLineCap=PenLineCap.Round,
  50. StrokeEndLineCap=PenLineCap.Round,
  51. StrokeLineJoin=PenLineJoin.Round,
  52. };
  53. if(IsEraser)
  54. {
  55. _DrawingPath.Stroke=newSolidColorBrush(Colors.Black);
  56. _DrawingPath.StrokeThickness=40;
  57. }
  58. else
  59. {
  60. varrandom=newRandom();
  61. varstrokeBrush=newSolidColorBrush(Color.FromRgb((byte)random.Next(200,255),(byte)random.Next(0,255),(byte)random.Next(0,255)));
  62. _DrawingPath.Stroke=strokeBrush;
  63. _DrawingPath.StrokeThickness=10;
  64. }
  65. _PathVector2DDictionary.Add(_DrawingPath,newList<Vector2D>());
  66. drawingCanvas.Children.Add(_DrawingPath);
  67. }
  68. privatevoidDrawingCanvas_PreviewMouseLeftButtonUp(objectsender,MouseButtonEventArgse)
  69. {
  70. _IsStart=false;
  71. _DrawingPath=default;
  72. }
  73. privatevoidDrawingCanvas_PreviewMouseMove(objectsender,MouseEventArgse)
  74. {
  75. if(!_IsStart)
  76. return;
  77. if(_DrawingPathisnull)
  78. return;
  79. Vector2DcurrenPoint=e.GetPosition(drawingCanvas);
  80. if(currenPoint.X<0||currenPoint.Y<0)
  81. return;
  82. if(currenPoint.X>drawingCanvas.ActualWidth||currenPoint.Y>drawingCanvas.ActualHeight)
  83. return;
  84. if(_PathVector2DDictionary[_DrawingPath].Count>0)
  85. {
  86. if(Vector2D.CalculateVectorDistance(currenPoint,_PathVector2DDictionary[_DrawingPath][_PathVector2DDictionary[_DrawingPath].Count-1])>1)
  87. _PathVector2DDictionary[_DrawingPath].Add(e.GetPosition(drawingCanvas));
  88. }
  89. else
  90. _PathVector2DDictionary[_DrawingPath].Add(e.GetPosition(drawingCanvas));
  91. DrawLine(_DrawingPath);
  92. }
  93. publicdoubleTension
  94. {
  95. get=>(double)GetValue(TensionProperty);
  96. set=>SetValue(TensionProperty,value);
  97. }
  98. publicdoubleSmoothSampling
  99. {
  100. get=>(double)GetValue(SmoothSamplingProperty);
  101. set=&g;SetValue(SmoothSamplingProperty,value);
  102. }
  103. publicboolIsEraser
  104. {
  105. get=>(bool)GetValue(IsEraserProperty);
  106. set=>SetValue(IsEraserProperty,value);
  107. }
  108. privatevoidDrawLine(Pathpath)
  109. {
  110. if(_PathVector2DDictionary[path].Count>2)
  111. {
  112. varpathVector2Ds=_PathVector2DDictionary[path];
  113. varsmoothNum=(int)(_PathVector2DDictionary[path].Count*SmoothSampling);
  114. if(smoothNum>1)
  115. pathVector2Ds=ComputingHelper.AverageSampling(_PathVector2DDictionary[path],smoothNum);
  116. varlineB=newLineB(pathVector2Ds,false);
  117. lineB.Tension=Tension;
  118. path.Data=lineB;
  119. }
  120. }
  121. privatevoidbtnClertCanvas_Click(objectsender,RoutedEventArgse)
  122. {
  123. drawingCanvas.Children.Clear();
  124. _PathVector2DDictionary.Clear();
  125. }
  126. }
  127. }
复制代码
到此这篇关于WPF+Canvas实现平滑笔迹的示例代码的文章就介绍到这了,更多相关WPF Canvas平滑笔迹内容请搜索中国红客联盟以前的文章或继续浏览下面的相关文章希望大家以后多多支持中国红客联盟!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4004
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行