cad.net 曲线类操作

说明

如果你想在界面上面画一个曲线,那么推荐你这个whudj博客

以下都是cad的曲线操作
cad曲线函数中文帮助

创建样条曲线

/// <summary>
/// 创建样条曲线
/// </summary>
/// <param name="splineType"><see langword="true"/>控制点ControlPoints;<see langword="false"/>:拟合点FitPoints</param>
/// <param name="points">点集</param>
/// <param name="tolerance">精度</param>
/// <returns></returns>
public static Entity AddSplineToEntity(bool splineType,
    IEnumerable<Point3d> points,
    double tolerance = 1e-10)
{
    if (points.Count() < 2)
        throw new ArgumentNullException(nameof(points) + "点集少于2");

    var pts = new Point3dCollection(points).Collection;

    if (!splineType)
        return new Spline(pts, 4, tolerance);/*order 创建的样条曲线的顺序(在2到26的范围内),arx默认4 */

    //首尾相同就闭合
    var tol = new Autodesk.AutoCAD.Geometry.Tolerance(tolerance, tolerance);
    bool closed = points.First().IsEqualTo(points.Last(), tol);

    // 要创建的样条的阶数(ARX说明: 在1到11的范围内,我们目前只支持阶数为3)
    int degree;
    switch (pts.Count)
    {
        case 2:
            degree = 1;
            break;
        case 3:
            degree = 2;
            break;
        default:
            degree = 3;
            break;
    }

    DoubleCollection knots = new();
    {
        //节点数计算方式:
        var num = pts.Count + degree + 1;
        for (int i = 0; i < num; i++)
            knots.Add(0);

        //通过spl命令绘制的基本规律
        if (num <= 8)
        {
            //对半分0/1
            for (int i = num / 2; i < num; i++)
                knots[i] = 1;
        }
        else
        {
            //前面四个值相同0,中间值递增,尾部4个值相同
            int addNum = 0;
            for (int i = 4; i < num; i++)
            {
                if (i < num - 4)
                {
                    addNum = i - 3;
                    knots[i] = addNum;                  
                }
                else
                    knots[i] = addNum + 1;//末尾4个用
            }
        }
    }

    //权重,cad的都是-1
    DoubleCollection weights = new();
    for (int i = 0; i < pts.Count; i++)
        weights.Add(-1.0);

    return new Spline(degree, false, closed, false,
                pts,
                knots.Collection,
                weights.Collection,
                tolerance, tolerance);
}


#if false
        // 0x01 官网这个例子不对,用拟合点作为参数的话,控制点比拟合点多,造成末尾抖动
        // https://help.autodesk.com/view/ACD/2016/ENU/?guid=GUID-A48AD068-9868-4F7A-8A51-F72A358EBB09 
        // 0x02 LibreCAD的样条曲线 https://wiki.librecad.org/lc3doc/lckernel/html/db/d07/spline_8cpp_source.html
        // 0x03 因为cad08用不了,但是这个函数是可行的.
        /// <summary>
        /// 控制点创建样条曲线
        /// </summary>
        /// <param name="points">控制点集</param>
        /// <returns></returns>
        public static Entity AddSplineToEntity(Point3d[] points)
        {
            var spl = new Spline();
            spl.SetDatabaseDefaults();
            spl.Type = SplineType.ControlPoints;
            spl.Rebuild(3, points.Length); 
            //控制点覆盖拟合点
            for (int i = 0; i < points.Length; i++)
                spl.SetControlPointAt(i, points[i]);
            return spl;
        }
#endif

曲线投影

曲线投影

依照旧样条曲线数据生成一条新样条曲线

Spline spl = entity as Spline; //拿到旧的spline图元...
//样条曲线生成条件
var controlPoints = new Point3dCollection();
for (int i = 0; i < spl.NumControlPoints; i++)  //控制点 
{
    controlPoints.Add(spl.GetControlPointAt(i));
}

NurbsData nu = spl.NurbsData;
entnew = new Spline(spl.Degree, spl.IsRational, spl.Closed,
                    spl.IsPeriodic,  //周期性  
                    controlPoints,   //控制点 
                    nu.GetKnots(),   //节点
                    nu.GetWeights(), //权重
                    nu.ControlPointTolerance,  //控制点容差
                    nu.KnotTolerance           //节点容差
                   );

也可以直接克隆

var splClone = spl.Clone();

记得降权,参考cad.net 所有的克隆方式

曲线采样

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using System;
using System.Collections.Generic;

namespace JoinBox
{
    public class CurveSplit
    {
        public List<Curve> Curves { get; set; }

