000

Index Labels

Showing Progress for long code execution in AutoCAD

.
More often than not we need to write code to loop through large amount of data set, such as entities in a selection set or even entire ModelSpace/PaperSpace of a huge drawing. This process may take a while to complete. It is a common practice to show a progress bar during this lengthy processing to let user know that AutoCAD is busy processing data.

With AutoCAD .NET API, one can quite easily uses the built-in Autodesk.AutoCAD.Runtime.ProgressMeter object to show progress of lengthy executing process. However, from my experience of using ProgressMeter, it often does not show a satisfactory progressing visual effect. The processing effect shown by the ProgressMeter for exact long processing operation done by the exact code could be different from one AutoCAD version to another version, and in many cases, the progress meter simply does not get refreshed during the lengthy processing.

A few years back, I wrote an article on showing a progress window for a long running process in AutoCAD, where I used a modeless window/dialog box to host a progress bar. The purpose was to separate the actual lengthy operation and the visual progress effect in different component, so that the code for progress bar can be easily reused as a component. Again, it has been proving that using modeless window/dialog box has the same unstable progressing visual effect. That is the progress bar sometimes is not refreshed when AutoCAD is busy of intense processing.

I guess, using either AutoCAD built-in ProgressMeter, or ProgressBar hosted in a modeless window, we, as .NET API programmer, do not have much control on how AutoCAD handles its UI update.

The only way to guarantee the UI that shows promptly updated progress bar is to host the progress bar in a modal window/dialog box.

So, I decided to update/rebuild my progress bar code component by using a modal form. Here I used similar approach to use an Interface separating the actual code that does the lengthy AutoCAD processing and the code to display progressing bar.

