Files
ShrlAlgoToolkit/ShrlAlgoToolkit.RevitAddins/RvIndependent/MetroGauges/CircuitHelper.cs

610 lines
22 KiB
C#
Raw Normal View History

using Autodesk.Revit.DB;
2025-04-24 20:56:44 +08:00
namespace ShrlAlgoToolkit.RevitAddins.RvIndependent.MetroGauges;
using Curve = LandXMLData.Curve;
using Interval = LandXMLData.Interval;
using LandXMLData_Alignment = LandXMLData.Alignment;
using LandXMLData_CircCurve = LandXMLData.CircCurve;
using LandXMLData_Spiral = LandXMLData.Spiral;
using Line = LandXMLData.Line;
public class CircuitHelper
{
/// <summary>
/// 获取线路所有竖曲线区间
/// </summary>
/// <param name="al"></param>
/// <returns></returns>
public Dictionary<LandXMLData_CircCurve, List<SpaceXYZ>> DictCir(LandXMLData_Alignment al)
{
var prof = al.GroundProfile;
Dictionary<LandXMLData_CircCurve, List<SpaceXYZ>> dictcir = new();
for (int i = 0; i < prof.Count - 1; i++)
{
if (prof[i].GetType() == typeof(CircCurve))
{
LandXMLData_CircCurve c = (LandXMLData_CircCurve)prof[i];
SpaceXYZ stap = new(prof[i - 1].First, prof[i - 1].Second);
SpaceXYZ pi = new(prof[i].First, prof[i].Second);
SpaceXYZ endp = new(prof[i + 1].First, prof[i + 1].Second);
List<SpaceXYZ> pts = GetStartCenterEnd(c, stap, pi, endp);
dictcir.Add(c, pts);
}
}
return dictcir;
}
/// <summary>
/// 得到区域内的参考点
/// </summary>
/// <param name="align"></param>
/// <param name="reg"></param>
/// <param name="interval"></param>
/// <returns></returns>
public List<XYZ> Get3DRefPointOfRegion(LandXMLData_Alignment align, Region reg, double interval)
{
List<XYZ> pts = new();
for (double i = reg.StartStation; i < reg.EndStation; i += interval)
{
XYZ pt = new(GetHorizonCoord(i, align).X, GetHorizonCoord(i, align).Y, GetElevAtStation(i, align));
pts.Add(pt);
}
XYZ lastp = GetHorizonCoord(reg.EndStation, align);
XYZ lastpt = new(lastp.X, lastp.Y, GetElevAtStation(reg.EndStation, align));
pts.Add(lastpt);
return pts;
}
/// <summary>
/// 获取圆曲线上的点(坐标英制)
/// </summary>
/// <param name="length">沿着线路方向从圆曲线起点行走过的距离</param>
/// <param name="c"></param>
/// <returns></returns>
public SpaceXYZ GetArcPtCoord(double length, Curve c)
{
double r = Math.Sqrt(
Math.Pow(c.Center.Second - c.Start.Second, 2.0) + Math.Pow(c.Center.First - c.Start.First, 2.0)
);
//弧度
double rad = length / r;
//计算圆心起点直线与X轴弧度带符号
double num3 = Math.Atan2(c.Start.First - c.Center.First, c.Start.Second - c.Center.Second);
double Xrad = num3 < 0.0 ? (2 * Math.PI) + num3 : num3;
//总弧度
double Totalrad = c.Length / r;
//斜率判断圆弧左转右转(起点与圆心直线斜率小于终点与圆心直线斜率,左转,逆时针;大于则右转)
int dir = -1;
if (
((c.Start.Second - c.Center.Second) * (c.End.First - c.Center.First))
- ((c.Start.First - c.Center.First) * (c.End.Second - c.Center.Second))
< 0.0
)
{
rad = -rad;
}
if (Math.Abs(Totalrad) > Math.PI)
{
rad = -rad;
}
double x = (r * Math.Cos((-dir * rad) + Xrad)) + c.Center.Second;
double y = (r * Math.Sin((-dir * rad) + Xrad)) + c.Center.First;
return new SpaceXYZ(x * 1000 / 304.8, y * 1000 / 304.8, 0);
}
/// <summary>
/// 纵断圆弧高程(英制)
/// </summary>
/// <param name="station"></param>
/// <param name="c"></param>
/// <param name="circleCenter"></param>
/// <param name="radius"></param>
/// <returns></returns>
public static double GetArcY(double station, LandXMLData_CircCurve c, SpaceXYZ circleCenter, double radius)
{
double[] y = new double[2];
//(x-x0)^2*+y0^2-R^2
double num = Math.Pow(radius, 2.0) - Math.Pow(station - circleCenter.Station, 2.0);
//4*R^2-4(x-x0)^2
//double num2 = 4.0 * Math.Pow(circleCenter.Bottom, 2.0) - 4.0 * num;
//y-y0的开方
double num3 = Math.Sqrt(num);
//y-y0值的不同符号
y[0] = circleCenter.Elevation - num3;
y[1] = circleCenter.Elevation + num3;
SpaceXYZ pvi = new(c.First, c.Second);
double elevation;
{
double l1 = GetLength(pvi, new SpaceXYZ(station, y[0]));
double l2 = GetLength(pvi, new SpaceXYZ(station, y[1]));
elevation = l1 <= l2 ? y[0] : y[1];
}
return elevation * 1000 / 304.8;
}
/// <summary>
/// 获取圆心坐标
/// </summary>
/// <param name="x1"></param>
/// <param name="y1"></param>
/// <param name="x2"></param>
/// <param name="y2"></param>
/// <param name="r"></param>
/// <param name="bump"></param>
/// <returns></returns>
public SpaceXYZ GetCircleCenter(double x1, double y1, double x2, double y2, double r, bool bump)
{
double c1 = ((x2 * x2) - (x1 * x1) + (y2 * y2) - (y1 * y1)) / (2 * (x2 - x1));
double c2 = (y2 - y1) / (x2 - x1); //斜率
double a = (c2 * c2) + 1;
double b = (2 * x1 * c2) - (2 * c1 * c2) - (2 * y1);
double c = (x1 * x1) - (2 * x1 * c1) + (c1 * c1) + (y1 * y1) - (r * r);
//为true时,为凹曲线,false为凸曲线
if (bump)
{
double y = (-b + Math.Sqrt((b * b) - (4 * a * c))) / (2 * a);
double x = c1 - (c2 * y);
return new SpaceXYZ(x, y);
}
else
{
double y = (-b - Math.Sqrt((b * b) - (4 * a * c))) / (2 * a);
double x = c1 - (c2 * y);
return new SpaceXYZ(x, y);
}
}
/// <summary>
/// 获取任意桩号位置的高程
/// </summary>
/// <param name="station"></param>
/// <param name="al"></param>
/// <returns></returns>
public double GetElevAtStation(double station, LandXMLData_Alignment al)
{
double elev = 0.0;
var prof = al.GroundProfile;
//var liprof = al.Profile.ProfileCurve;
//var prof = liprof[0];
Dictionary<LandXMLData_CircCurve, List<SpaceXYZ>> dictcir = DictCir(al);
//+0.1保证终点桩号的小数能满足条件
for (int i = 0; i < prof.Count - 1; i++)
{
if (station >= prof[i].First && station <= prof[i + 1].First + 0.1)
{
SpaceXYZ stap = new(prof[i].First, prof[i].Second);
SpaceXYZ endp = new(prof[i + 1].First, prof[i + 1].Second);
elev = GetLineY(station, stap, endp);
foreach (KeyValuePair<LandXMLData_CircCurve, List<SpaceXYZ>> cir in dictcir)
{
if (station >= cir.Value[0].Station && station <= cir.Value[2].Station)
{
elev = GetArcY(station, cir.Key, cir.Value[1], cir.Key.Radius);
return elev;
}
}
return elev;
}
}
//if (prof[i].GetType() == typeof(TextPoint2D))
//{
// SpaceXYZ stap = new SpaceXYZ(prof[i].First, prof[i].Second);
// SpaceXYZ endp = new SpaceXYZ(prof[i + 1].First, prof[i + 1].Second);
// elev = GetLinePtY(station, stap, endp);
//}
//GetStartAndEndSta()
return elev;
}
/// <summary>
/// 获取任意桩号位置的水平坐标
/// </summary>
/// <param name="station"></param>
/// <param name="al"></param>
/// <returns></returns>
public SpaceXYZ GetHorizonCoord(double station, LandXMLData_Alignment al)
{
List<Interval> hc = al.CoordGeom.HorizonCurve;
SpaceXYZ p = new(0.0, 0.0, 0.0);
Dictionary<Interval, Region> dicts = GetIntervalRegion(al);
//if (station > al.Length + al.StationStart)
//{
// return p;
//}
foreach (KeyValuePair<Interval, Region> dict in dicts)
{
double sta = dict.Value.StartStation;
double end = dict.Value.EndStation;
//加0.1保证终点桩号的小数能满足条件
if (station >= dict.Value.StartStation && station <= dict.Value.EndStation + 0.1)
{
double distance = station - dict.Value.StartStation;
if (dict.Key.GetType() == typeof(Autodesk.Revit.DB.Curve))
{
Curve c = (Curve)dict.Key;
//公制转英制,但是显示的时候英制转为公制
p = GetArcPtCoord(distance, c);
//ReferencePoint refpt = formdoc.FamilyCreate.NewReferencePoint(p);
//refpts.Append(refpt);
//SpaceXYZ spt = new SpaceXYZ(p.X, p.Y, p.Z);
//spt.Station = j + station;
//spt.Bottom = GetElevAtStation(spt.Station, al);
//spts.Add(spt);
}
else if (dict.Key.GetType() == typeof(Line))
{
Line l = (Line)dict.Key;
p = GetLinePtCoord(distance, l);
}
//else if (dict.Key.GetType() == typeof(landxml.Spiral))
else
{
LandXMLData_Spiral s = (LandXMLData_Spiral)dict.Key;
p = GetSpiralPtCoord(distance, s);
}
}
}
return p;
}
/// <summary>
/// 得到平曲线参考点
/// </summary>
/// <param name="al"></param>
/// <param name="doc"></param>
/// <returns></returns>
public List<XYZ> GetHorizonPts(LandXMLData_Alignment al, Document doc)
{
List<XYZ> pts = new();
var hc = al.CoordGeom.HorizonCurve;
//double station = al.StationStart;
for (int i = 0; i < hc.Count; i++)
{
//if (i - 1 >= 0)
//{
// station += hc[i - 1].Length;
//}
if (hc[i].GetType() == typeof(Autodesk.Revit.DB.Curve))
{
Curve c = (Curve)hc[i];
for (int j = 0; j < c.Length; j++)
{
//公制转英制,但是显示的时候英制转为公制
XYZ p = GetArcPtCoord(j, c);
pts.Add(p);
//ReferencePoint refpt = formdoc.FamilyCreate.NewReferencePoint(p);
//refpts.Append(refpt);
//SpaceXYZ spt = new SpaceXYZ(p.X, p.Y, p.Z);
//spt.Station = j + station;
//spt.Bottom = GetElevAtStation(spt.Station, al);
//spts.Add(spt);
}
}
else if (hc[i].GetType() == typeof(Autodesk.Revit.DB.Line))
{
Line l = (Line)hc[i];
for (int j = 0; j < l.Length; j++)
{
XYZ p = GetLinePtCoord(j, l);
pts.Add(p);
}
}
else if (hc[i].GetType() == typeof(Spiral))
{
LandXMLData_Spiral s = (LandXMLData_Spiral)hc[i];
for (int j = 0; j < s.Length; j++)
{
XYZ p = GetSpiralPtCoord(j, s);
pts.Add(p);
}
}
}
//最后一点
XYZ last = new XYZ(hc[hc.Count - 1].End.Second, hc[hc.Count - 1].End.First, 0.0) * 1000 / 304.8;
pts.Add(last);
//ReferencePointArray array = new ReferencePointArray();
//for (int i = 0; i < pts.Count; i++)
//{
// ReferencePoint refp = doc.FamilyCreate.NewReferencePoint(pts[i]);
// array.Append(refp);
//}
return pts;
}
/// <summary>
/// 按曲线类型获取区域
/// </summary>
/// <param name="al"></param>
/// <returns></returns>
public Dictionary<Interval, Region> GetIntervalRegion(LandXMLData_Alignment al)
{
var hc = al.CoordGeom.HorizonCurve;
Dictionary<Interval, Region> regions = new();
regions.Clear();
double currentsta = al.StationStart;
double endsta = 0.0;
if (al != null)
{
for (int i = 0; i < hc.Count; i++)
{
if (i - 1 >= 0)
{
//currentsta += Math.Round(hc[i - 1].Length, 3, MidpointRounding.AwayFromZero);
currentsta += hc[i - 1].Length;
}
Region re = new(currentsta, endsta);
if (hc[i].GetType() == typeof(Autodesk.Revit.DB.Curve))
{
Curve c = (Curve)hc[i];
//re.EndStation = Math.Round(c.Length + currentsta, 3, MidpointRounding.AwayFromZero);
re.EndStation = c.Length + currentsta;
}
else if (hc[i].GetType() == typeof(Autodesk.Revit.DB.Line))
{
Line l = (Line)hc[i];
//re.EndStation = Math.Round(l.Length + currentsta, 3, MidpointRounding.AwayFromZero);
re.EndStation = l.Length + currentsta;
}
else
{
LandXMLData_Spiral s = (LandXMLData_Spiral)hc[i];
re.EndStation = s.Length + currentsta;
}
regions.Add(hc[i], re);
}
}
//return allregions;
return regions;
}
/// <summary>
/// 计算两点直线距离
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <returns></returns>
public static double GetLength(SpaceXYZ p1, SpaceXYZ p2)
{
double num = p1.Station - p2.Station;
double num2 = p1.Elevation - p2.Elevation;
return Math.Sqrt((num * num) + (num2 * num2));
}
/// <summary>
/// 获取直线上的点(坐标英制)
/// </summary>
/// <param name="length">沿着线路方向从直线起点行走过的距离</param>
/// <param name="line"></param>
/// <returns></returns>
public SpaceXYZ GetLinePtCoord(double length, Line line)
{
XYZ startCoord = new(line.Start.Second, line.Start.First, 0);
XYZ endCoord = new(line.End.Second, line.End.First, 0);
//Autodesk.Revit.DB.Point stapt = Autodesk.Revit.DB.Point.Create(startCoord);
XYZ xyz = endCoord - startCoord;
double length2 = xyz.GetLength();
//if (length > line.Length)
//{
// System.Windows.MessageBox.ShowAhead("长度错误");
//}
XYZ p = xyz * length / length2;
XYZ pt = p + startCoord;
SpaceXYZ sp = new(pt.X * 1000 / 304.8, pt.Y * 1000 / 304.8, pt.Z * 1000 / 304.8);
return sp;
}
/// <summary>
/// 纵断直线高程(英制)
/// </summary>
/// <param name="station"></param>
/// <param name="startpoint"></param>
/// <param name="endpoint"></param>
/// <returns></returns>
public double GetLineY(double station, SpaceXYZ startpoint, SpaceXYZ endpoint)
{
double deltaY = endpoint.Elevation - startpoint.Elevation;
double deltaX = endpoint.Station - startpoint.Station;
double k = deltaY / deltaX;
double c = startpoint.Elevation - (k * startpoint.Station);
double elev = (k * station) + c;
return elev * 1000 / 304.8;
//double[] lineBetween2Points = GetLineBetween2Points(startpoint, endpoint);
//if (lineBetween2Points[1] != 0.0)
//{
// return (-lineBetween2Points[0] * station - lineBetween2Points[2]) / lineBetween2Points[1];
//}
//if (startpoint.Bottom <= endpoint.Bottom)
//{
// return endpoint.Bottom;
//}
//return startpoint.Bottom;
}
/// <summary>
/// 获取缓和曲线上的点(坐标英制)
/// </summary>
/// <param name="length">沿着线路方向从缓和曲线起点行走过的距离</param>
/// <param name="spiral">缓和曲线</param>
/// <returns>坐标点</returns>
public SpaceXYZ GetSpiralPtCoord(double length, LandXMLData_Spiral spiral)
{
//左右转向
double num =
((spiral.PI.Second - spiral.Start.Second) * (spiral.End.First - spiral.Start.First))
- ((spiral.PI.First - spiral.Start.First) * (spiral.End.Second - spiral.Start.Second));
//转角符号右为正,左为负
double direction = num >= 0.0 ? 1 : -1;
//根据起始半径1起点为直线起始曲率半径无穷大0起点为圆曲线起始半径为圆曲线半径,判断是前缓和曲线还是后缓和曲线前为1后为0
int fOrb = spiral.RadiusStart is double.PositiveInfinity or double.MaxValue ? 1 : 0;
//n3为1取终点半径n3为0取起点半径圆曲线半径
double R = fOrb != 0 ? spiral.RadiusEnd : spiral.RadiusStart;
//(缓和曲线长/圆曲线半径)^2 用于计算TotalX,TotalY
num = spiral.Length * spiral.Length / (R * R);
//x,y方向总距离
spiral.TotalX = spiral.Length * (1.0 - (num / 40.0) + (num * num / 3456.0));
spiral.TotalY = num * R / 6.0 * (1.0 - (num / 56.0) + (num * num / 7040.0));
//当前长度
double l = length;
double totalx = 0.0;
double totaly = 0.0;
double num8 = 1.0;
if (fOrb == 0)
{
l = spiral.Length - length;
num8 = -1.0;
//x,y方向总距离
totalx = spiral.TotalX;
totaly = spiral.TotalY;
}
num = l * l / (2.0 * R * spiral.Length);
double x = totalx + (num8 * l * (1.0 - (num * num / 10.0) + (Math.Pow(num, 4.0) / 216.0)));
double y =
direction * (-totaly + (l * ((num / 3.0) - (Math.Pow(num, 3.0) / 42.0) + (Math.Pow(num, 5.0) / 1320.0))));
//转东北距同时转英制
return TransformXYtoNE(x, y, direction * num8, spiral);
}
/// <summary>
/// 获取圆弧纵断线起点圆心终点
/// </summary>
/// <param name="c"></param>
/// <param name="start"></param>
/// <param name="pi"></param>
/// <param name="end"></param>
/// <returns></returns>
public List<SpaceXYZ> GetStartCenterEnd(LandXMLData_CircCurve c, SpaceXYZ start, SpaceXYZ pi, SpaceXYZ end)
{
List<SpaceXYZ> li = new();
//圆心角
double w = c.CurveLength / c.Radius;
//交点距圆弧起点桩号距离(X轴)
double l = Math.Tan(w / 2) * c.Radius;
//起点交点连线
double θ1 = RadianFromHorizon(start, pi);
//交点终点连线
double θ2 = RadianFromHorizon(pi, end);
//圆曲线起点
SpaceXYZ startpoint = new(pi.Station - (l * Math.Cos(θ1)), pi.Elevation - (l * Math.Sin(θ1)));
SpaceXYZ endpoint = new(pi.Station + (l * Math.Cos(θ2)), pi.Elevation + (l * Math.Sin(θ2)));
li.Add(startpoint);
if (θ2 - θ1 > 0)
{
//凹曲线
//SpaceXYZ centerpoint = new SpaceXYZ(start.Station - c.Radius * Math.Sin(θ1), start.Bottom + c.Radius * Math.Cos(θ1));
SpaceXYZ centerpoint = GetCircleCenter(
startpoint.Station,
startpoint.Elevation,
endpoint.Station,
endpoint.Elevation,
c.Radius,
true
);
li.Add(centerpoint);
}
else
{
//凸曲线
//SpaceXYZ centerpoint = new SpaceXYZ(start.Station + c.Radius * Math.Sin(θ1), start.Bottom - c.Radius * Math.Cos(θ1));
SpaceXYZ centerpoint = GetCircleCenter(
startpoint.Station,
startpoint.Elevation,
endpoint.Station,
endpoint.Elevation,
c.Radius,
false
);
li.Add(centerpoint);
}
//圆曲线终点
li.Add(endpoint);
return li;
}
/// <summary>
/// 纵断直线与X的夹角弧度
/// </summary>
/// <param name="start"></param>
/// <param name="end"></param>
/// <returns></returns>
public double RadianFromHorizon(SpaceXYZ start, SpaceXYZ end)
{
return Math.Atan((start.Elevation - end.Elevation) / (start.Station - end.Station));
}
/// <summary>
/// 坐标转换
/// </summary>
/// <param name="point"></param>
/// <param name="transform"></param>
/// <returns></returns>
public static XYZ TransformPoint(XYZ point, Transform transform)
{
double x = point.X;
double y = point.Y;
double z = point.Z;
//获取变换的原点和基向量
XYZ b0 = transform.get_Basis(0);
XYZ b1 = transform.get_Basis(1);
XYZ b2 = transform.get_Basis(2);
XYZ origin = transform.Origin;
//对原来坐标系统的点在新的坐标系统进行变换
double xTemp = (x * b0.X) + (y * b1.X) + (z * b2.X) + origin.X;
double yTemp = (x * b0.Y) + (y * b1.Y) + (z * b2.Y) + origin.Y;
double zTemp = (x * b0.Z) + (y * b1.Z) + (z * b2.Z) + origin.Z;
return new XYZ(xTemp, yTemp, zTemp);
}
/// <summary>
/// 缓和曲线坐标转东北距(坐标英制)
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <param name="signTotalY"></param>
/// <param name="spi"></param>
/// <returns></returns>
public SpaceXYZ TransformXYtoNE(double x, double y, double signTotalY, LandXMLData_Spiral spi)
{
double DeltaY = spi.End.First - spi.Start.First;
double DeltaX = spi.End.Second - spi.Start.Second;
spi.TotalY = signTotalY * spi.TotalY;
double num3 =
((spi.TotalX * DeltaY) - (spi.TotalY * DeltaX)) / (Math.Pow(spi.TotalX, 2.0) + Math.Pow(spi.TotalY, 2.0));
double num4 =
((spi.TotalX * DeltaX) + (spi.TotalY * DeltaY)) / (Math.Pow(spi.TotalX, 2.0) + Math.Pow(spi.TotalY, 2.0));
double num5 = (num4 * x) - (num3 * y);
double num6 = (num3 * x) + (num4 * y);
num6 += spi.Start.First;
return new SpaceXYZ((num5 + spi.Start.Second) * 1000 / 304.8, num6 * 1000 / 304.8, 0.0);
}
}