生成填充的类
渐变填充部分参考了才鸟的书里的代码,但是我改的也挺多的...
调用
namespace JoinBox
{
public partial class CmdTest
{
/// <summary>
/// 测试填充
/// </summary>
[CommandMethod("CmdTest_hatch")]
public void CmdTest_hatch()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
ed.WriteMessage($"{Environment.NewLine}填充测试");
//选择已有的闭合多段线进行内部填充
int actionNum = 2 | 4;
if ((actionNum & 1) == 1)
{
var psr = ed.GetSelection();
if (psr.Status != PromptStatus.OK)
return;
db.Action(tr => {
new HatchInfo(psr.Value.GetObjectIds(), true, null, 100, 0)
.Mode1PreDefined("ANSI34")
.Build(tr, db);
});
}
if ((actionNum & 2) == 2)
{
var psr = ed.GetSelection();
if (psr.Status != PromptStatus.OK)
return;
db.Action(tr => {
new HatchInfo(psr.Value.GetObjectIds(), true, null, 100, 0)
.Mode2UserDefined()
.Build(tr, db);
});
}
if ((actionNum & 4) == 4)
{
var psr = ed.GetSelection();
if (psr.Status != PromptStatus.OK)
return;
db.Action(tr => {
new HatchInfo(psr.Value.GetObjectIds(), true, null, 100, Math.PI / 2)
.Mode4Gradient(HatchInfo.HatchGradientName.Linear,
Autodesk.AutoCAD.Colors.Color.FromRgb(0, 0, 0),
Autodesk.AutoCAD.Colors.Color.FromRgb(152, 35, 100))
.Build(tr, db);
});
}
}
}
}
封装
#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Colors;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.Geometry;
using GrxCAD.Colors;
#endif
using System.Collections.Generic;
using System;
namespace JoinBox
{
/// <summary>
/// 图案填充
/// </summary>
public class HatchInfo
{
#region 成员
/// <summary>
/// 边界id(最外面放第一)
/// </summary>
List<ObjectId> _boundaryIds;
/// <summary>
/// 填充图元
/// </summary>
Hatch _hatch;
/// <summary>
/// 填充的名字
/// </summary>
string _hatchName;
/// <summary>
/// 填充模式类型(预定义/用户定义/自定义)
/// </summary>
HatchPatternType _patternTypeHatch;
/// <summary>
/// 渐变模式类型
/// </summary>
GradientPatternType _patternTypeGradient;
/// <summary>
/// 比例/间距
/// </summary>
double _scale;
/// <summary>
/// 角度
/// </summary>
double _angle;
/// <summary>
/// 边界关联
/// </summary>
bool _boundaryAssociative;
#endregion
#region 构造
/// <summary>
/// 图案填充
/// </summary>
/// <param name="boundaryAssociative">关联边界</param>
/// <param name="hatchOrigin">填充原点</param>
/// <param name="hatchScale">比例</param>
/// <param name="hatchAngle">角度</param>
public HatchInfo(bool boundaryAssociative = true,
Point2d? hatchOrigin = null,
double hatchScale = 1,
double hatchAngle = 0)
{
_hatch = new Hatch();
_hatch.SetDatabaseDefaults();
_boundaryIds = new();
_scale = hatchScale;
_angle = hatchAngle;
_boundaryAssociative = boundaryAssociative;
hatchOrigin ??= Point2d.Origin;
_hatch.Origin = hatchOrigin.Value; //填充原点
if (_scale <= 0)
throw new ArgumentNullException("填充比例不允许小于等于0");
_hatch.PatternScale = _scale; //填充比例
_hatch.PatternAngle = _angle; //填充角度
}
/// <summary>
/// 图案填充
/// </summary>
/// <param name="boundaryIds">边界</param>
/// <param name="boundaryAssociative">关联边界</param>
/// <param name="hatchOrigin">填充原点</param>
/// <param name="hatchScale">比例</param>
/// <param name="hatchAngle">角度</param>
public HatchInfo(IEnumerable<ObjectId> boundaryIds,
bool boundaryAssociative = true,
Point2d? hatchOrigin = null,
double hatchScale = 1,
double hatchAngle = 0)
: this(boundaryAssociative, hatchOrigin, hatchScale, hatchAngle)
{
_boundaryIds.AddRange(boundaryIds);
}
#endregion
#region 方法
/// <summary>
/// 模式1:预定义
/// </summary>
public HatchInfo Mode1PreDefined(string name)
{
_hatchName = name;
_hatch.HatchObjectType = HatchObjectType.HatchObject; //对象类型(填充/渐变)
_patternTypeHatch = HatchPatternType.PreDefined;
return this;
}
/// <summary>
/// 模式2:用户定义
/// </summary>
/// <param name="patternDouble">是否双向</param>
public HatchInfo Mode2UserDefined(bool patternDouble = true)
{
_hatchName = "_USER";
_hatch.HatchObjectType = HatchObjectType.HatchObject; //对象类型(填充/渐变)
_patternTypeHatch = HatchPatternType.UserDefined;
_hatch.PatternDouble = patternDouble; //是否双向(必须写在 SetHatchPattern 之前)
_hatch.PatternSpace = _scale; //间距(必须写在 SetHatchPattern 之前)
return this;
}
/// <summary>
/// 模式3:自定义
/// </summary>
/// <param name="name"></param>
public HatchInfo Mode3UserDefined(string name)
{
_hatchName = name;
_hatch.HatchObjectType = HatchObjectType.HatchObject; //对象类型(填充/渐变)
_patternTypeHatch = HatchPatternType.CustomDefined;
return this;
}
/// <summary>
/// 模式4:渐变填充
/// </summary>
/// <param name="name">渐变填充名称</param>
/// <param name="colorStart">渐变色起始颜色</param>
/// <param name="colorEnd">渐变色结束颜色</param>
/// <param name="gradientShift">渐变移动</param>
/// <param name="shadeTintValue">色调值</param>
/// <param name="gradientOneColorMode">单色<see langword="true"/>双色<see langword="false"/></param>
public HatchInfo Mode4Gradient(GradientName name, Color colorStart, Color colorEnd,
float gradientShift = 0,
float shadeTintValue = 0,
bool gradientOneColorMode = false)
{
//entget渐变的名字必然是"SOLID",但是这里作为"渐变"名,而不是"填充"名
_hatchName = name.ToString();
_hatch.HatchObjectType = HatchObjectType.GradientObject; //对象类型(填充/渐变)
_patternTypeGradient = GradientPatternType.PreDefinedGradient;//模式4:渐变
//_patternTypeGradient = GradientPatternType.UserDefinedGradient;//模式5:渐变..这种模式干啥用呢
//设置渐变色填充的起始和结束颜色
var gColor1 = new GradientColor(colorStart, 0);
var gColor2 = new GradientColor(colorEnd, 1);
_hatch.SetGradientColors(new GradientColor[] { gColor1, gColor2 });
_hatch.GradientShift = gradientShift; //梯度位移
_hatch.ShadeTintValue = shadeTintValue; //阴影色值
_hatch.GradientOneColorMode = gradientOneColorMode;//渐变单色/双色
_hatch.GradientAngle = _angle; //渐变角度
return this;
}
/// <summary>
/// 构建
/// </summary>
/// <param name="tr">事务</param>
/// <param name="db">数据库</param>
public ObjectId Build(Transaction tr, Database db)
{
//加入数据库
var hatchId = tr.AddEntityToMsPs(db, _hatch);
//设置模式
if (_hatch.HatchObjectType == HatchObjectType.GradientObject)
_hatch.SetGradient(_patternTypeGradient, _hatchName); //渐变模式类型
else
_hatch.SetHatchPattern(_patternTypeHatch, _hatchName); //填充模式类型
//关联边界,不先添加数据库会出错.
//true 因为会加入反应器,所以会比 false 慢,视需求而定.
//用了关联生成二维码,将会十几秒才生成好.
_hatch.Associative = _boundaryAssociative;
//利用 AppendLoop 重载加入,这里就不处理
if (_boundaryIds.Count > 0)
AppendLoop(_boundaryIds, HatchLoopTypes.Default);
//计算填充并显示(若边界出错,这句会异常)
_hatch.EvaluateHatch(true);
return hatchId;
}
/// <summary>
/// 执行图元的属性修改
/// </summary>
/// <param name="action">扔出填充实体</param>
public HatchInfo Action(Action<Hatch> action)
{
action(_hatch);
return this;
}
/// <summary>
/// 清空边界集合
/// </summary>
public HatchInfo ClearBoundary()
{
_boundaryIds.Clear();
return this;
}
/// <summary>
/// 删除边界图元
/// </summary>
public HatchInfo EraseBoundary(Transaction tr)
{
tr.EntityErase(_boundaryIds);
return this;
}
/// <summary>
/// 加入边界
/// </summary>
/// <param name="boundaryIds">边界id</param>
/// <param name="hatchLoopTypes">加入方式</param>
/// <returns></returns>
void AppendLoop(IEnumerable<ObjectId> boundaryIds,
HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default)
{
var obIds = new ObjectIdCollection();
//边界是闭合的,而且已经加入数据库
//填充闭合环类型.最外面
foreach (var border in boundaryIds)
{
obIds.Clear();
obIds.Add(border);
_hatch.AppendLoop(hatchLoopTypes, obIds.Collection);
}
obIds.Dispose();
}
/// <summary>
/// 加入边界(仿高版本的填充函数)
/// </summary>
/// <param name="pts">点集</param>
/// <param name="bluges">凸度集</param>
/// <param name="tr">事务</param>
/// <param name="db">数据库</param>
/// <param name="hatchLoopTypes">加入方式</param>
/// <returns></returns>
public HatchInfo AppendLoop(Point2dCollection pts, DoubleCollection bluges,
Transaction tr, Database db,
HatchLoopTypes hatchLoopTypes = HatchLoopTypes.Default)
{
//创建边界,之后再 Build
#if NET35
var polylineId = CreateAddBoundary(pts, bluges, tr, db);
_boundaryIds.Add(polylineId);
#else
//通过这里进入的话,边界 _boundaryIds 是空的,
//那么 Build 时候就需要过滤空的
var pts2 = pts.End2End();
_hatch.AppendLoop(hatchLoopTypes, pts2.Collection, bluges.Collection); //2011新增API
#endif
return this;
}
/// <summary>
/// 创建边界
/// </summary>
/// <param name="pts">点集</param>
/// <param name="bluges">凸度集</param>
/// <param name="tr">事务</param>
/// <param name="db">数据库</param>
/// <returns>多段线id</returns>
ObjectId CreateAddBoundary(Point2dCollection pts, DoubleCollection bluges,
Transaction tr, Database db)
{
var pts2 = pts.End2End();
var bvws = new List<BulgeVertexWidth>();
var itor1 = pts2.GetEnumerator();
var itor2 = bluges.GetEnumerator();
while (itor1.MoveNext() && itor2.MoveNext())
bvws.Add(new BulgeVertexWidth(itor1.Current, itor2.Current));
var polyline = EntityAdd.AddPolyLineToEntity(bvws);
return tr.AddEntityToMsPs(db, polyline);
}
#endregion
#region 枚举
/// <summary>
/// 渐变色填充的图案名称
/// </summary>
public enum GradientName
{
/// <summary>
/// 线状渐变
/// </summary>
Linear,
/// <summary>
/// 圆柱状渐变
/// </summary>
Cylinder,
/// <summary>
/// 反圆柱状渐变
/// </summary>
Invcylinder,
/// <summary>
/// 球状渐变
/// </summary>
Spherical,
/// <summary>
/// 反球状渐变
/// </summary>
Invspherical,
/// <summary>
/// 半球状渐变
/// </summary>
Hemisperical,
/// <summary>
/// 反半球状渐变
/// </summary>
InvHemisperical,
/// <summary>
/// 抛物面状渐变
/// </summary>
Curved,
/// <summary>
/// 反抛物面状渐变
/// </summary>
Incurved
}
#endregion
}
}
生成填充边界的类
有一些顾名思义的函数,基本上博客搜索一下都有,
搜索时候用 public BulgeVertexWidth
就可以搜到构造函数,而不是调用.
没有就自己写,毕竟看到这里的都是高手了.
比较特别的是调用了IFox的ToCurve函数
这个类测试得不是很全面,可能存在bug..
闲逛明经发现飞狐也写过,只是他是生成原线 http://bbs.mjtd.com/thread-75636-1-1.html
调用
/// <summary>
/// 测试填充边界生成
/// </summary>
[CommandMethod("CmdTest_HatchConverter")]
public void CmdTest_HatchConverter()
{
var dm = Acap.DocumentManager;
var doc = dm.MdiActiveDocument;
var db = doc.Database;
var ed = doc.Editor;
ed.WriteMessage($"{Environment.NewLine}测试填充边界生成");
var ids = ed.Ssget();
List<ObjectId> boIds = new();
db.Action(tr => {
ids.ToEntity(tr, ent => {
if (ent is Hatch hatch)
{
var hc = new HatchConverter(hatch);
hc.CreatBoundary(tr, db, boIds);
}
});
});
}
封装
#if !HC2020
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.Geometry;
#endif
using System;
using System.Collections.Generic;
using IFoxCAD.Cad;
using static JoinBox.EntityAdd;
using JoinBox.BasalMath;
namespace JoinBox
{
public class HatchConverter
{
#region 辅助类
class CircleData
{
public PointV Center;
public double Radius;
/// <summary>
/// 生成圆形数据
/// </summary>
/// <param name="symmetryAxisPoint1">对称点1</param>
/// <param name="symmetryAxisPoint2">对称点2</param>
public CircleData(PointV symmetryAxisPoint1, PointV symmetryAxisPoint2)
{
Center = symmetryAxisPoint1.GetCenter(symmetryAxisPoint2);
Radius = symmetryAxisPoint1.GetDistanceTo(symmetryAxisPoint2) / 2;
}
}
class HatchConverterData
{
public List<BulgeVertexWidth> PolyLineData;
public List<CircleData> CircleData;
public List<NurbCurve2d> SplineData;
public HatchConverterData()
{
PolyLineData = new();
CircleData = new();
SplineData = new();
}
}
#endregion
#region 成员
//外部只能调用id,否则跨事务造成错误
public ObjectId OldHatchId => _oldHatch.ObjectId;
Hatch _oldHatch;
List<HatchConverterData> _hcDatas;
#endregion
#region 构造
public HatchConverter()
{
_hcDatas = new();
}
/// <summary>
/// 填充边界转换器
/// </summary>
/// <param name="hatch">需要转化的Hatch对象</param>
public HatchConverter(Hatch hatch) : this()
{
_oldHatch = hatch;
//不能在提取信息的时候进行新建cad图元,
//否则cad将会提示遗忘释放
hatch.ForEach(loop => {
var hcData = new HatchConverterData();
bool isCurve2d = true;
if (loop.IsPolyline)
{
//边界是多段线
HatchLoopIsPolyline(loop, hcData);
isCurve2d = false;
}
else
{
//边界是曲线,过滤可能是圆形的情况
var cir = TwoArcFormOneCircle(loop);
if (cir != null)
{
hcData.CircleData.Add(cir);
isCurve2d = false;
}
}
//边界是曲线
if (isCurve2d)
HatchLoopIsCurve2d(loop, hcData);
_hcDatas.Add(hcData);
});
}
#endregion
#region 方法
/// <summary>
/// 多段线处理
/// </summary>
/// <param name="loop">填充边界</param>
/// <param name="hcData">收集图元信息</param>
void HatchLoopIsPolyline(HatchLoop loop, HatchConverterData hcData)
{
//判断为圆形:
//上下两个圆弧,然后填充,就会生成此种填充
//顶点数是3,凸度是半圆,两个半圆就是一个圆形
if (loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == 1 && loop.Polyline[1].Bulge == 1 ||
loop.Polyline.Count == 3 && loop.Polyline[0].Bulge == -1 && loop.Polyline[1].Bulge == -1)
{
hcData.CircleData.Add(new CircleData(loop.Polyline[0].Vertex, loop.Polyline[1].Vertex));
}
else
{
//遍历多段线信息
var bvc = loop.Polyline;
for (int i = 0; i < bvc.Count; i++)
hcData.PolyLineData.Add(new BulgeVertexWidth(bvc[i]));
}
}
/// <summary>
/// 两个圆弧组成圆形
/// </summary>
/// <param name="loop"></param>
/// <returns></returns>
CircleData TwoArcFormOneCircle(HatchLoop loop)
{
if (loop.Curves.Count != 2)//边界非多段线,但是只有两个点
return null;
CircleData circular = null;
//判断为圆形:
//用一条(不是两条)多段线画出两条圆弧为正圆,就会生成此种填充
//边界为曲线,数量为2,可能是两个半圆曲线,如果是,就加入圆形数据中
//第一段
var getCurves1Pts = loop.Curves[0].GetSamplePoints(3); //曲线取样点分两份(3点)
var mid1Pt = getCurves1Pts[1]; //腰点
double bulge1 = MathHelper.GetArcBulge(
loop.Curves[0].StartPoint, mid1Pt, loop.Curves[0].EndPoint);
//第二段
var getCurves2Pts = loop.Curves[1].GetSamplePoints(3);
var mid2Pt = getCurves2Pts[1];
double bulge2 = MathHelper.GetArcBulge(
loop.Curves[1].StartPoint, mid2Pt, loop.Curves[1].EndPoint);
//第一段上弧&&第二段反弧 || 第一段反弧&&第二段上弧
if (bulge1 == -1 && bulge2 == -1 || bulge1 == 1 && bulge2 == 1)
circular = new CircleData(loop.Curves[0].StartPoint, loop.Curves[1].StartPoint); //两个起点就是对称点
return circular;
}
/// <summary>
/// 处理边界曲线
/// </summary>
/// <param name="loop">填充边界</param>
/// <param name="hcData">收集图元信息</param>
void HatchLoopIsCurve2d(HatchLoop loop, HatchConverterData hcData)
{
var tol = new Autodesk.AutoCAD.Geometry.Tolerance(1e-6, 1e-6);
//取每一段曲线,曲线可能是直线来的,但是圆弧会按照顶点来分段
int curveIsClosed = 0;
//遍历边界的多个子段
foreach (Curve2d curve in loop.Curves)
{
//计数用于实现闭合
curveIsClosed++;
if (curve is NurbCurve2d spl)
{
//判断为样条曲线:
hcData.SplineData.Add(spl);
continue;
}
var pts = curve.GetSamplePoints(3);
var midPt = pts[1];
if (curve.StartPoint.IsEqualTo(curve.EndPoint, tol))//首尾相同,就是圆形
{
//判断为圆形:
//获取起点,然后采样三点,中间就是对称点(直径点)
hcData.CircleData.Add(new CircleData(curve.StartPoint, midPt));
continue;
}
//判断为多段线,圆弧:
double bulge = MathHelper.GetArcBulge(curve.StartPoint, midPt, curve.EndPoint);
hcData.PolyLineData.Add(new BulgeVertexWidth(curve.StartPoint, bulge));
//末尾点,不闭合的情况下就要获取这个
if (curveIsClosed == loop.Curves.Count)
hcData.PolyLineData.Add(new BulgeVertexWidth(curve.EndPoint, 0));
}
}
/// <summary>
/// 创建边界图元
/// </summary>
/// <param name="tr"></param>
/// <param name="db"></param>
/// <param name="boundaryIds">填充边界id</param>
/// <returns></returns>
public void CreateBoundary(Transaction tr, Database db, List<ObjectId> boundaryIds)
{
var ents = new List<Entity>();
for (int i = 0; i < _hcDatas.Count; i++)
{
var info = _hcDatas[i];
//生成边界:多段线
if (info.PolyLineData.Count > 0)
{
var ent = AddPolyLineToEntity(info.PolyLineData);
ents.Add(ent);
}
//生成边界:圆
info.CircleData.ForEach(item => {
var ent = AddCircleToEntity(item.Center.ToPoint3d(), item.Radius);
ents.Add(ent);
});
//生成边界:样条曲线
info.SplineData.ForEach(item => {
var ent = item.ToCurve();
ents.Add(ent);
});
}
ents.ForEach(ent => {
ent.Color = _oldHatch.Color;
ent.Layer = _oldHatch.Layer;
boundaryIds.Add(tr.AddEntityToMsPs(db, ent));
});
}
/// <summary>
/// 创建填充边界并设置到新填充上
/// </summary>
/// <param name="tr"></param>
/// <param name="db"></param>
/// <param name="boundaryIds">填充边界id</param>
/// <param name="boundaryAssociative">边界关联</param>
/// <returns>新填充的id</returns>
public ObjectId CreateBoundaryAndEntity(Transaction tr, Database db,
List<ObjectId> boundaryIds,
bool boundaryAssociative = true)
{
CreateBoundary(tr, db, boundaryIds);
/*
* 此处为什么要克隆填充,而不是新建填充?
* 因为填充如果是新建的,那么将会丢失基点,概念如下:
* 两个一样的填充,平移其中一个,那么再提取他们的基点会是一样的!
* 所以生成时候就不等同于画面相同.
* 也因为我不知道什么新建方式可以新建一模一样的填充,因此使用了克隆
* 它的平移后的基点在哪里呢?
*/
var newHatch = tr.EntityClone(OldHatchId.ToArray())[0];
newHatch.ToEntity(tr, ent => {
ReconstructionBoundary((Hatch)ent, boundaryIds, boundaryAssociative);
});
return newHatch;
}
/// <summary>
/// 重设边界
/// </summary>
/// <param name="hatch"></param>
/// <param name="boundaryIds">填充边界id</param>
/// <param name="boundaryAssociative">边界关联</param>
/// <returns></returns>
void ReconstructionBoundary(Hatch hatch,
List<ObjectId> boundaryIds,
bool boundaryAssociative = true)
{
if (boundaryIds == null || boundaryIds.Count == 0)
throw new ArgumentNullException("参数为空" + nameof(boundaryIds));
//删除原有边界
while (hatch.NumberOfLoops != 0)
hatch.RemoveLoopAt(0);
hatch.Associative = boundaryAssociative;
var obIds = new ObjectIdCollection();
for (int i = 0; i < boundaryIds.Count; i++)
{
obIds.Clear();
obIds.Add(boundaryIds[i]);
//要先添加最外面的边界
if (i == 0)
hatch.AppendLoop(HatchLoopTypes.Outermost, obIds.Collection);
else
hatch.AppendLoop(HatchLoopTypes.Default, obIds.Collection);
}
//计算填充并显示
hatch.EvaluateHatch(true);
}
#endregion
}
}
填充工具类
public static class HatchHelper
{
/// <summary>
/// 遍历填充每条边
/// </summary>
/// <param name="hatch"></param>
/// <param name="action"></param>
public static void ForEach(this Hatch hatch, Action<HatchLoop> action)
{
for (int i = 0; i < hatch.NumberOfLoops; i++)
{
var hatLoop = hatch.GetLoopAt(i);
action.Invoke(hatLoop);
}
}
}
(完)