C#/.NET实现天花板喷淋点位及最近水管接驳点计算
.NET C# 喷淋布设计算实现方案
输入参数
天花板4个顶点3D坐标(Z值统一为2500.00,即喷淋安装面高度)
(97500.00, 34000.00, 2500.00) (85647.67, 43193.61, 2500.00) (91776.75, 51095.16, 2500.00) (103629.07, 41901.55, 2500.00)
天花板上方3根水管起止3D坐标
水管1:(98242.11, 36588.29, 3000.00) → (87970.10, 44556.09, 3500.00) 水管2:(99774.38, 38563.68, 3500.00) → (89502.37, 46531.47, 3000.00) 水管3:(101306.65, 40539.07, 3000.00) → (91034.63, 48506.86, 3000.00)
布设规则
- 喷淋头全部安装于天花板平面,Z坐标与天花板高度一致
- 喷淋头距离矩形天花板边界(墙体)最小距离为2500mm
- 相邻喷淋头间距为2500mm
输出要求
- 统计可安装喷淋头总数量
- 输出每个喷淋头的(x,y,z)三维坐标
- 输出每个喷淋头对应最近水管上的接驳点(x,y,z)三维坐标
核心计算逻辑
喷淋点位排布方法
输入的天花板为任意朝向矩形,非轴对齐矩形,不能直接按XY轴做网格排布,按以下步骤计算:
- 从4个天花板顶点中识别矩形的两条邻边,计算邻边方向向量、矩形实际长宽,以矩形某一顶点为原点建立局部二维坐标系,两个坐标轴分别与两条邻边平行
- 边界内缩:将原矩形四条边整体向内部偏移2500mm,得到喷淋允许布设的有效矩形区域;若内缩后矩形长宽任意一边小于等于0,说明房间尺寸不足,无法布设喷淋
- 网格布点:沿有效矩形的两个坐标轴方向,分别以2500mm为间隔逐行逐列取点,所有点Z值固定为天花板高度2500.00,落在有效矩形范围内的点即为合法喷淋点位
布点时首行、首列点位与内缩后矩形边界重合,即可保证最外侧喷淋与原墙体距离刚好为2500mm,满足规范要求
线段最近接驳点计算方法
对每个喷淋点,遍历所有水管线段,计算点到线段的最短距离,取距离最短的线段上的垂足作为接驳点,3D空间中点到线段的最近点计算逻辑:
- 设线段起点为A,终点为B,待计算喷淋点为P
- 计算方向向量
AB = B - A、AP = P - A - 计算投影参数
t = 向量AP与AB的点积 / 向量AB与自身的点积 - 对t做范围裁剪:t<0时取t=0,t>1时取t=1,保证接驳点落在线段本体上,不会取到线段延长线位置
- 最近接驳点坐标 = A + t * AB
- 遍历所有水管后,取距离最小的接驳点作为该喷淋的最终接驳点
*已知3D两点距离公式:d = Math.Sqrt(Math.Pow(x2-x1, 2) + Math.Pow(y2-y1, 2) + Math.Pow(z2-z1, 2))
实现伪代码(C#)
// 3D坐标结构体 public struct Point3D { public double X, Y, Z; } // 水管线段结构体 public struct PipeSegment { public Point3D Start; public Point3D End; } // --- 通用向量运算方法 --- // 向量减:返回 a - b Point3D VecSub(Point3D a, Point3D b) => new() {X = a.X - b.X, Y = a.Y - b.Y, Z = a.Z - b.Z}; // 向量数乘:返回 v * scale Point3D VecMul(Point3D v, double scale) => new() {X = v.X * scale, Y = v.Y * scale, Z = v.Z * scale}; // 向量加:返回 a + b Point3D VecAdd(Point3D a, Point3D b) => new() {X = a.X + b.X, Y = a.Y + b.Y, Z = a.Z + b.Z}; // 向量点积 double Dot(Point3D a, Point3D b) => a.X*b.X + a.Y*b.Y + a.Z*b.Z; // 3D两点距离 double Distance3D(Point3D a, Point3D b) { var dx = a.X - b.X; var dy = a.Y - b.Y; var dz = a.Z - b.Z; return Math.Sqrt(dx*dx + dy*dy + dz*dz); } // --- 主计算逻辑 --- List<(Point3D Sprinkler, Point3D ConnectPoint)> CalculateSprinklerLayout( List<Point3D> ceilingVertices, List<PipeSegment> pipes, double offsetToWall = 2500, double sprinklerSpacing = 2500) { // 1. 解析矩形参数,生成内缩2500mm后的有效布设区域 var (uAxis, vAxis, innerOrigin, innerLengthU, innerLengthV) = BuildInnerRect(ceilingVertices, offsetToWall); if (innerLengthU <= 0 || innerLengthV <=0) return new List<(Point3D, Point3D)>(); // 尺寸不足无法布设 // 2. 按2500mm间隔生成所有喷淋点位 List<Point3D> sprinklers = new List<Point3D>(); for (double u = 0; u <= innerLengthU + 1e-6; u += sprinklerSpacing) // 加1e-6容差避免浮点误差漏点 { for (double v = 0; v <= innerLengthV + 1e-6; v += sprinklerSpacing) { // 局部坐标转全局3D坐标,Z固定为天花板高度2500 var point = VecAdd(innerOrigin, VecAdd(VecMul(uAxis, u), VecMul(vAxis, v))); point.Z = 2500.0; sprinklers.Add(point); } } // 3. 逐点计算最近水管接驳点 List<(Point3D, Point3D)> result = new(); foreach (var sprinkler in sprinklers) { double minDist = double.MaxValue; Point3D bestConnect = default; foreach (var pipe in pipes) { var AB = VecSub(pipe.End, pipe.Start); var AP = VecSub(sprinkler, pipe.Start); double t = Dot(AP, AB) / Dot(AB, AB); t = Math.Clamp(t, 0, 1); var connect = VecAdd(pipe.Start, VecMul(AB, t)); var dist = Distance3D(sprinkler, connect); if (dist < minDist) { minDist = dist; bestConnect = connect; } } result.Add((sprinkler, bestConnect)); } return result; }
开发注意事项
- 浮点数运算需保留1e-6量级的容差,避免精度误差导致点位漏算、边界判断错误
- 矩形内缩时需校验偏移方向为矩形内侧,避免偏移到区域外
- 矩形邻边识别时,可通过判断相邻边向量点积为0(垂直)确认边的对应关系
内容的提问来源于stack exchange,提问作者royt85
相关产品推荐
相关产品推荐

