Skip to content

Annotation อัตโนมัติ (Tag, Dimension & Text)

Annotation คือชั้นสุดท้ายของ BIM Documentation — การเพิ่ม Tag, Dimension และข้อความลงในแบบ ซึ่งโดยปกติต้องใช้เวลาหลายชั่วโมงถ้าทำมือ แต่ด้วย Revit API เราสามารถสร้าง Annotation ทั้งแบบได้ในไม่กี่วินาทีครับ!


1. TextNote — สร้างข้อความบนแบบ

Section titled “1. TextNote — สร้างข้อความบนแบบ”

TextNote คือกล่องข้อความที่วางอยู่บน View ใน Revit ใช้สำหรับหมายเหตุ, ชื่อแบบ, หรือคำอธิบายเพิ่มเติม

Command.cs — สร้าง TextNote บน View
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using System.Linq;
namespace RevitToolkit;
[Transaction(TransactionMode.Manual)]
public class CreateTextNoteCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIDocument uidoc = commandData.Application.ActiveUIDocument;
Document doc = uidoc.Document;
View activeView = uidoc.ActiveView;
// 1. ค้นหา TextNoteType (รูปแบบ/ขนาดตัวอักษร)
TextNoteType noteType = new FilteredElementCollector(doc)
.OfClass(typeof(TextNoteType))
.Cast<TextNoteType>()
.FirstOrDefault(); // ใช้ TextNoteType แรกที่หาเจอ
if (noteType == null)
{
TaskDialog.Show("Error", "ไม่พบ TextNoteType ในโปรเจ็กต์");
return Result.Failed;
}
using (Transaction trans = new Transaction(doc, "Create Text Notes"))
{
trans.Start();
// 2. กำหนดตำแหน่งและข้อความ
XYZ position = new XYZ(0, 0, 0); // พิกัดบน View (หน่วย: ฟุต)
// 3. สร้าง TextNoteOptions สำหรับกำหนดรูปแบบ
TextNoteOptions options = new TextNoteOptions(noteType.Id)
{
HorizontalAlignment = HorizontalTextAlignment.Left,
VerticalAlignment = VerticalTextAlignment.Top,
Rotation = 0.0, // มุมหมุน (Radian)
};
// 4. สร้าง TextNote (วางบน View ที่ระบุ)
TextNote note = TextNote.Create(
doc,
activeView.Id,
position,
"หมายเหตุ: ตรวจสอบขนาดเสาตามที่ระบุในแบบรายการ\n(โดยวิศวกรโครงสร้าง)",
options
);
trans.Commit();
TaskDialog.Show("สำเร็จ", $"สร้าง TextNote สำเร็จ ID: {note.Id.Value}");
}
return Result.Succeeded;
}
}

🔍 API ที่ต้องรู้ใน TextNote

Section titled “🔍 API ที่ต้องรู้ใน TextNote”
Propertyคำอธิบาย
note.Textอ่าน/แก้ไขข้อความ
note.Widthความกว้างกล่องข้อความ (ฟุต)
note.GetTextNoteType()ดึง TextNoteType ปัจจุบัน
TextNoteType.TextSizeขนาดตัวอักษร

2. IndependentTag — Tag วัตถุอัตโนมัติ

Section titled “2. IndependentTag — Tag วัตถุอัตโนมัติ”

IndependentTag คือ Tag ที่โชว์ค่าพารามิเตอร์ของวัตถุโดยอัตโนมัติ (เช่น Mark, Type, Size) เมื่อสั่ง Tag แล้วค่าจะอัปเดตตามข้อมูลจริงในโมเดลตลอดเวลา

