328 lines
14 KiB
C#
328 lines
14 KiB
C#
using System.Windows;
|
||
using Autodesk.Revit.Attributes;
|
||
using Autodesk.Revit.DB;
|
||
using Autodesk.Revit.UI;
|
||
using CommunityToolkit.Diagnostics;
|
||
using Nice3point.Revit.Toolkit.External;
|
||
|
||
|
||
namespace ShrlAlgoToolkit.RevitAddins.RvIndependent.MetroTunnel;
|
||
|
||
[Transaction(TransactionMode.Manual)]
|
||
[Regeneration(RegenerationOption.Manual)]
|
||
public class MetroTunnelCmd : ExternalCommand
|
||
{
|
||
public override void Execute()
|
||
{
|
||
MetroTunnelViewModel viewModel = new();
|
||
MetroTunnelView view = new(viewModel);
|
||
view.ShowDialog();
|
||
if (view.DialogResult == false)
|
||
{
|
||
Result = Result.Cancelled;
|
||
return;
|
||
}
|
||
|
||
var importInstance = UiDocument.SelectObject<ImportInstance>("请选择dwg实例");
|
||
if (importInstance == null || !importInstance.Category.Name.EndsWith(".dwg"))
|
||
{
|
||
MessageBox.Show("请选择dwg隧道中心线", "提示");
|
||
Result = Result.Cancelled;
|
||
return;
|
||
}
|
||
|
||
var curves = importInstance.GetCurves();
|
||
if (curves.Count == 0)
|
||
{
|
||
ErrorMessage = "所选dwg参照不存在曲线";
|
||
Result = Result.Failed;
|
||
return;
|
||
}
|
||
|
||
List<XYZ> points = [];
|
||
//精度,每条曲线均分数
|
||
double precision = 3;
|
||
for (var i = 0; i < curves.Count; i++)
|
||
{
|
||
var curve = curves[i];
|
||
|
||
//points.Add(curve.GetEndPoint(0));
|
||
|
||
if (curve is Arc)
|
||
{
|
||
for (var j = 0; j < precision; j++)
|
||
{
|
||
points.Add(curve.Evaluate(j / precision, true));
|
||
}
|
||
}
|
||
|
||
if (i == curves.Count - 1)
|
||
{
|
||
points.Add(curve.GetEndPoint(1));
|
||
}
|
||
}
|
||
|
||
var spline = HermiteSpline.Create(points, false);
|
||
//var dividePoint = spline.Divide();
|
||
//起始角度
|
||
var startAngle = viewModel.StartAngle;
|
||
//前邻接块角度
|
||
var frontBehindAngle = viewModel.FrontTubeAngle;
|
||
//后邻接块角度
|
||
var backBehindAngle = viewModel.BackTubeAngle;
|
||
//标准块角度
|
||
var standardAngle = viewModel.StandardTubeAngle;
|
||
//管片宽度
|
||
var width = viewModel.TubeWidth / 304.8;
|
||
//管片厚度
|
||
var thickness = viewModel.TubeThickness / 304.8;
|
||
//大圆半径
|
||
var outerDiameter = viewModel.TubeOuterDiameter / 304.8;
|
||
;
|
||
//小圆半径
|
||
var interDiameter = outerDiameter - (2 * thickness);
|
||
//错缝变化角度
|
||
var switchAngle = viewModel.StaggerAngle;
|
||
SegmentPlacementType placementType = 0;
|
||
var num = Math.Floor(spline.Length / width);
|
||
List<List<XYZ>> lists = [];
|
||
for (var i = 0; i < num - 1; i++)
|
||
{
|
||
//var p1 = spline.Evaluate(i * 2000 / 304.8, false);
|
||
//if (spline.IsInsideEx((i + 1) * 2000 / 304.8))
|
||
//{
|
||
// var p2 = spline.Evaluate((i + 1) * 2000 / 304.8, false);
|
||
//}
|
||
//basicX为切向量,basicY为法向量(垂直该点),basicZ为basicX叉乘basicY
|
||
var transform1 = spline.ComputeDerivatives(i * width, false);
|
||
var transform2 = spline.ComputeDerivatives((i + 1) * width, false);
|
||
//Frame frame1 = new Frame();
|
||
//frame1.Transform(transform1);
|
||
//平面法向与路线曲线方向相反,即平面法向面向人视角,在创建圆时才能保证角度旋转准确
|
||
//var frontPlane = Plane.CreateByNormalAndOrigin(-transform1.BasisX, transform1.Origin);
|
||
var frontRight = transform1.BasisX.CrossProduct(XYZ.BasisZ).Normalize();
|
||
var frontTop = frontRight.CrossProduct(transform1.BasisX).Normalize();
|
||
var frontPlane = Plane.CreateByOriginAndBasis(transform1.Origin, frontRight, frontTop);
|
||
|
||
//Frame frame2 = new Frame();
|
||
//frame1.Transform(transform2);
|
||
//var backPlane = Plane.CreateByNormalAndOrigin(transform2.BasisX, transform2.Origin);
|
||
var backRight = transform2.BasisX.CrossProduct(XYZ.BasisZ).Normalize();
|
||
var backTop = backRight.CrossProduct(transform2.BasisX).Normalize();
|
||
var backPlane = Plane.CreateByOriginAndBasis(transform2.Origin, backRight, backTop);
|
||
|
||
var frontOuterCircle = Arc.Create(frontPlane, outerDiameter / 2, 0, Math.PI * 2);
|
||
var frontInnerCircle = Arc.Create(frontPlane, interDiameter / 2, 0, Math.PI * 2);
|
||
var backOuterCircle = Arc.Create(backPlane, outerDiameter / 2, 0, Math.PI * 2);
|
||
var backInnerCircle = Arc.Create(backPlane, interDiameter / 2, 0, Math.PI * 2);
|
||
|
||
//frontOuterCircle.MakeBound(0, frontOuterCircle.Length);
|
||
//frontInnerCircle.MakeBound(0, frontInnerCircle.Length);
|
||
//backOuterCircle.MakeBound(0, backOuterCircle.Length);
|
||
//backInnerCircle.MakeBound(0, backInnerCircle.Length);
|
||
//doc.Invoke(ts =>
|
||
//{
|
||
// if (i < 10)
|
||
// {
|
||
// doc.Create.NewModelCurve(frontFaceOuterCircle, sk1);
|
||
// doc.Create.NewModelCurve(frontFaceInnerCircle, sk1);
|
||
// doc.Create.NewModelCurve(backFaceOuterCircle, sk2);
|
||
// doc.Create.NewModelCurve(backFaceInnerCircle, sk2);
|
||
// }
|
||
|
||
//});
|
||
var actuallyFrontStartAngle = MetroTunnelCmd.GetReallyStartAngle(placementType, startAngle, switchAngle, i);
|
||
var actuallyBackStartAngle = MetroTunnelCmd.GetReallyStartAngle(placementType, startAngle, switchAngle, i + 1);
|
||
var param = MetroTunnelCmd.CurveParameters(actuallyFrontStartAngle, frontBehindAngle, standardAngle);
|
||
var param1 = MetroTunnelCmd.CurveParameters(actuallyBackStartAngle, backBehindAngle, standardAngle);
|
||
for (var j = 0; j < param.Length; j++)
|
||
{
|
||
var paramF = param[j];
|
||
var paramB = param1[j];
|
||
//doc.Invoke(_ =>
|
||
//{
|
||
//var sk1 = SketchPlane.Create(doc, frontPlane);
|
||
//var sk2 = SketchPlane.Create(doc, backPlane);
|
||
|
||
//var p1 = frontInnerCircle.Evaluate(paramF[0], true);
|
||
//var p2 = frontInnerCircle.Evaluate(paramF[1], true);
|
||
//var p3 = frontInnerCircle.Evaluate(paramF[2], true);
|
||
var arc1 = Arc.Create(
|
||
frontPlane,
|
||
interDiameter / 2,
|
||
(paramF[0] > paramF[2] ? paramF[0] - 1 : paramF[0]) * 360.0.ToRadian(),
|
||
paramF[2] * 360.0.ToRadian()
|
||
);
|
||
var points1 = arc1.GetEquivalentPoints(2);
|
||
//var mc1 = doc.Create.NewModelCurve(arc1, sk1);
|
||
//var l1 = mc1.GeometryCurve.Length;
|
||
|
||
//var p4 = backInnerCircle.Evaluate(paramB[0], true);
|
||
//var p5 = backInnerCircle.Evaluate(paramB[1], true);
|
||
//var p6 = backInnerCircle.Evaluate(paramB[2], true);
|
||
|
||
var arc2 = Arc.Create(
|
||
backPlane,
|
||
interDiameter / 2,
|
||
(paramB[0] > paramB[2] ? paramB[0] - 1 : paramB[0]) * 360.0.ToRadian(),
|
||
paramB[2] * 360.0.ToRadian()
|
||
);
|
||
//var mc2 = doc.Create.NewModelCurve(arc2, sk2);
|
||
var points2 = arc2.GetEquivalentPoints(2);
|
||
//if (j == 0)
|
||
//{
|
||
// doc.Create.NewModelCurve(Arc.Create(p1, 2, 0, 2 * Math.PI, frontPlane.XVec, frontPlane.YVec), sk1);
|
||
// doc.Create.NewModelCurve(Arc.Create(frontInnerCircle.Evaluate(0, true), 2, 0, 2 * Math.PI, frontPlane.XVec, frontPlane.YVec), sk1);
|
||
// doc.Create.NewModelCurve(frontOuterCircle, sk1);
|
||
|
||
// doc.Create.NewModelCurve(Arc.Create(p2, 2, 0, 2 * Math.PI, frontPlane.XVec, frontPlane.YVec), sk1);
|
||
// doc.Create.NewModelCurve(Arc.Create(p3, 2, 0, 2 * Math.PI, frontPlane.XVec, frontPlane.YVec), sk1);
|
||
// doc.Create.NewModelCurve(Line.CreateBound(frontPlane.Origin, frontPlane.Origin + frontPlane.XVec), sk1);
|
||
// doc.Create.NewModelCurve(Line.CreateBound(frontPlane.Origin, frontPlane.Origin + frontPlane.YVec), sk1);
|
||
// //doc.Create.NewModelCurve(Arc.Create(backInnerCircle.Evaluate(0, true), 2, 0, 2 * Math.PI, frontPlane.XVec, frontPlane.YVec), sk2);
|
||
//}
|
||
//var l2 = mc2.GeometryCurve.Length;
|
||
//var p7 = frontOuterCircle.Evaluate(paramF[0], true);
|
||
//var p8 = frontOuterCircle.Evaluate(paramF[1], true);
|
||
//var p9 = frontOuterCircle.Evaluate(paramF[2], true);
|
||
//doc.Create.NewModelCurve(Arc.Create(p7, p9, p8), sk1);
|
||
//var p10 = backOuterCircle.Evaluate(paramB[0] , true);
|
||
//var p11 = backOuterCircle.Evaluate(paramB[1], true);
|
||
//var p12 = backOuterCircle.Evaluate(paramB[2], true);
|
||
//doc.Create.NewModelCurve(Arc.Create(p10, p12, p11), sk2);
|
||
var arc3 = Arc.Create(
|
||
frontPlane,
|
||
outerDiameter / 2,
|
||
(paramF[0] > paramF[2] ? paramF[0] - 1 : paramF[0]) * 360.0.ToRadian(),
|
||
paramF[2] * 360.0.ToRadian()
|
||
);
|
||
var points3 = arc3.GetEquivalentPoints(2);
|
||
var arc4 = Arc.Create(
|
||
backPlane,
|
||
outerDiameter / 2,
|
||
(paramB[0] > paramB[2] ? paramB[0] - 1 : paramB[0]) * 360.0.ToRadian(),
|
||
paramB[2] * 360.0.ToRadian()
|
||
);
|
||
var points4 = arc4.GetEquivalentPoints(2);
|
||
var list = new List<XYZ>();
|
||
list.AddRange(points1);
|
||
list.AddRange(points2);
|
||
list.AddRange(points3);
|
||
list.AddRange(points4);
|
||
//list = new List<XYZ>()
|
||
//{
|
||
// p1, p2, p3,p4,p5,p6, p7, p8, p9,p10,p11,p12
|
||
//};
|
||
//});
|
||
lists.Add(list);
|
||
}
|
||
}
|
||
|
||
Document.Invoke(_ =>
|
||
{
|
||
var familySymbol = Document
|
||
.OfClass<FamilySymbol>()
|
||
.Cast<FamilySymbol>()
|
||
.First(
|
||
symbol => symbol.Name == "盾构管片" && AdaptiveComponentInstanceUtils.IsAdaptiveFamilySymbol(symbol)
|
||
);
|
||
if (!familySymbol.IsActive)
|
||
{
|
||
familySymbol.Activate();
|
||
}
|
||
|
||
for (var i = 0; i < lists.Count; i++)
|
||
{
|
||
var list = lists[i];
|
||
if (i == 100)
|
||
{
|
||
return;
|
||
}
|
||
|
||
familySymbol.CreateAdaptiveComponent(list);
|
||
}
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算圆弧上的所有自适应布置点角度与整圆(360)的比值[0,1]
|
||
/// </summary>
|
||
/// <param name="startAngle">实际起始角度</param>
|
||
/// <param name="behindAngle">邻接块角度跨度</param>
|
||
/// <param name="standardAngle">标准块角度跨度</param>
|
||
/// <returns>数组的数组集合</returns>
|
||
private static double[][] CurveParameters(double startAngle, double behindAngle, double standardAngle)
|
||
{
|
||
var x = startAngle / 360;
|
||
//封顶块
|
||
var fdq = 360 - (standardAngle * 3) - (behindAngle * 2);
|
||
Guard.IsGreaterThan(fdq, 0, "封顶块角度跨度不能为负");
|
||
|
||
var a0 = startAngle + behindAngle;
|
||
var b0 = a0 + fdq;
|
||
var c0 = b0 + behindAngle;
|
||
var d0 = c0 + standardAngle;
|
||
var e0 = d0 + standardAngle;
|
||
|
||
var aq = a0 / 360;
|
||
var bq = b0 / 360;
|
||
var cq = c0 / 360;
|
||
var dq = d0 / 360;
|
||
var eq = e0 / 360;
|
||
var xaq = (x + aq) / 2;
|
||
var abq = (aq + bq) / 2;
|
||
var bcq = (bq + cq) / 2;
|
||
var cdq = (cq + dq) / 2;
|
||
var deq = (dq + eq) / 2;
|
||
var con = (eq - 1 + x) / 2;
|
||
var exq = con < 0 ? 1 + con : con;
|
||
return
|
||
[
|
||
[x, xaq, aq],
|
||
[aq, abq, bq],
|
||
[bq, bcq, cq],
|
||
[cq, cdq, dq],
|
||
[dq, deq, eq],
|
||
[eq, exq, x]
|
||
];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算起始角度每环的实际起始角度(第一环不用计算就是StartAngle)
|
||
/// </summary>
|
||
/// <param name="placementType">拼装方式</param>
|
||
/// <param name="startAngle">标准块起始角度</param>
|
||
/// <param name="switchAngle">错缝变化角度</param>
|
||
/// <param name="currentFaceNumber">当前管环面的索引+1</param>
|
||
/// <returns></returns>
|
||
private static double GetReallyStartAngle(
|
||
SegmentPlacementType placementType,
|
||
double startAngle,
|
||
double switchAngle,
|
||
int currentFaceNumber
|
||
)
|
||
{
|
||
double result = default;
|
||
switch (placementType)
|
||
{
|
||
case SegmentPlacementType.Common:
|
||
result = startAngle + (switchAngle * (0.5 - (0.5 * Math.Pow(1, currentFaceNumber))));
|
||
break;
|
||
case SegmentPlacementType.Swing:
|
||
result = startAngle + (switchAngle * (0.5 + (0.5 * Math.Pow(-1, currentFaceNumber))));
|
||
break;
|
||
case SegmentPlacementType.ClockWise:
|
||
result = startAngle + (switchAngle * (currentFaceNumber - 1));
|
||
break;
|
||
case SegmentPlacementType.CounterClockWise:
|
||
result =
|
||
startAngle
|
||
- (switchAngle * (currentFaceNumber - 1))
|
||
- (Math.Floor((startAngle - (switchAngle * (currentFaceNumber - 1))) / 360) * 360);
|
||
break;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
}
|