Wat we bouwen
Typ BOUTGATEN in AutoCAD, kies een middelpunt, voer een steekcirkeldiameter, het aantal gaten en de gatdiameter in — en het commando tekent het volledige patroon: elk gat op de juiste positie, de steekcirkel als hartlijn, en een kruismarkering in het midden. Precies wat je handmatig zou tekenen op een flens, lagerbehuizing of voetplaat, gedaan in seconden.
Dit is een realistische eerste add-in. Het behandelt alles wat je nodig hebt in 90% van het AutoCAD-automatiseringswerk: de gebruiker om invoer vragen, lagen programmatisch aanmaken, de geometrieberekeningen uitvoeren en entiteiten naar de tekening schrijven.
Vereisten
- Visual Studio 2019 of 2022 (Community is gratis)
- AutoCAD 2020 of later geïnstalleerd
- Het lijntype CENTER geladen in AutoCAD (standaard meegeleverd met elke installatie)
- Basiskennis C# — classes, methoden, lussen
Heb je je project en referenties nog niet ingesteld, volg dan eerst de eerste helft van onze installatiegids en kom daarna terug.
Het eindresultaat
Voor de code: dit is precies wat het commando produceert voor een 6-gatenpatroon, steekcirkeldiameter 100 mm, gatdiameter 8 mm:
- Een cirkel met diameter 100 op laag CENTRE-LINE (rood, lijntype CENTER)
- Zes cirkels met diameter 8, gelijkmatig verdeeld, op laag OBJECT (wit)
- Een klein kruis op het hoofdmiddelpunt op laag CENTRE-LINE
- Een kort hartlijnkruis in elk gat
Elk element landt automatisch op de juiste laag — geen handmatig laagwisselen nodig.
Volledige code
Maak een nieuw class-bestand aan in je project en plak dit erin:
using System;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
[assembly: CommandClass(typeof(KleverCAD.BoltHoleCommand))]
namespace KleverCAD
{
public class BoltHoleCommand
{
// ── Laagnamen ────────────────────────────────────────────────────────
// Als constanten opslaan: een typfout wordt opgemerkt door de compiler,
// niet als een stille verkeerde-laag-fout bij uitvoering.
private const string LayerObject = "OBJECT";
private const string LayerCentreLine = "CENTRE-LINE";
/// <summary>
/// BOUTGATEN-commando — tekent een boutgatenpatroon op basis van gebruikersinvoer.
///
/// Werkwijze:
/// 1. Gebruiker kiest het middelpunt
/// 2. Gebruiker voert de steekcirkeldiameter in
/// 3. Gebruiker voert het aantal gaten in
/// 4. Gebruiker voert de gatdiameter in
/// 5. Commando tekent het patroon en rapporteert terug
///
/// Escape indrukken bij een prompt annuleert netjes — er wordt niets getekend.
/// </summary>
[CommandMethod("BOUTGATEN")]
public void DrawBoltHoles()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// ── Stap 1: middelpunt ───────────────────────────────────────────
PromptPointOptions centerOpt = new PromptPointOptions(
"\nKies het middelpunt van het boutgatenpatroon: ");
PromptPointResult centerRes = ed.GetPoint(centerOpt);
if (centerRes.Status != PromptStatus.OK) return;
Point3d center = centerRes.Value;
// ── Stap 2: steekcirkeldiameter ──────────────────────────────────
PromptDistanceOptions pcdOpt = new PromptDistanceOptions(
"\nSteekcirkeldiameter (SCD): ");
pcdOpt.BasePoint = center;
pcdOpt.UseBasePoint = true;
pcdOpt.AllowNegative = false;
pcdOpt.AllowZero = false;
PromptDoubleResult pcdRes = ed.GetDistance(pcdOpt);
if (pcdRes.Status != PromptStatus.OK) return;
double pcdRadius = pcdRes.Value;
double pcd = pcdRadius * 2;
// ── Stap 3: aantal gaten ─────────────────────────────────────────
PromptIntegerOptions countOpt = new PromptIntegerOptions(
"\nAantal gaten: ");
countOpt.AllowNegative = false;
countOpt.AllowZero = false;
countOpt.LowerLimit = 2;
PromptIntegerResult countRes = ed.GetInteger(countOpt);
if (countRes.Status != PromptStatus.OK) return;
int numHoles = countRes.Value;
// ── Stap 4: gatdiameter ──────────────────────────────────────────
PromptDistanceOptions holeOpt = new PromptDistanceOptions(
"\nGatdiameter: ");
holeOpt.AllowNegative = false;
holeOpt.AllowZero = false;
PromptDoubleResult holeRes = ed.GetDistance(holeOpt);
if (holeRes.Status != PromptStatus.OK) return;
double holeDiameter = holeRes.Value;
double holeRadius = holeDiameter / 2.0;
// ── Stap 5: alles tekenen in één transactie ──────────────────────
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// Zorg dat de lagen bestaan voor we erop tekenen.
EnsureLayer(tr, db, LayerObject, colorIndex: 7, linetype: "Continuous");
EnsureLayer(tr, db, LayerCentreLine, colorIndex: 1, linetype: "CENTER");
BlockTableRecord modelSpace = GetModelSpace(tr, db);
// Steekcirkel (hartlijn)
DrawCircle(tr, modelSpace, center, pcdRadius, LayerCentreLine);
// Kruismarkering in het midden
DrawCentreCross(tr, modelSpace, center, pcdRadius * 0.08, LayerCentreLine);
// Gaten gelijkmatig verdelen over de steekcirkel
double angleStep = (2 * Math.PI) / numHoles;
double startAngle = Math.PI / 2.0; // start bovenaan (90°)
for (int i = 0; i < numHoles; i++)
{
double angle = startAngle + (i * angleStep);
// Poolcoördinaten (hoek + straal) omzetten naar cartesisch (x, y)
double x = center.X + pcdRadius * Math.Cos(angle);
double y = center.Y + pcdRadius * Math.Sin(angle);
Point3d holeCenter = new Point3d(x, y, center.Z);
DrawCircle(tr, modelSpace, holeCenter, holeRadius, LayerObject);
DrawCentreCross(tr, modelSpace, holeCenter, holeRadius * 0.4, LayerCentreLine);
}
tr.Commit(); // pas hier wordt alles opgeslagen in de tekening
}
ed.WriteMessage(
$"\nBoutgatenpatroon getekend: {numHoles}x Ø{holeDiameter:F1} op SCD Ø{pcd:F1}");
}
// ── Hulpmethode: cirkel tekenen ──────────────────────────────────────
private static void DrawCircle(
Transaction tr, BlockTableRecord btr,
Point3d center, double radius, string layer)
{
Circle c = new Circle();
c.Center = center;
c.Radius = radius;
c.Layer = layer;
btr.AppendEntity(c);
tr.AddNewlyCreatedDBObject(c, true);
}
// ── Hulpmethode: kruismarkering tekenen (twee korte lijnen) ──────────
private static void DrawCentreCross(
Transaction tr, BlockTableRecord btr,
Point3d center, double halfSize, string layer)
{
Line h = new Line(
new Point3d(center.X - halfSize, center.Y, center.Z),
new Point3d(center.X + halfSize, center.Y, center.Z));
h.Layer = layer;
btr.AppendEntity(h);
tr.AddNewlyCreatedDBObject(h, true);
Line v = new Line(
new Point3d(center.X, center.Y - halfSize, center.Z),
new Point3d(center.X, center.Y + halfSize, center.Z));
v.Layer = layer;
btr.AppendEntity(v);
tr.AddNewlyCreatedDBObject(v, true);
}
// ── Hulpmethode: laag aanmaken als die nog niet bestaat ──────────────
private static void EnsureLayer(
Transaction tr, Database db,
string name, short colorIndex, string linetype)
{
LayerTable lt = (LayerTable)tr.GetObject(
db.LayerTableId, OpenMode.ForRead);
if (lt.Has(name))
return; // laag bestaat al — niets te doen
lt.UpgradeOpen();
LayerTableRecord ltr = new LayerTableRecord();
ltr.Name = name;
ltr.Color = Color.FromColorIndex(ColorMethod.ByAci, colorIndex);
LinetypeTable ltt = (LinetypeTable)tr.GetObject(
db.LinetypeTableId, OpenMode.ForRead);
if (ltt.Has(linetype))
ltr.LinetypeObjectId = ltt[linetype];
lt.Add(ltr);
tr.AddNewlyCreatedDBObject(ltr, true);
}
// ── Hulpmethode: modelruimte ophalen ─────────────────────────────────
private static BlockTableRecord GetModelSpace(Transaction tr, Database db)
{
BlockTable bt = (BlockTable)tr.GetObject(
db.BlockTableId, OpenMode.ForRead);
return (BlockTableRecord)tr.GetObject(
bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
}
}
}
Code-uitleg — regel voor regel
De using-statements
using Autodesk.AutoCAD.ApplicationServices; // Application, Document
using Autodesk.AutoCAD.Colors; // Color (voor laagkleuren)
using Autodesk.AutoCAD.DatabaseServices; // Database, Transaction, Circle, Line, LayerTable...
using Autodesk.AutoCAD.EditorInput; // Editor, PromptPointOptions, PromptDistanceOptions...
using Autodesk.AutoCAD.Geometry; // Point3d, Vector3d
using Autodesk.AutoCAD.Runtime; // CommandMethod-attribuut
Elke using-regel importeert een namespace uit de AutoCAD .NET API. DatabaseServices bevat alle tekeningobjecten, EditorInput alles wat te maken heeft met de opdrachtregel en gebruikersinvoer, en Geometry alle wiskundetypen (punten, vectoren).
Het commando registreren
[assembly: CommandClass(typeof(KleverCAD.BoltHoleCommand))]
Deze regel vertelt AutoCAD: "zoek in BoltHoleCommand naar methoden met [CommandMethod]". Zonder deze regel vindt AutoCAD je commando niet, ook al is de DLL geladen. Hij staat buiten de namespace, bovenaan het bestand.
Laagnaamconstanten
private const string LayerObject = "OBJECT";
private const string LayerCentreLine = "CENTRE-LINE";
Laagnamen als constanten opslaan zorgt ervoor dat een typfout door de compiler wordt opgemerkt in plaats van stilzwijgend op de verkeerde laag te tekenen. Wijzig de naam op één plek en alle verwijzingen worden automatisch bijgewerkt.
De drie objecten die je altijd nodig hebt
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
doc is het huidige open tekeningbestand. db is de database van de tekening — alle entiteiten (lijnen, cirkels, lagen) leven hier. ed is de opdrachtregel en gebruikersinvoer. Deze drie regels staan aan het begin van vrijwel elk AutoCAD-commando dat je ooit schrijft.
Vragen om een punt
PromptPointOptions centerOpt = new PromptPointOptions("\nKies het middelpunt: ");
PromptPointResult centerRes = ed.GetPoint(centerOpt);
if (centerRes.Status != PromptStatus.OK) return;
Point3d center = centerRes.Value;
De \n aan het begin zorgt voor een nieuwe regel in het opdrachtvenster. ed.GetPoint() pauzeert het commando en wacht tot de gebruiker klikt of coördinaten intypt. Als de status niet OK is (bijv. Escape ingedrukt), keren we direct terug — er is nog niets getekend, dus niets om ongedaan te maken.
Vragen om een afstand met rubber-band lijn
pcdOpt.BasePoint = center;
pcdOpt.UseBasePoint = true;
Door BasePoint en UseBasePoint = true in te stellen tekent AutoCAD een rubber-band lijn van het middelpunt terwijl de gebruiker de muis beweegt — dezelfde visuele terugkoppeling als bij het tekenen van een cirkel. AllowNegative en AllowZero voorkomen ongeldige invoer.
Vragen om een geheel getal met limiet
countOpt.LowerLimit = 2;
LowerLimit = 2 zorgt ervoor dat AutoCAD zelf alles onder 2 afwijst en een foutmelding toont — je hoeft die validatie niet zelf te schrijven. De API handelt het af vóór het resultaat je code bereikt.
Een transactie openen
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// ... alle tekenopdrachten hier ...
tr.Commit();
}
Een transactie is een beschermende envelop om al je tekenbewerkingen. Niets wordt permanent naar de tekening geschreven totdat tr.Commit() wordt aangeroepen. Als er een uitzondering optreedt vóór de commit, wordt de transactie automatisch teruggedraaid en blijft de tekening onaangeroerd — net als een SQL-transactie: alles of niets.
Lagen aanmaken met EnsureLayer
EnsureLayer(tr, db, LayerObject, colorIndex: 7, linetype: "Continuous");
EnsureLayer(tr, db, LayerCentreLine, colorIndex: 1, linetype: "CENTER");
De hulpmethode controleert eerst of de laag al bestaat. Als de gebruiker BOUTGATEN tweemaal uitvoert, zijn de lagen er al — geen fout, geen duplicaat. Kleurindex 1 = rood, 7 = wit. Het lijntype CENTER geeft de steekcirkel zijn streeplijn-uiterlijk.
De boutgaten-lus
double angleStep = (2 * Math.PI) / numHoles;
double startAngle = Math.PI / 2.0;
for (int i = 0; i < numHoles; i++)
{
double angle = startAngle + (i * angleStep);
double x = center.X + pcdRadius * Math.Cos(angle);
double y = center.Y + pcdRadius * Math.Sin(angle);
...
}
2 * Math.PI is een volledige cirkel in radialen (360°). Delen door het aantal gaten geeft de hoek tussen elk gat. Math.Cos en Math.Sin zetten de hoek om naar X- en Y-coördinaten — standaard poolcoördinaten-naar-cartesisch. Beginnen bij 90° (boven) is een conventie in het mechanisch tekenen.
Een entiteit toevoegen aan de tekening
Circle c = new Circle();
c.Center = center;
c.Radius = radius;
c.Layer = layer;
btr.AppendEntity(c);
tr.AddNewlyCreatedDBObject(c, true);
Een Circle-object aanmaken in C# voegt hem nog niet toe aan de tekening. btr.AppendEntity(c) voegt hem toe aan de modelruimte. tr.AddNewlyCreatedDBObject(c, true) registreert hem bij de transactie. Beide regels zijn altijd verplicht — sla er één over en de entiteit verschijnt niet of lekt geheugen.
Hoe de wiskunde werkt
double x = center.X + pcdRadius * Math.Cos(angle);
double y = center.Y + pcdRadius * Math.Sin(angle);
Dit converteert poolcoördinaten — een afstand van het middelpunt (pcdRadius) en een hoek — naar de cartesische X/Y-coördinaten die AutoCAD nodig heeft. Voor een 6-gatenpatroon zijn de hoeken 90°, 150°, 210°, 270°, 330° en 30° — gelijkmatig op 60° afstand. De formule werkt voor elk aantal gaten.
Probeer het uit te breiden
- Vraag om een starthoek — voeg een
GetAngle()-prompt toe zodat de gebruiker het patroon kan roteren - Voeg een MText-label toe — schrijf "6x Ø8 OP SCD Ø100" als tekst-entiteit onder het patroon
- Ondersteuning voor lijntype HIDDEN — voor gaten in een verborgen-lijnaanzicht
- Lees uit een tabel — laad standaard boutmaten uit een Excel- of CSV-bestand zodat de gebruiker M6, M8, M10 kiest in plaats van een diameter in te typen
Volgende stap in deze cursus
Volgende les: werken met blokken en attributen — een titelblok invoegen, attribuutvelden (tekeningnummer, revisie, schaal) vanuit code invullen en exporteren naar een spreadsheet.
De volledige C# voor AutoCAD-cursus is beschikbaar voor Standard- en Full Access-abonnees in de E-Learning-sectie.
Comments
No comments yet — be the first!
Leave a comment
Sign in to skip moderation — anonymous comments are reviewed before appearing.