/// <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> /// 计算同一容器中一组控件的最小外接矩形。 /// </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)); } }
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); } } }