AutoTagColumnsCommand.cs — Tag เสาทั้งหมดอัตโนมัติ
using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace RevitToolkit;
[Transaction(TransactionMode.Manual)]
public class AutoTagColumnsCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIDocument uidoc = commandData.Application.ActiveUIDocument;
Document doc = uidoc.Document;
View activeView = uidoc.ActiveView;
// 1. ค้นหา TagType สำหรับเสาโครงสร้าง
// FamilySymbol ของ Tag อยู่ใน OST_StructuralColumnTags
FamilySymbol columnTagType = new FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_StructuralColumnTags)
.WhereElementIsElementType()
.Cast<FamilySymbol>()
.FirstOrDefault();
if (columnTagType == null)
{
TaskDialog.Show("Error",
"ไม่พบ Column Tag Type ในโปรเจ็กต์\n" +
"กรุณาโหลด Structural Column Tag Family ก่อน"
);
return Result.Failed;
}
// 2. ดึงเสาทั้งหมดที่มองเห็นใน View ปัจจุบัน
// ใช้ FilteredElementCollector(doc, viewId) เพื่อกรองเฉพาะที่มองเห็น
var visibleColumns = new FilteredElementCollector(doc, activeView.Id)
.OfCategory(BuiltInCategory.OST_StructuralColumns)
.WhereElementIsNotElementType()
.ToElements();
if (!visibleColumns.Any())
{
TaskDialog.Show("ผลลัพธ์", "ไม่พบเสาที่มองเห็นใน View ปัจจุบัน");
return Result.Succeeded;
}
using (Transaction trans = new Transaction(doc, "Auto Tag Structural Columns"))
{
trans.Start();
int tagCount = 0;
foreach (Element column in visibleColumns)
{
// คำนวณตำแหน่งสำหรับ Tag (วางที่พิกัดเสา + เยื้องขึ้นไปเล็กน้อย)
XYZ tagOffset = new XYZ(0, 0, 0);
if (column.Location is LocationPoint lp)
{
// เยื้อง Tag ขึ้นด้านบน 0.5 เมตร จากจุดกึ่งกลางเสา
tagOffset = lp.Point + new XYZ(0, 0.5 / 0.3048, 0);
}
// สร้าง Reference ชี้ไปที่ Element ที่ต้องการ Tag
Reference columnRef = new Reference(column);
// สร้าง IndependentTag
// addLeader = false: ไม่ต้องมีเส้นชี้
IndependentTag tag = IndependentTag.Create(
doc,
columnTagType.Id,
activeView.Id,
columnRef,
addLeader: false,
TagOrientation.Horizontal,
tagOffset
);
tagCount++;
}
trans.Commit();
TaskDialog.Show("สำเร็จ", $"Tag เสาอัตโนมัติสำเร็จ {tagCount} ต้น");
}
return Result.Succeeded;
}
}

3. Dimension — วัดระยะอัตโนมัติ

Section titled “3. Dimension — วัดระยะอัตโนมัติ”

Dimension คือเส้นมาตร (เส้นบอกขนาด) ที่อ้างอิงขอบของวัตถุและแสดงระยะห่าง ซึ่ง Reference ที่ใช้ต้องได้มาจาก Geometry ของวัตถุจริงๆ