        /// <summary>
        /// 获取定值分割的曲线集合
        /// </summary>
        /// <param name="curve">曲线</param>
        /// <param name="fixedValue">定值分割</param>
        public CurveSplit(Curve curve, double fixedValue)
        {
            Curves = new();

            //算曲线长度
            double curveLength = curve.GetLength();

            //若少于定值,则直接返回这条曲线,表示返回这段长度
            if (curveLength < fixedValue)
            {
                Curves.Add(curve);
                return;
            }

            var pts = new Point3dCollection();
            //用来叠加长度
            double overlyingLength = 0;
            //定值采集点
            while (overlyingLength < curveLength)
            {
                //求起点到长度的点
                pts.Add(curve.GetPointAtDist(overlyingLength));
                overlyingLength += fixedValue;
            }
            //最后没有完全重合,加入尾巴点
            if (overlyingLength - curveLength < 1e-6)
                pts.Add(curve.GetPointAtDist(curveLength));

            //通过点集,分割曲线
            var splits = curve.GetSplitCurves(pts.Collection);
            foreach (var item in splits)
            {
                var cuItem = (Curve)item;
                Curves.Add(cuItem);
            }
            pts.Dispose();
            splits.Dispose();//释放
        }

        /// <summary>
        /// 手动释放生成出来的曲线,
        /// 因为cad的Point3d没有继承,所以不能用 <see cref="IDisposable">进行释放</see>
        /// 否则提示:Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.Arc): DisposableWrapper
        /// </summary>
        public void Dispose()
        {
            Curves?.ForEach(cu => {
                cu.Dispose();
            });
        }
    }

    public class CurveSample
    {
        Curve _curve { get; set; }
        int _numSample { get; set; }

        /// <summary>
        /// 曲线采样
        /// </summary>
        /// <param name="curve">曲线</param>
        /// <param name="sampleNum">采样份数</param>
        public CurveSample(Curve curve, int sampleNum = 256)
        {
            _curve     = curve;
            _numSample = sampleNum;
        }

        /// <summary>
        /// 曲线采样(注意尾点是否缺少哦)
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Point3d> GetSamplePoints
        {
            get
            {
                if (_numSample == 0)
                    throw new System.Exception("NumSample参数不能为0");

                var length = _curve.GetLength();
                var fixedValue = length / _numSample;
                var cs = new CurveSplit(_curve, fixedValue);
                var curves = cs.Curves;

                var pts = new List<Point3d>();
                pts.Add(curves[0].StartPoint);//起点

                foreach (var item in curves)
                    pts.Add(item.EndPoint);//间隔点,尾点

                //末尾两个点可能一样,需要判断去除
                if (pts[pts.Count - 1] == pts[pts.Count - 2])
                    pts.RemoveAt(pts.Count - 1);

                cs.Dispose();
                return pts;
            }
        }
    }
}

调用方式见:葛立恒凸包 CurveSample

曲线长度

namespace JoinBox
{
    public static partial class MathHelper
    {
        /// <summary>
        /// 曲线长度
        /// </summary>
        /// <param name="curve">曲线</param>
        /// <returns></returns>
        public static double GetLength(this Curve curve)
        {
            return curve.GetDistanceAtParameter(curve.EndParam);
        }
    }
}

曲线起点到鼠标点长度

public partial class CmdTest
{
    [CommandMethod("CmdTest_Param")]
    public void CmdTest_Param()
    {
        var dm = Acap.DocumentManager;
        var doc = dm.MdiActiveDocument;
        var db = doc.Database;
        var ed = doc.Editor;

        var peo = new PromptEntityOptions("\n选择样条曲线:");
        var per = ed.GetEntity(peo);
        if (per.Status != PromptStatus.OK)
            return;

        var ps = ed.GetPoint(new PromptPointOptions("\n获取的点: "));
        if (ps.Status != PromptStatus.OK)
            return;

        var pt = ps.Value;
        ed.WriteMessage(pt.ToString());

        db.Action(tr => {
            var cur = tr.GetObject(per.ObjectId, OpenMode.ForRead) as Curve;
            if (cur == null)
                return;
            /*
             * 验证哪个是曲线长度很简单的操作是:
             * div命令1000,取一份直线长度,然后*1000,就很好拟合误差了.
             * 所以下面这句 曲线总长 就是对的.
             */
            var curLength = cur.GetLength();//曲线总长
            ed.WriteMessage("\n曲线总长: " + curLength);

            var cp = cur.GetClosestPointTo(pt, true);//最近点(为了点在曲线上)
            ed.WriteMessage("\n最近的点: " + cp);
            double ptParam = cur.GetParameterAtPoint(cp);//点参数(貌似是一种单位化之后得到的数值)
            ed.WriteMessage("\n点的参数: " + ptParam.ToString());
            ed.WriteMessage("\n末尾参数: " + cur.EndParam);

            var curPtLength = ptParam * (curLength / cur.EndParam);//曲线起点到鼠标点长度==点参数*(总长/总参数)
            ed.WriteMessage("\n点击长度: " + curPtLength);
        });
    }
}

缺省函数

db.Action委托无返回值写法

图示

spl的曲线参数就是拟合点连线长度(不过会受到缩放影响)

拟合点和控制点的定义

没有缩放的时候,获取末尾点参数就是拟合点连线长度(正常情况)
操作: 选择曲线,然后输入测试命令,选择曲线,点击末尾

缩放了之后只会呈现未缩放的长度(缩放情况,关闭cad之后自动修改回来)
操作: 缩放样条曲线,然后输入测试命令,选择曲线,点击末尾

相关阅读

SPL的数学定义

(完)

posted @ 2019-09-05 11:27  惊惊  阅读(1627)  评论(0编辑  收藏  举报