Files
Shrlalgo.RvKits/ShrlAlgoToolkit.RevitAddins/RvIndependent/MetroGauges/CircuitHelper.cs
2025-04-24 20:56:44 +08:00

610 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Autodesk.Revit.DB;
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);
}
}