public static class JigUtils
{
public static double Atan(double y, double x)
{
if (x > 0)
return Math.Atan(y / x);
else if (x < 0)
return Math.Atan(y / x) + Math.PI;
else // x == 0
{
if (y > 0)
return Math.PI / 2;
else if (y < 0)
return -Math.PI / 2;
else // y == 0, theta is undefined
return 0.0;
}
}
public static double ComputeAngle(Point3d startPoint, Point3d endPoint, Vector3d xdir, Matrix3d ucs)
{
Vector3d v
= new Vector3d
((endPoint
.X - startPoint
.X) / 2,
(endPoint
.Y - startPoint
.Y) / 2,
(endPoint
.Z - startPoint
.Z) / 2); double cos = v.DotProduct(xdir);
double sin = v.DotProduct(Vector3d.ZAxis.TransformBy(ucs).CrossProduct(xdir));
return Atan(sin, cos);
}
}
public class BulgePolyJig : EntityJig
{
public Stack
<Point3d
> _pointHistory
= new Stack
<Point3d
>(); public Point3d _tempPoint;
Plane _plane;
bool _isArcSeg = true; // Arc is the default
bool _isUndoing = false;
Matrix3d _ucs;
Polyline FlexLine;
public BulgePolyJig
(Matrix3d ucs
) : base(new Polyline
()) {
_ucs = ucs;
CoordinateSystem3d cs = ucs.CoordinateSystem3d;
Vector3d normal = cs.Zaxis;
_plane
= new Plane
(Point3d
.Origin, normal
);
Polyline pline = Entity as Polyline;
pline.SetDatabaseDefaults();
pline.Normal = normal;
Point3d closest = cs.Origin.Project(_plane, normal);
Vector3d disp = closest - cs.Origin;
pline.Elevation = disp.IsCodirectionalTo(normal) ? -disp.Length : disp.Length;
AddDummyVertex();
}
protected override SamplerStatus Sampler(JigPrompts prompts)
{
JigPromptPointOptions jigOpts
= new JigPromptPointOptions
();
jigOpts.UserInputControls = UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted | UserInputControls.NoNegativeResponseAccepted | UserInputControls.GovernedByOrthoMode;
_isUndoing = false;
Polyline pline = Entity as Polyline;
if (pline.NumberOfVertices == 1)
{
jigOpts.Message = "\nSpecify start point: ";
}
else if (pline.NumberOfVertices > 1)
{
string msgAndKwds = _isArcSeg ? "\nSpecify endpoint of arc or [Line/Undo]: " : "\nSpecify next point or [Arc/Undo]: ";
string kwds = _isArcSeg ? "Line Undo" : "Arc Undo";
jigOpts.SetMessageAndKeywords(msgAndKwds, kwds);
}
else
return SamplerStatus.Cancel; // Should never happen
PromptPointResult res = prompts.AcquirePoint(jigOpts);
if (res.Status == PromptStatus.Keyword)
{
if (res.StringResult.Equals("ARC", StringComparison.CurrentCultureIgnoreCase))
_isArcSeg = true;
else if (string.Equals(res.StringResult, "LINE", StringComparison.OrdinalIgnoreCase))
_isArcSeg = false;
else if (string.Equals(res.StringResult, "UNDO", StringComparison.OrdinalIgnoreCase))
_isUndoing = true;
return SamplerStatus.OK;
}
else if (res.Status == PromptStatus.OK)
{
if (_tempPoint == res.Value)
return SamplerStatus.NoChange;
else
{
_tempPoint = res.Value;
_pointHistory.Push(_tempPoint); // Store the point
return SamplerStatus.OK;
}
}
return SamplerStatus.Cancel;
}
protected override bool Update()
{
Polyline pl = Entity as Polyline;
FlexLine = pl;
double width = (double)GlobalVariables.MainWindow.Dim_Width.Value; // Get the desired width
// Check if we're setting the first real point
if (pl.NumberOfVertices == 1 && _tempPoint != Point3d.Origin)
{
pl.SetPointAt(0, _tempPoint.Convert2d(_plane));
pl.SetStartWidthAt(0, width);
pl.SetEndWidthAt(0, width);
}
else if (_isArcSeg && pl.NumberOfVertices >= 2)
{
// Safe to assume there are at least 2 vertices here
Point3d lastVertex = pl.GetPoint3dAt(pl.NumberOfVertices - 2);
Vector3d refDir;
// Determine reference direction for calculating bulge
if (pl.NumberOfVertices < 3)
{
refDir
= new Vector3d
(1.0,
1.0,
0.0); }
else
{
double bulge = pl.GetBulgeAt(pl.NumberOfVertices - 3);
if (bulge != 0)
{
CircularArc3d arcSegment = pl.GetArcSegmentAt(pl.NumberOfVertices - 3);
Line3d tangent = arcSegment.GetTangent(lastVertex);
refDir = tangent.Direction.MultiplyBy(-1.0);
}
else
{
Point3d ptBeforeLast = pl.GetPoint3dAt(pl.NumberOfVertices - 3);
refDir = lastVertex - ptBeforeLast;
}
}
double angle = JigUtils.ComputeAngle(lastVertex, _tempPoint, refDir, _ucs);
double bulgeValue = Math.Tan(angle * 0.5);
pl.SetBulgeAt(pl.NumberOfVertices - 2, bulgeValue);
}
else if (!_isArcSeg && pl.NumberOfVertices >= 2)
{
// For line segments, just ensure the last bulge is set to 0
if (pl.NumberOfVertices > 1)
{
pl.SetBulgeAt(pl.NumberOfVertices - 2, 0);
}
}
// Always update the last vertex position
pl.SetPointAt(pl.NumberOfVertices - 1, _tempPoint.Convert2d(_plane));
return true;
}
public bool IsUndoing
{
get
{
return _isUndoing;
}
}
public void AddDummyVertex()
{
Polyline pline = Entity as Polyline;
double width = (double)GlobalVariables.MainWindow.Dim_Width.Value; // Assuming this is accessible here
// Add a vertex with the specified width. If this is the first vertex, it doesn't form a segment yet,
// so the width will apply to the segment formed when the next vertex is added.
pline
.AddVertexAt(pline
.NumberOfVertices,
new Point2d
(0,
0),
0, width, width
); }
public void RemoveLastVertex()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
Polyline pline = Entity as Polyline;
if (pline.NumberOfVertices > 0)
{
// Remove the last vertex
pline.RemoveVertexAt(pline.NumberOfVertices - 1);
}
// After removal, check if there are enough vertices to determine the last point
if (pline.NumberOfVertices >= 2)
{
// Get the endpoint of the new last segment
Point3d newLastEndPoint = pline.GetPoint3dAt(pline.NumberOfVertices - 2);
// Update LASTPOINT system variable
Application.SetSystemVariable("LASTPOINT", newLastEndPoint);
}
else
{
// If no vertices are left, consider how you want to handle LASTPOINT.
// You might leave it as it is or reset it to some default value.
// This example resets it to the origin, but you might choose a different approach.
Application.SetSystemVariable("LASTPOINT", Point3d.Origin);
}
// Adjust arc segment flag based on the last segment's bulge, if applicable.
if (pline.NumberOfVertices >= 2)
{
double blg = pline.GetBulgeAt(pline.NumberOfVertices - 2);
_isArcSeg = (blg != 0);
}
}
private void EnsureFlexLinetypeLoaded(Document doc)
{
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
LinetypeTable ltTable = (LinetypeTable)tr.GetObject(db.LinetypeTableId, OpenMode.ForRead);
// Check if "FLEX" linetype is loaded
if (!ltTable.Has("FLEX"))
{
// Determine the path to the linetype file (.lin)
string assemblyLocation = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
string linFilePath = System.IO.Path.Combine(assemblyLocation, @"Linetypes\Flex.lin");
// Load the linetype from the file
try
{
db.LoadLineTypeFile("FLEX", linFilePath);
ed.WriteMessage("\nLoaded 'FLEX' linetype from file.");
}
catch (System.Exception ex)
{
ed.WriteMessage($"\nError loading 'FLEX' linetype: {ex.Message}");
}
}
else
{
ed.WriteMessage("\n'FLEX' linetype is already loaded.");
}
tr.Commit();
}
}
public void MakeFlex()
{
SetPolylineLinetypeToFlex(Application.DocumentManager.MdiActiveDocument, FlexLine);
}
private void SetPolylineLinetypeToFlex(Document doc, Polyline pline)
{
string objLayer;
Database db = doc.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
LinetypeTable ltTable = (LinetypeTable)tr.GetObject(db.LinetypeTableId, OpenMode.ForRead);
// Ensure the "FLEX" linetype is available
EnsureFlexLinetypeLoaded(doc);
// Open the LinetypeTable for read
LinetypeTableRecord ltr = (LinetypeTableRecord)tr.GetObject(ltTable["FLEX"], OpenMode.ForRead);
// Set the polyline's linetype to "FLEX"
pline.LinetypeId = ltr.ObjectId;
using (LayerTable lt = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable)
{
Main main = GlobalVariables.MainWindow;
objLayer = main.Layer.Text;
if (lt.Has(objLayer))
{
pline.Layer = objLayer;
}
}
tr.Commit();
}
}
public void DisposeEntity()
{
this.Entity.Dispose();
}
public void Append()
{
Database db = HostApplicationServices.WorkingDatabase;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord btr = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
btr.AppendEntity(this.Entity);
tr.AddNewlyCreatedDBObject(this.Entity, true);
tr.Commit();
}
}
}