Firstly, I define an Interface that will be used by the progress bar component. This Interface object tells the progress bar component when a lengthy processing starts and end, stimulate the progress bar to progress: all of these are done through vents defined in this Interface. The Interface also define an action (method), which the progress bar component calls as soon as the progress bar UI shows. See code below (including custom EventHandler and EventArgs used by those Events:

using System;

namespace ShowProgressBar
{
    public interface ILongProcessingObject
    {
        event LongProcessStarted ProcessingStarted;
        event LongProcessingProgressed ProcessingProgressed;
        event EventHandler ProcessingEnded;
        event EventHandler CloseProgressUIRequested;
        void DoLongProcessingWork();
    }

    public class LongProcessStartedEventArgs : EventArgs
    {
        private int _loopCount;
        private string _description;
        private bool _canStop;

        public LongProcessStartedEventArgs(
            string description, int loopCount = 0, bool canStop = false)
        {
            _loopCount = loopCount;
            _description = description;
            _canStop = canStop;
        }

        public int LoopCount
        {
            get { return _loopCount; }
        }

        public string Description
        {
            get { return _description; }
        }

        public bool CanStop
        {
            get { return _canStop; }
        }
    }

    public class LongProcessingProgressEventArgs : EventArgs
    {
        private string _progressDescription;
        private bool _cancel = false;

        public LongProcessingProgressEventArgs(string progressDescription)
        {
            _progressDescription = progressDescription;
        }

        public string ProgressDescription
        {
            get { return _progressDescription; }
        }

        public bool Cancel
        {
            set { _cancel = value; }
            get { return _cancel; }
        }
    }

    public delegate void LongProcessStarted(
        object sender, LongProcessStartedEventArgs e);

    public delegate void LongProcessingProgressed(
        object sender, LongProcessingProgressEventArgs e);
}

Then here the progress bar component:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShowProgressBar
{
    public class ProcessingProgressBar : IDisposable
    {
        private dlgProgress _dlg = null;
        private ILongProcessingObject _executingObject;

        public ProcessingProgressBar(ILongProcessingObject executingObj)
        {
            _executingObject = executingObj;
        }

        public void Start()
        {
            _dlg = new dlgProgress(_executingObject);
            _dlg.ShowDialog();
        }

        public void Dispose()
        {
            if (_dlg!=null)
            {
                _dlg.Dispose();
            }
        }
    }
}

As the code show, it is really simple with one public method Start(), which shows a modal dialog box where the progress bar is hosted. This component implements IDispose(), so that when it is disposed, the instance of dialog box (Windows Form) is disposed. The most important part of this component is that it holds an instance of ILongProcessingObject class: as soon as the progress bar form shows, the Start() method of this ILongProcessingObject gets called, and its events subscribed by the progress bar form stimulate the progress bar to progress and close the form when processing is done.

The dialog box form has 4 controls on it: 2 labels to show executing process' message/description, a progress bar, and a button labelled a "Stop". Here is the code behind the form:

using System;
using System.Windows.Forms;

namespace ShowProgressBar
{
    public partial class dlgProgress : Form
    {
        private ILongProcessingObject _executingObject = null;
        private bool _stop = false;
        private bool _isMarquee = false;
        private int _loopCount = 0;

        public dlgProgress()
        {
            InitializeComponent();
        }

        public dlgProgress(
            ILongProcessingObject executingObj)
            : this()
        {
            _executingObject = executingObj;

            _executingObject.ProcessingStarted +=
                new LongProcessStarted(ExecutingObject_ProcessStarted);
            _executingObject.ProcessingProgressed +=
                new LongProcessingProgressed(ExecutingObject_Progressed);
            _executingObject.ProcessingEnded +=
                new EventHandler(ExecutingObject_ProcessEnded);
            _executingObject.CloseProgressUIRequested +=
               new EventHandler(ExecutingObject_CloseProgressUIRequested);
        }

        private void ExecutingObject_CloseProgressUIRequested(object sender, EventArgs e)
        {
            this.DialogResult = DialogResult.OK;
        }

        private void ExecutingObject_ProcessEnded(object sender, EventArgs e)
        {
            pBar.Value = 0;
            lblTitle.Text = "";
            lblDescription.Text = "";
            this.Refresh();
        }

        private void ExecutingObject_ProcessStarted(
            object sender, LongProcessStartedEventArgs e)
        {

            if (e.LoopCount == 0)
            {
                pBar.Style = ProgressBarStyle.Marquee;
                lblDescription.Text = "Please wait...";
            }
            else
            {
                pBar.Style = ProgressBarStyle.Continuous;
                pBar.Minimum = 0;
                pBar.Maximum = e.LoopCount;
                pBar.Value = 0;
                lblDescription.Text = "";
                _loopCount = e.LoopCount;
            }

            _isMarquee = e.LoopCount == 0;
            btnStop.Visible = e.CanStop;
            lblTitle.Text = e.Description;
            Application.DoEvents();
            this.Refresh();
        }

        private void ExecutingObject_Progressed(
            object sender, LongProcessingProgressEventArgs e)
        {
            if (!_isMarquee)
            {
                pBar.Value++;
            }
 
            lblDescription.Text = e.ProgressDescription;
            lblDescription.Refresh();

            Application.DoEvents();
            if (_stop)
            {
                e.Cancel = true;
            }
        }

        private void dlgProgress_Shown(object sender, EventArgs e)
        {
            Application.DoEvents();

            if (_executingObject == null)
            {
                this.DialogResult = DialogResult.Cancel;
            }
            else
            {
                _executingObject.DoLongProcessingWork();
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            _stop = true;
        }
    }
}

Sometimes, a long processing may not have determined loop count, or the processing loop only ends with certain condition been met. In the code showing here, I assume if the LongProcessStartedEventArg.LoopCount is 0, then it means the processing loop is unknown, so I set the ProgressBar's Style property to Marquee.

That is all the code for the progress bar component that can be used easily with my AutoCAD data processing objects, as long as they implement ILongProcessingObject Interface. Here is an example class MyCadDataHandler, in which I let it do 2 lengthy tasks:

  • one with known loop count: looping through drawing's ModelSpace for each entity and do something with it
  • one without known loop count: looping through drawing's ModelSpace to find BlockReferences with certain name, and if the count of the found BlockReferences reach a given number the looping stops

Here is he code of class MyCadDataHandler:

using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;

namespace ShowProgressBar
{
    public class MyCadDataHandler : ILongProcessingObject
    {
        private enum LongProcessingType
        {
            Type1=0,
            Type2=1,
        }

        private Document _dwg;
        private LongProcessingType _processingType = LongProcessingType.Type1;

        public MyCadDataHandler(Document dwg)
        {
            _dwg = dwg;
        }

        #region public methods

        public void DoWorkWithKnownLoopCount()
        {
            _processingType = LongProcessingType.Type1;

            using (var progress=new ProcessingProgressBar(this))
            {
                progress.Start();
            }
        }

        public void DoWorkWithUnknownLoopCount()
        {
            _processingType = LongProcessingType.Type2;

            using (var progress = new ProcessingProgressBar(this))
            {
                progress.Start();
            }
        }

        #endregion

        #region Implementing ILongProcessingObject interface

        public event LongProcessStarted ProcessingStarted;

        public event LongProcessingProgressed ProcessingProgressed;

        public event EventHandler ProcessingEnded;

        public event EventHandler CloseProgressUIRequested;

        public void DoLongProcessingWork()
        {
            switch(_processingType)
            {
                case LongProcessingType.Type1:
                    LoopThroughModelSpace();
                    break;
                case LongProcessingType.Type2:
                    SearchForTopBlocks("StationLabel", 500);
                    break;
            }
        }

        #endregion

        #region private methods

        private void LoopThroughModelSpace()
        {
            try
            {
                //run 2 long processing loops
                for (int n = 0; n < 2; n++)
                {
                    using (var tran = 
                        _dwg.TransactionManager.StartTransaction())
                    {
                        //Get all entities' ID in ModelSpace
                        BlockTableRecord model = (BlockTableRecord)
                            tran.GetObject(
                            SymbolUtilityServices.GetBlockModelSpaceId(
                            _dwg.Database), OpenMode.ForRead);

                        ObjectId[] entIds = model.Cast<ObjectId>().ToArray();

                        if (ProcessingStarted != null)
                        {
                            string process = n == 0 ? 
                                "Searching ModelSpace for AAAA" : 
                                "Search ModelSpace for BBBB";
                            LongProcessStartedEventArgs e =
                                new LongProcessStartedEventArgs(
                                    process, entIds.Length, true);

                            ProcessingStarted(this, e);
                        }

                        int count = 0;
                        foreach (var entId in entIds)
                        {
                            count++;

                            if (ProcessingProgressed != null)
                            {
                                string progMsg = string.Format(
                                    "{0} out of {1}. {2} remaining...\n" +
                                    "Processing entity: {3}",
                                    count, 
                                    entIds.Length, 
                                    entIds.Length-count, 
                                    entId.ObjectClass.DxfName);

                                LongProcessingProgressEventArgs e =
                                    new LongProcessingProgressEventArgs(progMsg);
                                ProcessingProgressed(this, e);

                                //Since this processing is cancellable, we
                                //test if user clicked the "Stop" button in the 
                                //progressing dialog box
                                if (e.Cancel) break;
                            }

                            //Do something with the entity
                            Entity ent = (Entity)tran.GetObject(
                                entId, OpenMode.ForRead);
                            long s = 0;
                            for (int i = 0; i < 1000000; i++)
                            {
                                s += i * i;
                            }
                            
                        }

                        if (ProcessingEnded != null)
                        {
                            ProcessingEnded(thisEventArgs.Empty);
                        }

                        tran.Commit();
                    }
                }
            }
            finally
            {
                //Make sure the CloseProgressUIRequested event always fires, so
                //that the progress dialog box gets closed because of this event
                if (CloseProgressUIRequested != null)
                {
                    CloseProgressUIRequested(thisEventArgs.Empty);
                }
            }
        }

        private void SearchForTopBlocks(string blkName, int targetCount)
        {
            List<ObjectId> blkIds = new List<ObjectId>();

            try
            {
                if (ProcessingStarted != null)
                {
                    string msg = string.Format(
                        "Searching first {0} block refeences: \"{1}\"",
                        targetCount, blkName);
                    LongProcessStartedEventArgs e =
                        new LongProcessStartedEventArgs(msg);

                    ProcessingStarted(this, e);
                }

                using (var tran = _dwg.TransactionManager.StartTransaction())
                {
                    //Get all entities' ID in ModelSpace
                    BlockTableRecord model = (BlockTableRecord)tran.GetObject(
                        SymbolUtilityServices.GetBlockModelSpaceId(
                        _dwg.Database), OpenMode.ForRead);

                    foreach (ObjectId id in model)
                    {
                        if (ProcessingProgressed != null)
                        {
                            string progMsg = string.Format(
                                "{0} found\n" +
                                "Processing entity: {1}",
                                blkIds.Count, id.ObjectClass.DxfName);
                            LongProcessingProgressEventArgs e =
                                new LongProcessingProgressEventArgs(progMsg);
                            ProcessingProgressed(this, e);
                        }

                        if (IsTargetBlock(id, blkName, tran))
                        {
                            blkIds.Add(id);
                            if (blkIds.Count == targetCount) break;
                        }
                    }

                    tran.Commit();
                }

                if (ProcessingEnded != null)
                {
                    ProcessingEnded(thisEventArgs.Empty);
                }
            }
            finally
            {
                //Make sure the CloseProgressUIRequested event always fires,
                //so that the progress dialog box gets closed because of 
                //this event
                if (CloseProgressUIRequested != null)
                {
                    CloseProgressUIRequested(thisEventArgs.Empty);
                }
            }
        }

        private bool IsTargetBlock(
            ObjectId entId, string blkName, Transaction tran)
        {
            //kill a bit time to allow progress bar effect
            long s = 0;
            for (int i = 0; i < 10000000; i++)
            {
                s += i * i;
            }

            BlockReference blk = tran.GetObject(
                entId, OpenMode.ForRead) as BlockReference;
            if (blk!=null)
            {
                string name;
                if (blk.IsDynamicBlock)
                {
                    BlockTableRecord br = (BlockTableRecord)
                        tran.GetObject(
                        blk.DynamicBlockTableRecord, OpenMode.ForRead);
                    name = br.Name;
                }
                else
                {
                    name = blk.Name;
                }

                return name.ToUpper() == blkName.ToUpper();
            }

            return false;
        }

        #endregion
    }
}

As the code shows, it has 2 public methods that can be called in a CommandMethod to start the 2 lengthy data processing executions. The third public method DoLongProcessingWork() is the ILongProcessObject interface implementing method and is called when the progress bar hosting dialog box shows.

When I implementing the ILongProcessingObject interface, I have freedom to decide what exactly I want to do when ILongProcessObject.DoLongProcessWork() is called. In the code shown here, I used private enum type LongProcessingType to indicate what long processing operation to be executed. I also has the freedom (and am responsible) to decide when to raise events that stimulate progress bar and close the progress bar form.

Here is the CommandClass that uses MyCadDataHandler:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assemblyCommandClass(typeof(ShowProgressBar.MyCadCommands))]

namespace ShowProgressBar
{
    public class MyCadCommands
    {
        [CommandMethod("LongWork1")]
        public static void RunLongWork_1()
        {
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            try
            {
                MyCadDataHandler dataHandler = new MyCadDataHandler(dwg);
                dataHandler.DoWorkWithKnownLoopCount();
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage(
                    "\nError: {0}\n{1}", ex.Message, ex.StackTrace);
            }
            finally
            {
                Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
            }
        }

        [CommandMethod("LongWork2")]
        public static void RunLongWork_2()
        {
            Document dwg = CadApp.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            try
            {
                MyCadDataHandler dataHandler = new MyCadDataHandler(dwg);
                dataHandler.DoWorkWithUnknownLoopCount();
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage(
                    "\nError: {0}\n{1}", ex.Message, ex.StackTrace);
            }
            finally
            {
                Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
            }
        }
    }
}


This video clip show how the code works (I used a drawing with over 9000 entities in ModelSpace).

As I stated at the beginning, I have used AutoCAD built-in ProgressMeter, used Windows.Forms.ProgressBar hosed in both modeless and modal form. So far, using modal form to host a ProgressBar has the most reliable progressing visual effect (I'd guess it would be the same with WPF modeless and modal window, but I have not tried). However, as we all know, forced UI update slows down the actual processing. So, better progressing visual effect with modal form means longer processing time. The bottom line is that psychologically, user feel things going faster with visual progressing indicator when he/she waits 10 seconds than 5 seconds while staring at frozen AutoCAD screen.

The source code can be downloaded here.

Blog Archive

Labels

.NET Programming 2D Drafting 3D Animation 3D Art 3D Artist 3D design 3D effects 3D Engineering 3D Materials 3D Modeling 3D models 3D presentation 3D Printing 3D rendering 3D scanning 3D scene 3D simulation 3D Sketch Inventor 3D Texturing 3D visualization 3D Web App 3ds Max 4D Simulation ACC Adaptive Clearing adaptive components Add-in Development Additive Manufacturing Advanced CAD features Advanced Modeling AEC Technology AEC Tools affordable Autodesk tools AI AI animation AI Assistance AI collaboration AI Design AI Design Tools AI Experts AI for Revit AI Guide AI in CAD AI in CNC AI in design AI in engineering AI in Manufacturing AI in Revit AI insights AI lighting AI rigging AI Tips AI Tools AI troubleshooting AI workflow AI-assisted AI-assisted rendering AI-Assisted Workflow AI-enhanced Animation animation pipeline animation tips Animation workflow annotation Annotation Scaling AR architectural design architectural modeling architectural preservation architectural visualization Architecture architecture design Architecture Engineering Architecture Firm Architecture Productivity architecture software architecture technology Architecture Workflow Arnold Renderer Arnold Shader Artificial Intelligence As-Built Model Asset Management augmented reality AutoCAD AutoCAD advice AutoCAD API AutoCAD Basics AutoCAD Beginner AutoCAD Beginners AutoCAD Civil 3D AutoCAD Civil3D AutoCAD commands AutoCAD efficiency AutoCAD Expert Advice AutoCAD features AutoCAD File Management AutoCAD Layer AutoCAD Layers AutoCAD learning AutoCAD print settings AutoCAD productivity AutoCAD Teaching AutoCAD Techniques AutoCAD tips AutoCAD tools AutoCAD training. AutoCAD tricks AutoCAD Tutorial AutoCAD workflow AutoCAD Xref Autodesk Autodesk 2025 Autodesk 2026 Autodesk 3ds Max Autodesk AI Autodesk AI Tools Autodesk Alias Autodesk AutoCAD Autodesk BIM Autodesk BIM 360 Autodesk Certification Autodesk Civil 3D Autodesk Cloud Autodesk community forums Autodesk Construction Cloud Autodesk Docs Autodesk Dynamo Autodesk features Autodesk for Education Autodesk Forge Autodesk FormIt Autodesk Fusion Autodesk Fusion 360 Autodesk help Autodesk InfraWorks Autodesk Inventor Autodesk Inventor Frame Generator Autodesk Inventor iLogic Autodesk Knowledge Network Autodesk License Autodesk Maya Autodesk mistakes Autodesk Navisworks Autodesk news Autodesk plugins Autodesk productivity Autodesk Recap Autodesk resources Autodesk Revit Autodesk Software Autodesk support ecosystem Autodesk Takeoff Autodesk Tips Autodesk training Autodesk tutorials Autodesk update Autodesk Upgrade Autodesk Vault Autodesk Video Autodesk Viewer Automated Design Automation Automation Tutorial automotive design automotive visualization Backup Basic Commands Basics Batch Plot Beginner Beginner Tips beginner tutorial beginners guide Big Data BIM BIM 360 BIM Challenges BIM collaboration BIM Compliance BIM Coordination BIM Data BIM Design BIM Efficiency BIM for Infrastructure BIM Implementation BIM Library BIM Management BIM modeling BIM software BIM Standards BIM technology BIM tools BIM Trends BIM workflow Block Editor Block Management Block Organization Building Design Software Building Efficiency Building Maintenance building modeling Building Systems Building Technology ByLayer CAD CAD API CAD assembly CAD Automation CAD Blocks CAD CAM CAD commands CAD comparison CAD Customization CAD Data Management CAD Design CAD errors CAD Evolution CAD File Size Reduction CAD Integration CAD Learning CAD line thickness CAD management CAD Migration CAD mistakes CAD modeling CAD Optimization CAD plugins CAD Productivity CAD Rendering CAD Security CAD Skills CAD software CAD software 2026 CAD software training CAD standards CAD technology CAD Tips CAD Tools CAD tricks CAD Tutorial CAD workflow CAM CAM strategies car design software Case Study CEO Guide CGI design Character Rig cinematic lighting Civil 3D Civil 3D hidden gems Civil 3D productivity Civil 3D tips civil design software civil engineering Civil engineering software Clash Detection Class-A surfacing clean CAD file cleaning command client engagement Cloud CAD Cloud Collaboration Cloud design platform Cloud Engineering Cloud Management Cloud Storage Cloud-First CNC CNC machining collaboration command abbreviations Complex Renovation concept car conceptual workflow Connected Design construction Construction Analytics Construction Automation Construction BIM Construction Cloud Construction Planning Construction Scheduling Construction Technology contractor tools Contractor Workflow Contraints corridor design Cost Effective Design cost estimation Create resizable blocks Creative Teams CTB STB Custom visual styles Cutting Parameters Cybersecurity Data Backup data management Data Protection Data Reference Data Security Data Shortcut Design Automation Design Career Design Collaboration Design Comparison Design Coordination design efficiency Design Engineering Design Hacks Design Innovation design optimization Design Options design productivity design review Design Rules design software design software tips Design Technology design tips Design Tools Design Workflow design-to-construction Designer Designer Tools Digital Art Digital Assets Digital Construction Digital Construction Technology Digital Content Digital Design Digital engineering digital fabrication Digital Manufacturing digital marketing digital takeoff Digital Thread Digital Tools Digital Transformation Digital Twin Digital Twins digital workflow dimension dimensioning Disaster Recovery drafting drafting automation Drafting Efficiency Drafting Shortcuts Drafting Standards Drafting Tips Drawing Drawing Automation drawing tips Dref Dynamic Block Dynamic Block AutoCAD Dynamic Blocks Dynamic doors Dynamic windows Dynamics Dynamo Dynamo automation early stage design eco design editing commands Electrical Systems Emerging Features Energy Analysis energy efficiency Energy Simulation Engineering Engineering Automation engineering data Engineering Design Engineering Innovation Engineering Productivity Engineering Skills engineering software Engineering Technology engineering tools Engineering Tools 2025 Engineering Workflow Excel Export Workflow Express Tools External Reference facial animation Facial Rigging Facility Management Families Fast Structural Design Field Documentation File Optimization File Recovery Fire Flame flange tips flat pattern Fluid Effects Fluid Simulation Forge Development Forge Viewer FreeCAD Fusion 360 Fusion 360 API Fusion 360 tutorial Future of Design Future Skills Game Development Gamification Generative Design Geospatial Data GIS Global design teams global illumination GPU Acceleration grading optimization Green Architecture green building Green Technology Grips Handoff HDRI health check Healthcare Facilities heavy CAD file Heavy CAD Files heritage building conservation hidden commands Hospital Design HVAC HVAC Design Tools HVAC Engineering HVAC Optimization Hydraulic Modeling IK/FK iLogic Import Workflow Industry 4.0 Infrastructure infrastructure design Infrastructure Monitoring Infrastructure Planning Infrastructure Technology InfraWorks innovation Insight intelligent modeling Interactive Design interactive presentation Interior Design Inventor Inventor API Inventor Drawing Template Inventor Frame Generator Inventor Graphics Issues Inventor IDW Inventor Tips Inventor Tutorial IoT ISO 19650 joints Keyboard Shortcuts keyframe animation Keyframe generation Landscape Design Large Projects Laser Scan Layer Management Layer Organization Learn AutoCAD Legacy CAD Licensing light techniques Lighting and shading Lighting Techniques Linked Models Liquid Machine Learning Machine Learning in CAD Machine Optimization Machining Efficiency machining productivity maintenance command Management manufacturing Manufacturing Innovation Manufacturing Technology Mapping Technology marketing visuals Material Creation Maya Maya character animation Maya lighting Maya Shader Maya Tips Maya tutorial measurement Mechanical Design Mechanical Engineering Media & Entertainment MEP MEP Modeling Mesh-to-BIM Metal Structure modal analysis Model Management Model Optimization Modeling Secrets Modular Housing Motion capture motion graphics motion simulation MotionBuilder Multi Office Workflow multi-axis machining Multi-User Environment multileader Navisworks Navisworks Best Practices Net Zero Design ObjectARX .NET API Open Source CAD Organization OVERKILL OVERKILL AutoCAD Page Setup Palette Parametric Components parametric design parametric family Parametric Modeling particle effects particle systems PDF PDM system Personal Brand Phasing PlanGrid Plot Settings Plot Style Plot Style AutoCAD Plotting Plugin Tutorial Plumbing Design point cloud Portfolio Post Construction Post-Processing Practice Drawing precision machining preconstruction workflow predictive analysis predictive animation Predictive Maintenance Predictive rigging Prefabrication Presentation-ready visuals Printing Printing Quality Problem Solving Procedural animation procedural motion Procedural Rig Procedural Textures Product Design Product Development product lifecycle product rendering Productivity productivity tools Professional 3D design Professional CAD Professional Drawings professional printing Professional Tips Project Documentation project efficiency project management Project Management Tools Project Visualization PTC Creo PURGE PURGE AutoCAD Rail Transit Rapid Prototyping realistic rendering ReCap Redshift Shader reduce CAD file size Render Render Passes Render Quality Render Settings Rendering rendering engine Rendering Engines Rendering Optimization rendering software Rendering Tips Rendering Workflow RenderMan Renewable Energy Renovation Project Renovation Workflow Reports Resizable Block restoration workflow Revit Revit add-ins Revit API Revit automation Revit Best Practices Revit Collaboration Revit Documentation Revit Family Revit integration Revit MEP Revit Performance Revit Phasing Revit Plugins Revit Scripting Revit skills Revit Standards Revit Template Revit Tips Revit tutorial Revit Workflow Ribbon Rigging Rigid Body robotics ROI Scale Autodesk Schedules screen Sculpting Secure Collaboration Sensor Data Shader Networks Sheet Metal Design Sheet Metal Tricks Sheet Set Manager shortcut keys Shortcuts Siemens NX Simulation simulation tools Sketch Sketching Tricks Small Firms Smart Architecture Smart Block Smart Building Design Smart City Smart Design smart dimensioning Smart Engineering Smart Factory Smart Infrastructur Smoke Soft Body Software Compliance software ecosystem Software Management Software Trends software troubleshooting Software Update Solar Energy Solar Panels SolidWorks Startup Design static stress Steel Structure Design Structural Optimization subscription model Subscription Value surface finish Surface Modeling sustainability sustainable design Sustainable Manufacturing system performance T-Spline team training guide Technical Drawing technical support Template Setup text style Texture Mapping Texturing thermal analysis Time Management time saving tools Title Blocks toolbar toolpath Toolpath Optimization Toolpaths Topography Troubleshooting Tutorial Tutorials urban planning User Interface (UI) UV Mapping UV Unwrap V-Ray Vault Best Practices Vault Lifecycle Vault Mistakes Vector Plotting vehicle modeling VFX Viewport configuration Virtual Environments virtual reality visual effects visualization workflow VR VR Tools VRED Water Infrastructure Water Management Weight Painting What’s New in Autodesk Wind Energy Wind Turbines Workbook workflow Workflow Automation workflow efficiency Workflow Optimization Workflow Tips Worksets Worksharing Workspace XLS Xref Xrefs เขียนแบบ