前言 本人在业余时间,开发了一款电子印章制作软件。印章制作软件看似简单,其实不然。
比如对椭圆形印章而言,要求公司名称中的每一个字间隔相等,要求字的方向与椭圆曲线垂直。
要满足这些条件,需要复杂的计算,并且要有一定的几何学基础。软件界面如下:
本文主要讲述椭圆形印章制作思路,对于圆形印章 ,软件开发难度并不大。
椭圆印章要满足以下两个条件:
1 公司名称字体间隔大小要一样。
2 字体方向与对应的椭圆曲线是相切的。
下面分析如何通过程序满足这两点要求。
1 计算椭圆的弧线长度
假设公司名称占了椭圆220度边长,如何计算出220度弧线的长度? 肯定有数学公式可以计算。我没有用公式计算。我联想到了“微积分”的概念。可以将这一段弧线切成几千份小的弧线,把小的弧线段长度加起来就是总的长度。小的弧线段长度非常小,可以用两点之间的直线长度代替。
假设将弧线分成n份,将这n个线段长度加起来就是弧线长度。
/// <summary> /// 将弧线分成splitCount份,计算弧线上的点 /// </summary> /// <param name="startDegree">弧线起始度数</param> /// <param name="splitCount">分成多少份</param> /// <returns></returns> List<PointDouble> GetDrawPoint(double startDegree, int splitCount) { double a = EllipseWidth / 2; double b = EllipseHeight / 2; List<PointDouble> result = new List<PointDouble>(); Graphics g = Graphics; for (int i = 0; i < splitCount; i++) { double degree = startDegree + (i * CompanySpanDegree) / splitCount; double k = Math.Tan(ToArc(degree)); List<PointDouble> intersections = CalIntersection(a, b, k); //Debug.WriteLine(string.Format("degree:{0}", degree)); PointDouble ptDraw = new PointDouble(); if (degree < 90) { if (intersections[0].X < 0) { ptDraw = intersections[0]; } else if (intersections[0].X > 0) { ptDraw = intersections[1]; } else { } } else if (degree > 90) { if (intersections[0].X > 0) { ptDraw = intersections[0]; } else if (intersections[0].X < 0) { ptDraw = intersections[1]; } else { } } else { if (intersections[0].Y < 0) { ptDraw = intersections[0]; } else { ptDraw = intersections[1]; } } result.Add(ptDraw); } return result; }
2 确定每个文字的坐标。
弧线总长度计算处理后,就可以算出每个文字占用的弧线长度,根据每个文字占用的弧线长度就可以计算出文字的坐标。
List<PointDouble> CalDrawPoint(float startDegree, int txtCount) { List<double> listLenPart = new List<double>(); List<PointDouble> listPoint = GetDrawPoint(startDegree, 10000); //计算弧线长度 double arcLen = CalPointLen(listPoint, listLenPart); //每个文字占用的弧线长度 double txtSpanPer = arcLen / (txtCount - 1); //计算文字对应的点 List<PointDouble> result = new List<PointDouble>(); result.Add(listPoint[0]); double lenSpanCal = 0; for (int i = 0; i < listLenPart.Count; i++) { double n = listLenPart[i]; lenSpanCal += n; if (lenSpanCal >= txtSpanPer) { result.Add(listPoint[i + 1]); lenSpanCal = 0; } } if (result.Count < txtCount) result.Add(listPoint.Last()); Debug.Assert(result.Count == txtCount); return result; }
3 计算文字的旋转角度
确定了文字的坐标后,就可以计算出该点对应的切线,从而求出切线角度。切线公式如下:
计算切线坐标源码如下:
/// <summary> /// 计算椭圆切线的角度 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> float CalTangentLineDegree(float x, float y) { if (y == 0) return 90; float a = EllipseWidth / 2; float b = EllipseHeight / 2; float k = -b * b * x / (a * a * y); float result = (float)Math.Atan(k); result = (float)ToDegree(result); return result; }
文字的坐标和旋转角度确定后,后续处理就水到渠成:
程序界面截图: