/// <summary> /// 计算同一容器中一组控件的最小外接矩形。 /// </summary> /// <param name="ctrls">控件数组。</param> /// <param name="edgeDist">矩形的每边到控件边缘的距离。</param> /// <returns>Point 结构,表示同一容器中一组控件的最小外接矩形。</returns> public static Rectangle GetMinimumBoundingRectangleOfControls(Control[] ctrls, int edgeDist) { if (InternalMethod.IsNullOrEmpty(ctrls)) { return(Rectangle.Empty); } else { int L = int.MaxValue, R = int.MinValue, T = int.MaxValue, B = int.MinValue; foreach (Control Ctrl in ctrls) { if (!(Ctrl is null)) { L = Math.Min(L, Ctrl.Left); T = Math.Min(T, Ctrl.Top); R = Math.Max(R, Ctrl.Right); B = Math.Max(B, Ctrl.Bottom); } } edgeDist = Math.Max(0, edgeDist); return(Rectangle.FromLTRB(L - edgeDist, T - edgeDist, R + edgeDist - 1, B + edgeDist - 1)); } }
/// <summary> /// 返回一个以 "d° m′ s″ " 格式表示角度的字符串。 /// </summary> /// <param name="degree">以角度为单位的角度。</param> /// <param name="decimalDigits">保留的小数位数,0 表示不保留小数,-1 表示保留所有小数。</param> /// <param name="cutdownIdleZeros">是否删除小数点后所有不必要的 "0"。</param> /// <returns>字符串,表示以 "d° m′ s″ " 格式表示的角度。</returns> public static string GetAngleStringOfDegree(double degree, int decimalDigits, bool cutdownIdleZeros) { if (InternalMethod.IsNaNOrInfinity(degree)) { return("N/A"); } else { string Sign = (degree < 0 ? "-" : string.Empty); degree = Math.Abs(degree); double DegF = degree; double DegD = Math.Floor(DegF); double MinF = (DegF - DegD) * 60; double MinD = Math.Floor(MinF); double SecF = (MinF - MinD) * 60; string DegStr = DegD + "° "; string MinStr = MinD + "′ "; string SecStr = SecF.ToString(decimalDigits >= 0 ? "N" + Math.Min(16, decimalDigits) : string.Empty) + "″ "; if (cutdownIdleZeros) { int DotIndex = SecStr.IndexOf('.'); if (DotIndex >= 0) { int LastPartIndex = DotIndex + 1; while (LastPartIndex < SecStr.Length && char.IsDigit(SecStr, LastPartIndex)) { LastPartIndex++; } string HeadPart = SecStr.Substring(0, DotIndex); string MiddlePart = SecStr.Substring(DotIndex, LastPartIndex - DotIndex); string LastPart = (LastPartIndex < SecStr.Length ? SecStr.Substring(LastPartIndex) : string.Empty); if (MiddlePart.Length > 0) { while (MiddlePart[MiddlePart.Length - 1] == '0') { MiddlePart = MiddlePart.Substring(0, MiddlePart.Length - 1); } if (MiddlePart.Length == 1) { MiddlePart = string.Empty; } } SecStr = string.Concat(HeadPart, MiddlePart, LastPart); } } return(string.Concat(Sign, DegStr, MinStr, SecStr)); } }
/// <summary> /// 判断在一个圆内部能否看到一个点。 /// </summary> /// <param name="pt">点。</param> /// <param name="offset">圆的圆心。</param> /// <param name="radius">圆的半径。</param> /// <returns>布尔值,表示在一个圆内部能否看到一个点。</returns> public static bool PointIsVisibleInCircle(PointD pt, PointD offset, double radius) { if (pt.IsNaNOrInfinity || offset.IsNaNOrInfinity || (InternalMethod.IsNaNOrInfinity(radius) || radius <= 0)) { return(false); } else { return(pt.DistanceFrom(offset) < radius); } }
/// <summary> /// 计算椭圆在指定相位的焦半径。 /// </summary> /// <param name="semiMajorAxis">椭圆的半长轴。</param> /// <param name="eccentricity">椭圆的离心率。</param> /// <param name="phase">椭圆的相位(弧度)(以近焦点相位为 0 弧度,从 +X 轴指向 +Y 轴的方向为正方向)。</param> /// <returns>双精度浮点数,表示椭圆在指定相位的焦半径。</returns> public static double GetFocalRadiusOfEllipse(double semiMajorAxis, double eccentricity, double phase) { if ((InternalMethod.IsNaNOrInfinity(semiMajorAxis) || semiMajorAxis <= 0) || (InternalMethod.IsNaNOrInfinity(eccentricity) || eccentricity < 0 || eccentricity >= 1) || InternalMethod.IsNaNOrInfinity(phase)) { return(double.NaN); } else { return(semiMajorAxis * Math.Sqrt(Math.Pow(Math.Cos(phase) - eccentricity, 2) + (1 - Math.Pow(eccentricity, 2)) * Math.Pow(Math.Sin(phase), 2))); } }
/// <summary> /// 将椭圆的圆心角转换为相位(弧度)(以近焦点相位为 0 弧度,从 +X 轴指向 +Y 轴的方向为正方向)。 /// </summary> /// <param name="centralAngle">椭圆的圆心角(弧度)。</param> /// <param name="eccentricity">椭圆的离心率。</param> /// <returns>双精度浮点数,表示将椭圆的圆心角转换为相位(弧度)(以近焦点相位为 0 弧度,从 +X 轴指向 +Y 轴的方向为正方向)得到的结果。</returns> public static double EllipseCentralAngleToPhase(double centralAngle, double eccentricity) { if (InternalMethod.IsNaNOrInfinity(centralAngle) || (InternalMethod.IsNaNOrInfinity(eccentricity) || eccentricity < 0 || eccentricity >= 1)) { return(double.NaN); } else { return(Math.Atan(Math.Tan(centralAngle) / Math.Sqrt(1 - Math.Pow(eccentricity, 2))) + Math.Round(centralAngle / Constant.Pi) * Constant.Pi); } }
/// <summary> /// 计算同一容器中一组控件的最小外接矩形。 /// </summary> /// <param name="ctrls">控件数组。</param> /// <returns>Point 结构,表示同一容器中一组控件的最小外接矩形。</returns> public static Rectangle GetMinimumBoundingRectangleOfControls(Control[] ctrls) { if (InternalMethod.IsNullOrEmpty(ctrls)) { return(Rectangle.Empty); } else { return(GetMinimumBoundingRectangleOfControls(ctrls, 0)); } }
/// <summary> /// 返回将一个时间间隔在合适的单位下保留指定位数有效数字的数值。 /// </summary> /// <param name="seconds">时间间隔(秒)。</param> /// <param name="significance">有效数字位数,0 表示保留所有有效数字。</param> /// <returns>双精度浮点数,表示将一个时间间隔在合适的单位下保留指定位数有效数字的数值。</returns> public static double GetStandardizationTimespanOfSecond(double seconds, int significance) { if (InternalMethod.IsNaNOrInfinity(seconds)) { return(double.NaN); } else { if (seconds != 0 && significance > 0) { int sign = Math.Sign(seconds); seconds = Math.Abs(seconds); double unit = 1; if (seconds >= 31557600.0) { seconds /= (unit = 31557600.0); } else if (seconds >= 86400.0) { seconds /= (unit = 86400.0); } else if (seconds >= 3600.0) { seconds /= (unit = 3600.0); } else if (seconds >= 60.0) { seconds /= (unit = 60.0); } else if (seconds < 1) { seconds /= (unit = 0.001); } int exp = (int)Math.Floor(Math.Log10(Math.Abs(seconds))) - Math.Max(0, significance - 1); seconds = Math.Round(seconds / Math.Pow(10, exp)); while (seconds >= 10) { seconds /= 10; exp++; } seconds *= (sign * Math.Pow(10, exp) * unit); } return(seconds); } }
/// <summary> /// 判断在一个菱形内部能否看到一个点。 /// </summary> /// <param name="pt">点。</param> /// <param name="offset">菱形的中心。</param> /// <param name="semiMajorAxis">菱形的半长轴。</param> /// <param name="semiMinorAxis">菱形的半短轴。</param> /// <param name="rotateAngle">菱形的旋转角(弧度)(以焦点为中心,以 +X 轴为 0 弧度,从 +X 轴指向 +Y 轴的方向为正方向,半长轴相对于 +X 轴的角度)。</param> /// <returns>布尔值,表示在一个菱形内部能否看到一个点。</returns> public static bool PointIsVisibleInRhombus(PointD pt, PointD offset, double semiMajorAxis, double semiMinorAxis, double rotateAngle) { if (pt.IsNaNOrInfinity || offset.IsNaNOrInfinity || (InternalMethod.IsNaNOrInfinity(semiMajorAxis) || semiMajorAxis <= 0) || (InternalMethod.IsNaNOrInfinity(semiMinorAxis) || semiMinorAxis <= 0) || InternalMethod.IsNaNOrInfinity(rotateAngle)) { return(false); } else { pt.Rotate(-rotateAngle, offset); return(Math.Abs(pt.X - offset.X) * semiMinorAxis + Math.Abs(pt.Y - offset.Y) * semiMajorAxis < semiMajorAxis * semiMinorAxis); } }
/// <summary> /// 判断在一个椭圆内部能否看到一个点。 /// </summary> /// <param name="pt">点。</param> /// <param name="offset">椭圆的焦点。</param> /// <param name="semiMajorAxis">椭圆的半长轴。</param> /// <param name="eccentricity">椭圆的离心率。</param> /// <param name="rotateAngle">椭圆的旋转角(弧度)(以焦点为中心,以 +X 轴为 0 弧度,从 +X 轴指向 +Y 轴的方向为正方向,焦点到近焦点连线相对于 +X 轴的角度)。</param> /// <returns>布尔值,表示在一个椭圆内部能否看到一个点。</returns> public static bool PointIsVisibleInEllipse(PointD pt, PointD offset, double semiMajorAxis, double eccentricity, double rotateAngle) { if (pt.IsNaNOrInfinity || offset.IsNaNOrInfinity || (InternalMethod.IsNaNOrInfinity(semiMajorAxis) || semiMajorAxis <= 0) || (InternalMethod.IsNaNOrInfinity(eccentricity) || eccentricity < 0 || eccentricity >= 1) || InternalMethod.IsNaNOrInfinity(rotateAngle)) { return(false); } else { PointD _Offset = new PointD(offset.X - 2 * semiMajorAxis * eccentricity * Math.Cos(rotateAngle), offset.Y - 2 * semiMajorAxis * eccentricity * Math.Sin(rotateAngle)); double FocalDistSum = pt.DistanceFrom(offset) + pt.DistanceFrom(_Offset); return(FocalDistSum < 2 * semiMajorAxis); } }
/// <summary> /// 返回将一个距离在合适的单位下保留指定位数有效数字的数值。 /// </summary> /// <param name="meters">距离(米)。</param> /// <param name="significance">有效数字位数,0 表示保留所有有效数字。</param> /// <returns>双精度浮点数,表示将一个距离在合适的单位下保留指定位数有效数字的数值。</returns> public static double GetStandardizationDistanceOfMeter(double meters, int significance) { if (InternalMethod.IsNaNOrInfinity(meters)) { return(double.NaN); } else { if (meters != 0 && significance > 0) { int sign = Math.Sign(meters); meters = Math.Abs(meters); double unit = 1; if (meters >= 9460730472580800.0) { meters /= (unit = 9460730472580800.0); } else if (meters >= 149597870700.0) { meters /= (unit = 149597870700.0); } else if (meters >= 1000.0) { meters /= (unit = 1000.0); } int exp = (int)Math.Floor(Math.Log10(Math.Abs(meters))) - Math.Max(0, significance - 1); meters = Math.Round(meters / Math.Pow(10, exp)); while (meters >= 10) { meters /= 10; exp++; } meters *= (sign * Math.Pow(10, exp) * unit); } return(meters); } }
/// <summary> /// 计算平面直角坐标系中过一个定点到一条直线的距离。 /// </summary> /// <param name="pt">定点。</param> /// <param name="A">直线的一般式方程的第一个参数。</param> /// <param name="B">直线的一般式方程的第二个参数。</param> /// <param name="C">直线的一般式方程的第三个参数。</param> /// <returns>双精度浮点数,表示平面直角坐标系中过一个定点到一条直线的距离。</returns> public static double GetDistanceBetweenPointAndLine(PointD pt, double A, double B, double C) { if (pt.IsNaNOrInfinity || InternalMethod.IsNaNOrInfinity(A) || InternalMethod.IsNaNOrInfinity(B) || InternalMethod.IsNaNOrInfinity(C)) { return(double.NaN); } else { if (A == 0 && B == 0) { return(double.NaN); } else { return((A * pt.X + B * pt.Y + C) / Math.Sqrt(A * A + B * B)); } } }
// private static double _CheckProportion(double proportion) // 对双精度浮点数表示的比例的值进行合法性检查,返回合法的值。 { if (InternalMethod.IsNaNOrInfinity(proportion)) { throw new ArgumentOutOfRangeException(); } // if (proportion < 0) { if (proportion <= -5E-13) { throw new ArgumentOutOfRangeException(); } return(0); } else if (proportion > 100) { if (proportion >= 100 + 5E-11) { throw new ArgumentOutOfRangeException(); } return(1); } else { if (proportion > 1) { return(proportion / 100); } else { return(proportion); } } }
/// <summary> /// 返回一个表示时间间隔的字符串。 /// </summary> /// <param name="seconds">时间间隔(秒)。</param> /// <returns>字符串,表示时间间隔。</returns> public static string GetLargeTimespanStringOfSecond(double seconds) { if (InternalMethod.IsNaNOrInfinity(seconds)) { return("N/A"); } else { if (seconds == 0) { return("0"); } else { int Sign = Math.Sign(seconds); seconds = Math.Abs(seconds); return((Sign == -1 ? "-" : string.Empty) + (seconds >= 31557600E12 ? seconds / (31557600E12) + " Ta" : (seconds >= 31557600E9 ? seconds / (31557600E9) + " Ga" : (seconds >= 31557600E6 ? seconds / (31557600E6) + " Ma" : (seconds >= 31557600E3 ? seconds / (31557600E3) + " ka" : (seconds >= 31557600.0 ? seconds / 31557600.0 + " a" : (seconds >= 86400.0 ? seconds / 86400.0 + " d" : (seconds >= 3600.0 ? seconds / 3600.0 + " h" : (seconds >= 60 ? seconds / 60.0 + " min" : (seconds >= 1 ? seconds + " s" : seconds * 1000.0 + " ms")))))))))); } } }
/// <summary> /// 返回一个表示距离的字符串。 /// </summary> /// <param name="meters">距离(米)。</param> /// <returns>字符串,表示距离。</returns> public static string GetLargeDistanceStringOfMeter(double meters) { if (InternalMethod.IsNaNOrInfinity(meters)) { return("N/A"); } else { if (meters == 0) { return("0"); } else { int Sign = Math.Sign(meters); meters = Math.Abs(meters); return((Sign == -1 ? "-" : string.Empty) + (meters >= 9460730472580800E12 ? meters / 9460730472580800E12 + " Tly" : (meters >= 9460730472580800E9 ? meters / 9460730472580800E9 + " Gly" : (meters >= 9460730472580800E6 ? meters / 9460730472580800E6 + " Mly" : (meters >= 9460730472580800E3 ? meters / 9460730472580800E3 + " kly" : (meters >= 9460730472580800.0 ? meters / 9460730472580800.0 + " ly" : (meters >= 149597870700.0 ? meters / 149597870700.0 + " AU" : (meters >= 1000.0 ? meters / 1000.0 + " km" : meters + " m")))))))); } } }
// private static double _CheckLevel(double level) // 对双精度浮点数表示的调整程度的值进行合法性检查,返回合法的值。 { if (InternalMethod.IsNaNOrInfinity(level)) { throw new ArgumentOutOfRangeException(); } // if (level < -100) { if (level <= -100 - 5E-11) { throw new ArgumentOutOfRangeException(); } return(-1); } else if (level > 100) { if (level >= 100 + 5E-11) { throw new ArgumentOutOfRangeException(); } return(1); } else { if (level < -1 || level > 1) { return(level / 100); } else { return(level); } } }
// /// <summary> /// 将一个角度映射到范围为一个周角的区间。 /// </summary> /// <param name="angle">用于转换的角度。</param> /// <param name="asDegree">用于转换的角度是否以角度制表示。</param> /// <param name="zeroCentered">目标区间是否以 0 为中心。</param> /// <returns>双精度浮点数,表示将一个角度映射到范围为一个周角的区间得到的结果。</returns> public static double AngleMapping(double angle, bool asDegree, bool zeroCentered) { if (InternalMethod.IsNaNOrInfinity(angle)) { return(double.NaN); } else { double result = angle; double perigon = (asDegree ? 360 : Constant.DoublePi); if (angle < 0 || angle >= perigon) { result = angle - Math.Floor(angle / perigon) * perigon; } if (zeroCentered) { double flat = (asDegree ? 180 : Constant.Pi); if (result >= flat) { return(result - flat); } else { return(result); } } else { return(result); } } }
/// <summary> /// 将位图的副本按顺时针方向旋转一个角度,返回旋转后得到的位图。 /// </summary> /// <param name="bmp">用于旋转的位图。</param> /// <param name="rotateAngle">按顺时针方向旋转的角度(弧度)。</param> /// <param name="antiAlias">是否使用抗锯齿模式绘图。</param> /// <returns>Bitmap 对象,表示将位图的副本按顺时针方向旋转一个角度得到的结果。</returns> public static Bitmap RotateBitmap(Bitmap bmp, double rotateAngle, bool antiAlias) { if (bmp is null || InternalMethod.IsNaNOrInfinity(rotateAngle)) { return(null); }
/// <summary> /// 判断在一个矩形内部能否看到一个圆的圆周的部分或全部。 /// </summary> /// <param name="offset">圆的圆心。</param> /// <param name="radius">圆的半径。</param> /// <param name="rect">矩形。</param> /// <returns>布尔值,表示在一个矩形内部能否看到一个圆的圆周的部分或全部。</returns> public static bool CircumferenceIsVisibleInRectangle(PointD offset, double radius, RectangleF rect) { if (offset.IsNaNOrInfinity || (InternalMethod.IsNaNOrInfinity(radius) || radius <= 0) || rect.Size.IsEmpty) { return(false); } else { if (offset.X < rect.X && offset.Y < rect.Y) { if (Math.Pow(offset.X - rect.X, 2) + Math.Pow(offset.Y - rect.Y, 2) < Math.Pow(radius, 2) && Math.Pow(offset.X - rect.Right, 2) + Math.Pow(offset.Y - rect.Bottom, 2) > Math.Pow(radius, 2)) { return(true); } else { return(false); } } else if (offset.X > rect.Right && offset.Y < rect.Y) { if (Math.Pow(offset.X - rect.Right, 2) + Math.Pow(offset.Y - rect.Y, 2) < Math.Pow(radius, 2) && Math.Pow(offset.X - rect.X, 2) + Math.Pow(offset.Y - rect.Bottom, 2) > Math.Pow(radius, 2)) { return(true); } else { return(false); } } else if (offset.X < rect.X && offset.Y > rect.Bottom) { if (Math.Pow(offset.X - rect.X, 2) + Math.Pow(offset.Y - rect.Bottom, 2) < Math.Pow(radius, 2) && Math.Pow(offset.X - rect.Right, 2) + Math.Pow(offset.Y - rect.Y, 2) > Math.Pow(radius, 2)) { return(true); } else { return(false); } } else if (offset.X > rect.Right && offset.Y > rect.Bottom) { if (Math.Pow(offset.X - rect.Right, 2) + Math.Pow(offset.Y - rect.Bottom, 2) < Math.Pow(radius, 2) && Math.Pow(offset.X - rect.X, 2) + Math.Pow(offset.Y - rect.Y, 2) > Math.Pow(radius, 2)) { return(true); } else { return(false); } } else { if (2 * radius >= Math.Sqrt(Math.Pow(rect.Width, 2) + Math.Pow(rect.Height, 2))) { if (((offset.X >= rect.X - radius && offset.X <= rect.Right + radius) && (offset.Y >= rect.Y - radius && offset.Y <= rect.Bottom + radius)) && !((offset.X >= rect.Right - Constant.HalfSqrt2 * radius && offset.X <= rect.X + Constant.HalfSqrt2 * radius) && (offset.Y >= rect.Bottom - Constant.HalfSqrt2 * radius && offset.Y <= rect.Y + Constant.HalfSqrt2 * radius))) { return(true); } else { return(false); } } else { if (offset.X >= rect.X - radius && offset.X <= rect.Right + radius && offset.Y >= rect.Y - radius && offset.Y <= rect.Bottom + radius) { return(true); } else { return(false); } } } } }
/// <summary> /// 判断在一个矩形内部能否看到一个圆的内部或者圆周的部分或全部。 /// </summary> /// <param name="offset">圆的圆心。</param> /// <param name="radius">圆的半径。</param> /// <param name="rect">矩形。</param> /// <returns>布尔值,表示在一个矩形内部能否看到一个圆的内部或者圆周的部分或全部。</returns> public static bool CircleInnerIsVisibleInRectangle(PointD offset, double radius, RectangleF rect) { if (offset.IsNaNOrInfinity || (InternalMethod.IsNaNOrInfinity(radius) || radius <= 0) || rect.Size.IsEmpty) { return(false); } else { if (offset.X < rect.X && offset.Y < rect.Y) { if (Math.Pow(offset.X - rect.X, 2) + Math.Pow(offset.Y - rect.Y, 2) < Math.Pow(radius, 2)) { return(true); } else { return(false); } } else if (offset.X > rect.Right && offset.Y < rect.Y) { if (Math.Pow(offset.X - rect.Right, 2) + Math.Pow(offset.Y - rect.Y, 2) < Math.Pow(radius, 2)) { return(true); } else { return(false); } } else if (offset.X < rect.X && offset.Y > rect.Bottom) { if (Math.Pow(offset.X - rect.X, 2) + Math.Pow(offset.Y - rect.Bottom, 2) < Math.Pow(radius, 2)) { return(true); } else { return(false); } } else if (offset.X > rect.Right && offset.Y > rect.Bottom) { if (Math.Pow(offset.X - rect.Right, 2) + Math.Pow(offset.Y - rect.Bottom, 2) < Math.Pow(radius, 2)) { return(true); } else { return(false); } } else if (offset.X >= rect.X - radius && offset.X <= rect.Right + radius && offset.Y >= rect.Y - radius && offset.Y <= rect.Bottom + radius) { return(true); } else { return(false); } } }
/// <summary> /// 判断在一个圆内部能否看到一个直线段的部分或全部。 /// </summary> /// <param name="pt1">直线段的第一个端点。</param> /// <param name="pt2">直线段的第二个端点。</param> /// <param name="offset">圆的圆心。</param> /// <param name="radius">圆的半径。</param> /// <returns>布尔值,表示在一个圆内部能否看到一个直线段的部分或全部。</returns> public static bool LineIsVisibleInCircle(PointD pt1, PointD pt2, PointD offset, double radius) { if (pt1.IsNaNOrInfinity || pt2.IsNaNOrInfinity || offset.IsNaNOrInfinity || (InternalMethod.IsNaNOrInfinity(radius) || radius <= 0)) { return(false); } else { double Dist_Off_P1 = offset.DistanceFrom(pt1); double Dist_Off_P2 = offset.DistanceFrom(pt2); if (Dist_Off_P1 <= radius || Dist_Off_P2 <= radius) { return(true); } else { double A, B, C; CalcLineGeneralFunction(pt1, pt2, out A, out B, out C); double Dist_Off_FP = GetDistanceBetweenPointAndLine(offset, A, B, C); if (Dist_Off_FP > radius) { return(false); } else { if ((A * pt1.X + B * pt1.Y + C) * (A * pt2.X + B * pt2.Y + C) > 0) { return(false); } else { return(true); } } } } }
private static readonly PointD3D _NormalVectorPZX = new PointD3D(0, 1, 0); // 正方体表面的 +ZX 法向量。 // /// <summary> /// 绘制一个长方体,并返回表示是否已经实际完成绘图的布尔值。 /// </summary> /// <param name="bmp">用于绘制的位图。</param> /// <param name="center">长方体的中心坐标。</param> /// <param name="size">长方体的大小。</param> /// <param name="color">长方体的颜色。</param> /// <param name="edgeWidth">长方体的棱的宽度。</param> /// <param name="affineMatrices">仿射矩阵(左矩阵)列表。</param> /// <param name="focalLength">焦距。</param> /// <param name="illuminationDirection">光照方向。</param> /// <param name="illuminationDirectionIsAfterAffineTransform">光照方向是否基于仿射变换之后的坐标系。</param> /// <param name="exposure">曝光,取值范围为 [-100, 100]。</param> /// <param name="antiAlias">是否使用抗锯齿模式绘图。</param> /// <returns>布尔值,表示是否已经实际完成绘图。</returns> public static bool PaintCuboid(Bitmap bmp, PointD3D center, PointD3D size, Color color, float edgeWidth, List <Matrix> affineMatrices, double focalLength, PointD3D illuminationDirection, bool illuminationDirectionIsAfterAffineTransform, double exposure, bool antiAlias) { try { if (bmp is null || center.IsNaNOrInfinity || (size.IsNaNOrInfinity || size.IsZero) || (color.IsEmpty || color.A == 0) || InternalMethod.IsNaNOrInfinity(edgeWidth) || InternalMethod.IsNullOrEmpty(affineMatrices) || (InternalMethod.IsNaNOrInfinity(focalLength) || focalLength < 0) || illuminationDirection.IsNaNOrInfinity || InternalMethod.IsNaNOrInfinity(exposure)) { return(false); } else { PointD3D P3D_000 = _Vertex000; PointD3D P3D_100 = _Vertex100; PointD3D P3D_010 = _Vertex010; PointD3D P3D_110 = _Vertex110; PointD3D P3D_001 = _Vertex001; PointD3D P3D_101 = _Vertex101; PointD3D P3D_011 = _Vertex011; PointD3D P3D_111 = _Vertex111; P3D_000.Scale(size); P3D_100.Scale(size); P3D_010.Scale(size); P3D_110.Scale(size); P3D_001.Scale(size); P3D_101.Scale(size); P3D_011.Scale(size); P3D_111.Scale(size); P3D_000.Offset(center); P3D_100.Offset(center); P3D_010.Offset(center); P3D_110.Offset(center); P3D_001.Offset(center); P3D_101.Offset(center); P3D_011.Offset(center); P3D_111.Offset(center); P3D_000.AffineTransform(affineMatrices); P3D_100.AffineTransform(affineMatrices); P3D_010.AffineTransform(affineMatrices); P3D_110.AffineTransform(affineMatrices); P3D_001.AffineTransform(affineMatrices); P3D_101.AffineTransform(affineMatrices); P3D_011.AffineTransform(affineMatrices); P3D_111.AffineTransform(affineMatrices); PointD3D PrjCenter = new PointD3D(bmp.Width / 2, bmp.Height / 2, 0); PointD P2D_000 = P3D_000.ProjectToXY(PrjCenter, focalLength); PointD P2D_100 = P3D_100.ProjectToXY(PrjCenter, focalLength); PointD P2D_010 = P3D_010.ProjectToXY(PrjCenter, focalLength); PointD P2D_110 = P3D_110.ProjectToXY(PrjCenter, focalLength); PointD P2D_001 = P3D_001.ProjectToXY(PrjCenter, focalLength); PointD P2D_101 = P3D_101.ProjectToXY(PrjCenter, focalLength); PointD P2D_011 = P3D_011.ProjectToXY(PrjCenter, focalLength); PointD P2D_111 = P3D_111.ProjectToXY(PrjCenter, focalLength); PointF P_000 = P2D_000.ToPointF(); PointF P_100 = P2D_100.ToPointF(); PointF P_010 = P2D_010.ToPointF(); PointF P_110 = P2D_110.ToPointF(); PointF P_001 = P2D_001.ToPointF(); PointF P_101 = P2D_101.ToPointF(); PointF P_011 = P2D_011.ToPointF(); PointF P_111 = P2D_111.ToPointF(); PointD3D[][] Element3D = new PointD3D[][] { // XY 面 new PointD3D[] { P3D_000, P3D_010, P3D_110, P3D_100 }, new PointD3D[] { P3D_001, P3D_011, P3D_111, P3D_101 }, // YZ 面 new PointD3D[] { P3D_000, P3D_001, P3D_011, P3D_010 }, new PointD3D[] { P3D_100, P3D_101, P3D_111, P3D_110 }, // ZX 面 new PointD3D[] { P3D_000, P3D_001, P3D_101, P3D_100 }, new PointD3D[] { P3D_010, P3D_011, P3D_111, P3D_110 }, // X 棱 new PointD3D[] { P3D_000, P3D_100 }, new PointD3D[] { P3D_010, P3D_110 }, new PointD3D[] { P3D_001, P3D_101 }, new PointD3D[] { P3D_011, P3D_111 }, // Y 棱 new PointD3D[] { P3D_000, P3D_010 }, new PointD3D[] { P3D_100, P3D_110 }, new PointD3D[] { P3D_001, P3D_011 }, new PointD3D[] { P3D_101, P3D_111 }, // Z 棱 new PointD3D[] { P3D_000, P3D_001 }, new PointD3D[] { P3D_100, P3D_101 }, new PointD3D[] { P3D_010, P3D_011 }, new PointD3D[] { P3D_110, P3D_111 } }; PointF[][] Element2D = new PointF[][] { // XY 面 new PointF[] { P_000, P_010, P_110, P_100 }, new PointF[] { P_001, P_011, P_111, P_101 }, // YZ 面 new PointF[] { P_000, P_001, P_011, P_010 }, new PointF[] { P_100, P_101, P_111, P_110 }, // ZX 面 new PointF[] { P_000, P_001, P_101, P_100 }, new PointF[] { P_010, P_011, P_111, P_110 }, // X 棱 new PointF[] { P_000, P_100 }, new PointF[] { P_010, P_110 }, new PointF[] { P_001, P_101 }, new PointF[] { P_011, P_111 }, // Y 棱 new PointF[] { P_000, P_010 }, new PointF[] { P_100, P_110 }, new PointF[] { P_001, P_011 }, new PointF[] { P_101, P_111 }, // Z 棱 new PointF[] { P_000, P_001 }, new PointF[] { P_100, P_101 }, new PointF[] { P_010, P_011 }, new PointF[] { P_110, P_111 } }; // bool CuboidIsVisible = false; bool[] ElementVisible = new bool[Element2D.Length]; for (int i = 0; i < Element2D.Length; i++) { bool EVisible = false; foreach (PointF P in Element2D[i]) { PointD P2D = P; if (P2D.IsNaNOrInfinity) { EVisible = false; break; } else { if (Geometry.PointIsVisibleInRectangle(P2D, new RectangleF(new Point(0, 0), bmp.Size))) { EVisible = true; } } } ElementVisible[i] = EVisible; if (!CuboidIsVisible && EVisible) { CuboidIsVisible = true; } } if (!CuboidIsVisible) { return(false); } else { double[] IlluminationIntensity = new double[Element3D.Length]; double Exposure = Math.Max(-2, Math.Min(exposure / 50, 2)); if (illuminationDirection.IsZero) { for (int i = 0; i < 6; i++) { IlluminationIntensity[i] = Exposure; } for (int i = 6; i < Element3D.Length; i++) { IlluminationIntensity[i] = Constant.HalfSqrt2 * (Exposure + 1) + (Exposure - 1); } } else { PointD3D[] NormalVector = new PointD3D[] { // XY 面 _NormalVectorNXY, _NormalVectorPXY, // YZ 面 _NormalVectorNYZ, _NormalVectorPYZ, // ZX 面 _NormalVectorNZX, _NormalVectorPZX }; if (illuminationDirectionIsAfterAffineTransform) { PointD3D NewOriginOpposite = PointD3D.Zero.AffineTransformCopy(affineMatrices).Opposite; for (int i = 0; i < NormalVector.Length; i++) { NormalVector[i].AffineTransform(affineMatrices); NormalVector[i].Offset(NewOriginOpposite); } } double[] Angle = new double[NormalVector.Length]; for (int i = 0; i < NormalVector.Length; i++) { Angle[i] = illuminationDirection.AngleFrom(NormalVector[i]); } for (int i = 0; i < Angle.Length; i++) { double A = Angle[i]; double CosA = Math.Cos(A); double CosSqrA = CosA * CosA; double _IlluminationIntensity = (A < Constant.HalfPi ? -CosSqrA : (A > Constant.HalfPi ? CosSqrA : 0)); if (color.A < 255 && A != Constant.HalfPi) { double Transmittance = 1 - (double)color.A / 255; if (A < Constant.HalfPi) { _IlluminationIntensity += (Transmittance * Math.Abs(CosA) * CosSqrA); } else { _IlluminationIntensity -= ((1 - Transmittance) * (1 - Math.Abs(CosA)) * CosSqrA); } } _IlluminationIntensity += Exposure; IlluminationIntensity[i] = _IlluminationIntensity; } for (int i = 6; i < Element3D.Length; i++) { double _IlluminationIntensity = 0; int Num = 0; for (int j = 0; j < 6; j++) { bool Flag = true; foreach (PointD3D P in Element3D[i]) { if (!Element3D[j].Contains(P)) { Flag = false; break; } } if (Flag) { _IlluminationIntensity += IlluminationIntensity[j]; Num++; } } _IlluminationIntensity = Constant.HalfSqrt2 * (_IlluminationIntensity / Num + 1) + (Exposure - 1); IlluminationIntensity[i] = _IlluminationIntensity; } } for (int i = 0; i < IlluminationIntensity.Length; i++) { IlluminationIntensity[i] = Math.Max(-1, Math.Min(IlluminationIntensity[i], 1)); } // Color[] ElementColor = new Color[IlluminationIntensity.Length]; for (int i = 0; i < IlluminationIntensity.Length; i++) { double _IlluminationIntensity = IlluminationIntensity[i]; if (_IlluminationIntensity == 0) { ElementColor[i] = color; } else { ColorX EColor = color; if (_IlluminationIntensity < 0) { EColor.Lightness_HSL += EColor.Lightness_HSL * _IlluminationIntensity; } else { EColor.Lightness_HSL += (100 - EColor.Lightness_HSL) * _IlluminationIntensity; } ElementColor[i] = EColor.ToColor(); } } // double[] ElementZAvg = new double[Element3D.Length]; for (int i = 0; i < Element3D.Length; i++) { PointD3D[] Element = Element3D[i]; double ZAvg = 0; foreach (PointD3D P in Element) { ZAvg += P.Z; } ZAvg /= Element.Length; ElementZAvg[i] = ZAvg; } int[] ElementIndex = new int[ElementZAvg.Length]; for (int i = 0; i < ElementZAvg.Length; i++) { ElementIndex[i] = i; } for (int i = 0; i < ElementZAvg.Length; i++) { for (int j = i + 1; j < ElementZAvg.Length; j++) { if (ElementZAvg[ElementIndex[i]] < ElementZAvg[ElementIndex[j]] || (ElementZAvg[ElementIndex[i]] <= ElementZAvg[ElementIndex[j]] + edgeWidth && Element2D[ElementIndex[i]].Length < Element2D[ElementIndex[j]].Length)) { int Temp = ElementIndex[i]; ElementIndex[i] = ElementIndex[j]; ElementIndex[j] = Temp; } } } // using (Graphics Grph = Graphics.FromImage(bmp)) { if (antiAlias) { Grph.SmoothingMode = SmoothingMode.AntiAlias; } // for (int i = 0; i < ElementIndex.Length; i++) { int EIndex = ElementIndex[i]; if (ElementVisible[EIndex]) { Color EColor = ElementColor[EIndex]; if (!EColor.IsEmpty && EColor.A > 0) { PointF[] Element = Element2D[EIndex]; if (Element.Length >= 3) { try { using (SolidBrush Br = new SolidBrush(EColor)) { Grph.FillPolygon(Br, Element); } } catch { } } else if (Element.Length == 2) { if (edgeWidth > 0) { float EdgeWidth = (focalLength == 0 ? edgeWidth : (float)(focalLength / (ElementZAvg[EIndex] - PrjCenter.Z) * edgeWidth)); try { Brush Br; Func <Color, double, int> GetAlpha = (Cr, Z) => { int Alpha; if (focalLength == 0) { Alpha = Cr.A; } else { if (Z - PrjCenter.Z <= focalLength) { Alpha = Cr.A; } else { Alpha = (int)Math.Max(0, Math.Min(focalLength / (Z - PrjCenter.Z) * Cr.A, 255)); } } if (EdgeWidth < 1) { Alpha = (int)(Alpha * EdgeWidth); } return(Alpha); }; if (((PointD)Element[0]).DistanceFrom(Element[1]) > 1) { int Alpha0 = GetAlpha(EColor, Element3D[EIndex][0].Z), Alpha1 = GetAlpha(EColor, Element3D[EIndex][1].Z); Br = new LinearGradientBrush(Element[0], Element[1], Color.FromArgb(Alpha0, EColor), Color.FromArgb(Alpha1, EColor)); } else { int Alpha = GetAlpha(EColor, ElementZAvg[EIndex]); Br = new SolidBrush(Color.FromArgb(Alpha, EColor)); } using (Pen Pn = new Pen(Br, EdgeWidth)) { Grph.DrawLines(Pn, Element); } if (!(Br is null)) { Br.Dispose(); } } catch { } } } } } } } // return(true); } } }
private const string _NegativeMagnitudeOrderCode = "mμnpfazy"; // 千进制负数量级符号。 /// <summary> /// 返回一个以科学记数法表示数值的字符串。 /// </summary> /// <param name="value">数值。</param> /// <param name="significance">有效数字位数,0 表示保留所有有效数字。</param> /// <param name="useNaturalExpression">是否使用 "m×10^n" 的格式,而不是 "mE+n"。</param> /// <param name="useMagnitudeOrderCode">对于数量级介于 ±24 的数值,是否使用千进制数量级符号。</param> /// <param name="unit">单位。</param> /// <returns>字符串,表示以科学记数法表示的数值。</returns> public static string GetScientificNotationString(double value, int significance, bool useNaturalExpression, bool useMagnitudeOrderCode, string unit) { if (InternalMethod.IsNaNOrInfinity(value)) { return("N/A"); } else { string part1 = string.Empty, part2 = string.Empty, part3 = string.Empty, part4 = string.Empty; if (value == 0 || (value > -1E-308 && value < 1E-308)) { part2 = "0"; part4 = (string.IsNullOrEmpty(unit) ? string.Empty : " " + unit); } else { int sign = Math.Sign(value); part1 = (sign < 0 ? "-" : string.Empty); value = Math.Abs(value); significance = Math.Max(0, Math.Min(significance, 16)); int exp = (int)Math.Floor(Math.Log10(value)); if (significance > 0) { exp -= (significance - 1); value = Math.Round(value / Math.Pow(10, exp)); } else { value /= Math.Pow(10, exp); } while (value >= 10) { value /= 10; exp++; } if (useMagnitudeOrderCode) { if (exp >= -24 && exp < 27) { int mod = 0; if (exp >= 0) { mod = exp % 3; } else { mod = (-exp) % 3; if (mod > 0) { mod = 3 - mod; } } if (mod > 0) { value *= Math.Pow(10, mod); } part2 = (significance > 0 ? value.ToString("N" + Math.Max(0, significance - 1 - mod)) : value.ToString()); int mag = 0; if (exp >= 0) { mag = exp / 3; } else { mag = (exp + 1) / 3 - 1; } string magCode = (mag > 0 ? _PositiveMagnitudeOrderCode[mag - 1].ToString() : (mag < 0 ? _NegativeMagnitudeOrderCode[-mag - 1].ToString() : string.Empty)); if (string.IsNullOrEmpty(unit)) { part3 = magCode; part4 = string.Empty; } else { part3 = " " + magCode; part4 = unit; } } else { part2 = (significance > 0 ? value.ToString("N" + Math.Max(0, significance - 1)) : value.ToString()); part3 = (useNaturalExpression ? "×10^" + exp : (exp > 0 ? "E+" + exp : "E" + exp)); part4 = (string.IsNullOrEmpty(unit) ? string.Empty : " " + unit); } } else { part2 = (significance > 0 ? value.ToString("N" + Math.Max(0, significance - 1)) : value.ToString()); part3 = (exp == 0 ? string.Empty : (useNaturalExpression ? "×10^" + exp : (exp > 0 ? "E+" + exp : "E" + exp))); part4 = (string.IsNullOrEmpty(unit) ? string.Empty : " " + unit); } } return(string.Concat(part1, part2, part3, part4)); } }