AutoDimensionCommand.cs — สร้าง Dimension ระหว่างเสา
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
namespace RevitToolkit;
[Transaction(TransactionMode.Manual)]
public class AutoDimensionCommand : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIDocument uidoc = commandData.Application.ActiveUIDocument;
Document doc = uidoc.Document;
View activeView = uidoc.ActiveView;
// 1. ดึงเสา 2 ต้นแรก (สำหรับสาธิตการวัดระยะระหว่างกัน)
var columns = new FilteredElementCollector(doc, activeView.Id)
.OfCategory(BuiltInCategory.OST_StructuralColumns)
.WhereElementIsNotElementType()
.ToElements()
.Take(2)
.ToList();
if (columns.Count < 2)
{
TaskDialog.Show("Error", "ต้องการเสาอย่างน้อย 2 ต้น");
return Result.Failed;
}
using (Transaction trans = new Transaction(doc, "Auto Dimension Columns"))
{
trans.Start();
// 2. ดึง Reference จากจุดกึ่งกลางของเสาแต่ละต้น
// เพื่อนำไปสร้าง Dimension Line
var referenceArray = new ReferenceArray();
var points = new List<XYZ>();
Options geomOpts = new Options
{
ComputeReferences = true, // สำคัญมาก! ต้องใช้เสมอสำหรับ Dimension
View = activeView
};
foreach (Element col in columns)
{
if (col.Location is not LocationPoint lp) continue;
points.Add(lp.Point);
// สร้าง Reference จากพิกัดจุดกึ่งกลางเสา
GeometryElement geom = col.get_Geometry(geomOpts);
foreach (GeometryObject obj in geom)
{
if (obj is GeometryInstance inst)
{
foreach (GeometryObject instObj in inst.GetInstanceGeometry())
{
if (instObj is Solid solid && solid.Volume > 0)
{
// เพิ่ม Reference จาก Face แรกที่หาเจอ
foreach (Face face in solid.Faces)
{
referenceArray.Append(face.Reference);
break; // ใช้แค่ 1 face ต่อ 1 เสา
}
break;
}
}
break;
}
}
}
if (referenceArray.Size < 2)
{
trans.RollBack();
TaskDialog.Show("Error", "ไม่สามารถสร้าง Reference จากเสาได้");
return Result.Failed;
}
// 3. สร้างเส้น Dimension (แนวนอน วัดระยะในแกน X)
XYZ lineStart = points[0] + new XYZ(0, -5, 0); // เยื้องลงมา 5 ฟุต
XYZ lineEnd = points[1] + new XYZ(0, -5, 0);
Line dimensionLine = Line.CreateBound(lineStart, lineEnd);
// 4. สร้าง Dimension
Dimension dim = doc.Create.NewDimension(activeView, dimensionLine, referenceArray);
trans.Commit();
TaskDialog.Show("สำเร็จ", "สร้าง Dimension ระหว่างเสาสำเร็จ");
}
return Result.Succeeded;
}
}

4. ตัวอย่างจริง: Auto-annotate แบบโครงสร้างครบชุด

Section titled “4. ตัวอย่างจริง: Auto-annotate แบบโครงสร้างครบชุด”
AutoAnnotateSheet.cs — Tag + Dimension ทั้ง Sheet
[Transaction(TransactionMode.Manual)]
public class AutoAnnotateSheet : IExternalCommand
{
public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
{
UIDocument uidoc = commandData.Application.ActiveUIDocument;
Document doc = uidoc.Document;
View view = uidoc.ActiveView;
FamilySymbol columnTagType = new FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_StructuralColumnTags)
.WhereElementIsElementType()
.Cast<FamilySymbol>()
.FirstOrDefault();
var columns = new FilteredElementCollector(doc, view.Id)
.OfCategory(BuiltInCategory.OST_StructuralColumns)
.WhereElementIsNotElementType()
.ToElements();
using (Transaction trans = new Transaction(doc, "Auto-Annotate Structural Plan"))
{
trans.Start();
foreach (Element col in columns)
{
if (col.Location is not LocationPoint lp) continue;
if (columnTagType == null) continue;
// วาง Tag เยื้องขึ้นด้านบน 300mm จากจุดกึ่งกลางเสา
XYZ tagPos = lp.Point + new XYZ(0, 300.0 / 304.8, 0);
IndependentTag.Create(
doc, columnTagType.Id, view.Id,
new Reference(col), false,
TagOrientation.Horizontal, tagPos
);
}
trans.Commit();
}
TaskDialog.Show("เสร็จสิ้น",
$"✅ Annotation อัตโนมัติครบชุด\n" +
$" - Tags สำหรับเสา: {columns.Count} ต้น"
);
return Result.Succeeded;
}
}

วัตถุประสงค์ClassMethod สร้าง
ข้อความหมายเหตุTextNoteTextNote.Create(doc, viewId, pos, text, options)
Tag วัตถุIndependentTagIndependentTag.Create(doc, tagTypeId, viewId, ref, ...)
เส้นมาตรDimensiondoc.Create.NewDimension(view, line, refArray)
ทาสีพื้นที่FilledRegionFilledRegion.Create(doc, typeId, viewId, loops)
ลูกศรชี้DetailCurvedoc.Create.NewDetailCurve(view, curve)