Author Topic: .NET Layout,ViewPort,Plot Routines  (Read 25581 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
.NET Layout,ViewPort,Plot Routines
« on: January 28, 2010, 06:39:35 AM »
LIBRARY THREAD for  AutoCAD Layout,ViewPort,Plot
 Members are encouraged to post any functions, methods, snips regarding
AutoCAD Layout,ViewPort,Plot in .NET : C# ,  VB , F# , Python , etc

Feel free to include comments, descriptive notes, limitations,  and images to document your post.

Please post questions in a regular thread.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

T.Willey

  • Needs a day job
  • Posts: 5251
Re: .NET Layout,ViewPort,Plot Routines
« Reply #1 on: January 28, 2010, 11:01:03 AM »
Here is the link to my batch plot routine that I did in C#.

[ http://www.theswamp.org/index.php?topic=17348.0 ]
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
Re: .NET Layout,ViewPort,Plot Routines
« Reply #2 on: January 31, 2010, 08:15:06 AM »
Code: [Select]
    ' A function to return the current Layout, you can get properties as name and id from the returned object.
    ' Ex: hzGetCurrentLayout.Name
    ' To check if the current layout is Modelspace, use .modeltype as property (true is Model, false is Paper).
    Shared Function hzGetCurrentLayout() As Layout
      '' Get the current document and database, and start a transaction
      Dim acDoc As Document = Application.DocumentManager.MdiActiveDocument
      Dim acCurDb As Database = acDoc.Database
      Dim acLayoutMgr As LayoutManager
      Dim acLayout As Layout

      Using acTrans As Transaction = acCurDb.TransactionManager.StartTransaction()
        ' Reference the Layout Manager
        acLayoutMgr = LayoutManager.Current
        ' Get the current layout
        acLayout = acTrans.GetObject(acLayoutMgr.GetLayoutId(acLayoutMgr.CurrentLayout), OpenMode.ForRead)
        ' Close transaction
        acTrans.Dispose()
      End Using

      Return acLayout

    End Function


It should be possible to get this information also via a variable.
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET Layout,ViewPort,Plot Routines
« Reply #3 on: July 15, 2012, 09:56:43 AM »
Publishing to 3d DWF

<Edit: adding a try/catch block in agreement with the observation of tony>
C#
Code - C#: [Select]
  1. using System.IO;
  2. using System.Text;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.PlottingServices;
  5.  
  6. namespace Plottings
  7. {
  8.     public class PlotTo3dDwf
  9.     {
  10.         private string dwgFile, dwfFile, dsdFile, title, outputDir;
  11.  
  12.         public PlotTo3dDwf()
  13.         {
  14.             outputDir = (string)Application.GetSystemVariable("DWGPREFIX");
  15.             string name = (string)Application.GetSystemVariable("DWGNAME");
  16.             dwgFile = Path.Combine(outputDir, name);
  17.             title = Path.GetFileNameWithoutExtension(name);
  18.             dwfFile = Path.ChangeExtension(dwgFile, "dwf");
  19.             dsdFile = Path.ChangeExtension(dwfFile, ".dsd");
  20.         }
  21.  
  22.         public PlotTo3dDwf(string outputDir)
  23.             : this()
  24.         {
  25.             this.outputDir = outputDir;
  26.         }
  27.  
  28.         public void Publish()
  29.         {
  30.             short bgPlot = (short)Application.GetSystemVariable("BACKGROUNDPLOT");
  31.             Application.SetSystemVariable("BACKGROUNDPLOT", 0);
  32.             try
  33.             {
  34.                 if (!Directory.Exists(outputDir))
  35.                     Directory.CreateDirectory(outputDir);
  36.  
  37.                 using (DsdData dsd = new DsdData())
  38.                 using (DsdEntryCollection dsdEntries = new DsdEntryCollection())
  39.                 {
  40.                     // add the Model layout to the entry collection
  41.                     DsdEntry dsdEntry = new DsdEntry();
  42.                     dsdEntry.DwgName = dwgFile;
  43.                     dsdEntry.Layout = "Model";
  44.                     dsdEntry.Title = title;
  45.                     dsdEntry.Nps = "0";
  46.                     dsdEntries.Add(dsdEntry);
  47.                     dsd.SetDsdEntryCollection(dsdEntries);
  48.  
  49.                     // set DsdData
  50.                     dsd.Dwf3dOptions.PublishWithMaterials = true;
  51.                     dsd.Dwf3dOptions.GroupByXrefHierarchy = true;
  52.                     dsd.SetUnrecognizedData("PwdProtectPublishedDWF", "FALSE");
  53.                     dsd.SetUnrecognizedData("PromptForPwd", "FALSE");
  54.                     dsd.SheetType = SheetType.SingleDwf;
  55.                     dsd.NoOfCopies = 1;
  56.                     dsd.ProjectPath = outputDir;
  57.                     dsd.IsHomogeneous = true;
  58.  
  59.                     if (File.Exists(dsdFile))
  60.                         File.Delete(dsdFile);
  61.  
  62.                     // write the DsdData file
  63.                     dsd.WriteDsd(dsdFile);
  64.  
  65.                     // get the Dsd File contents
  66.                     string str;
  67.                     using (StreamReader sr = new StreamReader(dsdFile, Encoding.Default))
  68.                     {
  69.                         str = sr.ReadToEnd();
  70.                     }
  71.                     // edit the contents
  72.                     str = str.Replace("Has3DDWF=0", "Has3DDWF=1");
  73.                     str = str.Replace("PromptForDwfName=TRUE", "PromptForDwfName=FALSE");
  74.                     // rewrite the Dsd file
  75.                     using (StreamWriter sw = new StreamWriter(dsdFile, false, Encoding.Default))
  76.                     {
  77.                         sw.Write(str);
  78.                     }
  79.  
  80.                     // import the Dsd file new contents in the DsdData
  81.                     dsd.ReadDsd(dsdFile);
  82.  
  83.                     File.Delete(dsdFile);
  84.  
  85.                     PlotConfig pc = PlotConfigManager.SetCurrentConfig("DWF6 ePlot.pc3");
  86.                     Application.Publisher.PublishExecute(dsd, pc);
  87.                 }
  88.             }
  89.             finally
  90.             {
  91.                 Application.SetSystemVariable("BACKGROUNDPLOT", bgPlot);
  92.             }
  93.         }
  94.     }
  95. }
  96.  

VB
Code - vb.net: [Select]
  1. Imports System.IO
  2. Imports System.Text
  3. Imports Autodesk.AutoCAD.ApplicationServices
  4. Imports Autodesk.AutoCAD.PlottingServices
  5.  
  6. Namespace Plottings
  7.     Public Class PlotTo3dDwf
  8.         Private dwgFile, dwfFile, dsdFile, title, outputDir As String
  9.  
  10.         Public Sub New()
  11.             outputDir = DirectCast(Application.GetSystemVariable("DWGPREFIX"), String)
  12.             Dim name As String = DirectCast(Application.GetSystemVariable("DWGNAME"), String)
  13.             dwgFile = Path.Combine(outputDir, name)
  14.             title = Path.GetFileNameWithoutExtension(name)
  15.             dwfFile = Path.ChangeExtension(dwgFile, "dwf")
  16.             dsdFile = Path.ChangeExtension(dwfFile, ".dsd")
  17.         End Sub
  18.  
  19.         Public Sub New(outputDir As String)
  20.             Me.New()
  21.             Me.outputDir = outputDir
  22.         End Sub
  23.  
  24.         Public Sub Publish()
  25.             Dim bgPlot As Short = CShort(Application.GetSystemVariable("BACKGROUNDPLOT"))
  26.             Application.SetSystemVariable("BACKGROUNDPLOT", 0)
  27.             Try
  28.                 If Not Directory.Exists(outputDir) Then
  29.                     Directory.CreateDirectory(outputDir)
  30.                 End If
  31.  
  32.                 Using dsd As New DsdData()
  33.                     Using dsdEntries As New DsdEntryCollection()
  34.                         ' add the Model layout to the entry collection
  35.                         Dim dsdEntry As New DsdEntry()
  36.                         dsdEntry.DwgName = dwgFile
  37.                         dsdEntry.Layout = "Model"
  38.                         dsdEntry.Title = title
  39.                         dsdEntry.Nps = "0"
  40.                         dsdEntries.Add(dsdEntry)
  41.                         dsd.SetDsdEntryCollection(dsdEntries)
  42.  
  43.                         ' set DsdData data
  44.                         dsd.Dwf3dOptions.PublishWithMaterials = True
  45.                         dsd.Dwf3dOptions.GroupByXrefHierarchy = True
  46.                         dsd.SetUnrecognizedData("PwdProtectPublishedDWF", "FALSE")
  47.                         dsd.SetUnrecognizedData("PromptForPwd", "FALSE")
  48.                         dsd.SheetType = SheetType.SingleDwf
  49.                         dsd.NoOfCopies = 1
  50.                         dsd.ProjectPath = outputDir
  51.                         dsd.IsHomogeneous = True
  52.  
  53.                         If File.Exists(dsdFile) Then
  54.                             File.Delete(dsdFile)
  55.                         End If
  56.  
  57.                         ' write the DsdData file
  58.                         dsd.WriteDsd(dsdFile)
  59.  
  60.                         ' get the Dsd File contents
  61.                         Dim str As String
  62.                         Using sr As New StreamReader(dsdFile, Encoding.[Default])
  63.                             str = sr.ReadToEnd()
  64.                         End Using
  65.                         ' edit the contents
  66.                         str = str.Replace("Has3DDWF=0", "Has3DDWF=1")
  67.                         str = str.Replace("PromptForDwfName=TRUE", "PromptForDwfName=FALSE")
  68.                         ' rewrite the Dsd file
  69.                         Using sw As New StreamWriter(dsdFile, False, Encoding.[Default])
  70.                             sw.Write(str)
  71.                         End Using
  72.  
  73.                         ' import the Dsd file new contents in the DsdData
  74.                         dsd.ReadDsd(dsdFile)
  75.  
  76.                         File.Delete(dsdFile)
  77.  
  78.                         Dim pc As PlotConfig = PlotConfigManager.SetCurrentConfig("DWF6 ePlot.pc3")
  79.                         Application.Publisher.PublishExecute(dsd, pc)
  80.                     End Using
  81.                 End Using
  82.             Finally
  83.                 Application.SetSystemVariable("BACKGROUNDPLOT", bgPlot)
  84.             End Try
  85.         End Sub
  86.     End Class
  87. End Namespace
« Last Edit: July 16, 2012, 03:03:55 PM by gile »
Speaking English as a French Frog

TheMaster

  • Guest
Re: .NET Layout,ViewPort,Plot Routines
« Reply #4 on: July 15, 2012, 06:50:24 PM »
Publishing to 3d DWF

Great work.

Just one small comment (and some code), which is that just like we've been doing in LISP since the beginning, we need to not allow a changed system variable's value to remain changed when our command exits. Using your code as an example, if an exception caused the code to exit before the BACKGROUNDPLOT system variable's value was restored, it will remain set to the temporary value you assign it to. While in many cases it may not be an issue, you might note that there are quite a few posts by users asking why the values of certain system variables mysteriously change while using AutoCAD, and of course, it is almost always caused by scripting that fails to take the needed precautions.

Here's a solution I've been using for quite some time, that I've recently updated to support System.Dynamic.

Code - C#: [Select]
  1.  
  2. // LocalSystemVariables.cs  copyright (c) 2007-2012  Tony Tanzillo
  3.  
  4. using System;
  5. using System.Linq;
  6. using System.Dynamic;
  7. using System.Collections.Generic;
  8. using System.Collections.ObjectModel;
  9. using Autodesk.AutoCAD.ApplicationServices;
  10.  
  11. namespace Autodesk.AutoCAD.ApplicationServices
  12. {
  13.   // A common problem that affects AutoCAD scripting
  14.   // using LISP, is the need to temporarily change
  15.   // the values of AutoCAD system variables, and save
  16.   // and restore their previous values once the code
  17.   // that requires the values changed has finished.
  18.   //
  19.   // At first this seems like a simple problem because
  20.   // the programmer assumes that they only need to save
  21.   // the previous values of system variables in local or
  22.   // global program variables, change the system variable
  23.   // values, and just before their code has finished its
  24.   // job, restore the system variables to their previous,
  25.   // saved values.
  26.   //
  27.   // What complicates this seemingly-simple task, and causes
  28.   // significant problems for users, is that when an error
  29.   // causes a script to terminate prematurely, unless there
  30.   // are special precautions taken by an error handler, the
  31.   // values of changed system variables will not be restored
  32.   // to their previous, user-assigned values. In fact, this
  33.   // is a well-known problem in the world of LISP scripting,
  34.   // and many strategies have been devised and published to
  35.   // deal with it.
  36.   //
  37.   // In the .NET world, the problem is no less prominent, and
  38.   // failure to address it can result in the very same problem
  39.   // for users (the values of system variables changed after a
  40.   // .NET program or custom command has exited, usually as a
  41.   // result of an error).
  42.   //
  43.   // Because error/exception handling is far more robust in
  44.   // the .NET world (in contrast to LISP), the means we have
  45.   // to deal with the problem can also be more robust.  
  46.   //
  47.   // LocalSystemVariable class.
  48.   //
  49.   // Encapsulates an AutoCAD system variable and an
  50.   // associated value that has the same scope as the
  51.   // life of the LocalSystemVariable instance.
  52.   //
  53.   // This class can be used to temporarily change the
  54.   // value of a system variable, save the previous value,
  55.   // and ensure the saved value is properly restored
  56.   // when the LocalSystemVariable instance is disposed.
  57.  
  58.   // To use this class, create an instance of it under
  59.   // control of a using() directive, and supply the name
  60.   // and the new, temporary value for the system variable.
  61.   //
  62.   // within the using() block, the system variable's value
  63.   // will be set to the value passed to the constructor.
  64.   // When control leaves the using() block, the previous
  65.   // saved value of the system variable (the value it had
  66.   // when the constructor was called) will be restored.
  67.   //
  68.   // This very simple example shows how to temporarily
  69.   // set the value of the OSMODE system variable to 0
  70.   // for the duration of a registered command handler:
  71.  
  72.   public static class LocalSystemVariableExample
  73.   {
  74.     [CommandMethod("MYCOMMAND")]
  75.     public static void MyCommand()
  76.     {
  77.       using( var osmode = new LocalSystemVariable( "OSMODE", 0 ) )
  78.       {
  79.         // here, the value of the OSMODE system variable
  80.         // will be set to 0.  When the using() block is
  81.         // exited (even if it happens as a result of an
  82.         // exception), the value of OSMODE is restored
  83.         // to the value it had prior to entering this
  84.         // using() block.
  85.        
  86.         // Show the temporary value of OSMODE:
  87.        
  88.         WriteMessage("In MyCommand(): The value of OSMODE is now {0}", osmode.Value );
  89.       }
  90.      
  91.       // Show the restored value of OSMODE:
  92.      
  93.       WriteMessage("Exiting MyCommand(): the value of OSMODE is now {0}",
  94.         Application.GetSystemVariable("OSMODE") );
  95.      
  96.     }
  97.    
  98.     // Example usage of LocalSystemVariables collection
  99.     // to manage multiple system varibles with temporary
  100.     // values within the same scope.
  101.    
  102.     // Just as when driving AutoCAD from LISP using the
  103.     // (command) function, When doing the same in .NET
  104.     // via the acedCmd() method, it is often necessary
  105.     // to save, change, and then restore the values of
  106.     // several system variables. This example shows how
  107.     // that can be easily achieved through the use of
  108.     // the LocalSystemVariables class:
  109.    
  110.     [CommandMethod("MYCOMMAND2")]
  111.     public static void MyCommand2()
  112.     {
  113.       // Prompt for user input here
  114.      
  115.       using( var sysvars = new LocalSystemVariables() )
  116.       {
  117.         sysvars["CMDECHO"] = 0;
  118.         sysvars["HIGHLIGHT"] = 0;
  119.         sysvars["BLIPMODE"] = 0;
  120.         sysvars["OSMODE"] = 0;
  121.        
  122.         // Make calls to acedCmd() here to perform
  123.         // operations using AutoCAD commands.
  124.        
  125.        
  126.       } // At this point the previous, saved values of all
  127.         // system variables changed above are automatically
  128.         // restored, even if an exception causes control flow
  129.         // to exit the using() block prematurely.
  130.  
  131.     }
  132.    
  133.     // This example is functionally identical to the above
  134.     // one, except that it uses System.Dynamic to specify
  135.     // the names of the system variable as code identifiers,
  136.     // as if they were properties of the LocalSystemVariables
  137.     // class (note that identifiers are case-insensitive):
  138.    
  139.     [CommandMethod("MYCOMMAND3")]
  140.     public static void MyCommand3()
  141.     {
  142.       // Prompt for user input here
  143.      
  144.       using( dynamic sysvars = new LocalSystemVariables() )
  145.       {
  146.         sysvars.CmdEcho = 0;
  147.         sysvars.Highlight = 0;
  148.         sysvars.BlipMode = 0;
  149.         sysvars.OSMode = 0;
  150.        
  151.         // Make calls to acedCmd() here to perform
  152.         // operations using AutoCAD commands.
  153.        
  154.       } // Saved system variable values restored here
  155.     }
  156.    
  157.     public static void WriteMessage( string fmt, params object[] args )
  158.     {
  159.       Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( fmt, args );
  160.     }
  161.   }
  162.  
  163.   // LocalSystemVariable class
  164.  
  165.   public class LocalSystemVariable : IDisposable
  166.   {
  167.     object oldval = null;
  168.     string name = string.Empty;
  169.    
  170.     public LocalSystemVariable( string name, object newval )
  171.     {
  172.       this.name = name;
  173.       this.oldval = SetValue( name, newval );
  174.     }
  175.  
  176.     public string Name {get { return this.name; } }
  177.  
  178.     internal static object GetValue( string name, bool validate = false )
  179.     {
  180.       if( string.IsNullOrEmpty( name ) )
  181.         throw new ArgumentException("name");
  182.       try
  183.       {
  184.         object result = Application.GetSystemVariable( name );
  185.         if( result == null )
  186.           throw new ArgumentException("name");
  187.         return result;
  188.       }
  189.       catch
  190.       {
  191.         if( validate )
  192.           throw new ArgumentException(
  193.             string.Format( "Invalid System Variable Name: {0}", name ));
  194.         else
  195.           return null;
  196.       }
  197.     }
  198.  
  199.     // This was revised to deal with incompatible types
  200.     // (e.g., passing an int when a short is required),
  201.     // and will try to convert the new value to the type
  202.     // of the existing value, although I suspect that the
  203.     // managed runtime is already converting int to short
  204.     // in all cases.
  205.    
  206.     internal static object SetValue( string name, object value )
  207.     {
  208.       if( value == null )
  209.         throw new ArgumentNullException( "value" );
  210.       object oldval = GetValue( name, true );
  211.       object newval = value;
  212.       if( oldval.GetType() != value.GetType() )
  213.         newval = Convert.ChangeType( value, oldval.GetType() );
  214.       if( ! object.Equals( newval, oldval ) )
  215.         Application.SetSystemVariable( name, newval );
  216.       return oldval;
  217.     }
  218.  
  219.     // Get or set the current value of the system variable.
  220.     // Note that setting this property does not change the
  221.     // initial saved value that will be restored when the
  222.     // instance is disposed:
  223.    
  224.     public object Value
  225.     {
  226.       get
  227.       {
  228.         return GetValue( this.name, true );
  229.       }
  230.       set
  231.       {
  232.         SetValue( this.name, value );
  233.       }
  234.     }
  235.  
  236.     public void Dispose()
  237.     {
  238.       if( oldval != null )
  239.       {
  240.         try
  241.         {
  242.           SetValue( this.name, this.oldval );
  243.         }
  244.         finally
  245.         {
  246.           this.oldval = null;
  247.         }
  248.       }
  249.     }
  250.   }
  251.  
  252.   // A collection of LocalSystemVariables that automatically
  253.   // disposes its elements when the collection is disposed.
  254.   //
  255.   // This class can be used to manage multiple LocalSystemVariable
  256.   // instances without the complexity that would otherwise result
  257.   // from using multiple instances of LocalSystemVariables directly.
  258.   //
  259.   // The contained system variables can be accessed and added via
  260.   // the default indexer, or by declaring the instance as 'dynamic',
  261.   // and writing the system variable names as identifiers (as if
  262.   // they were properties of this class).
  263.   //
  264.   // Refer to the included examples for both forms of usage.
  265.  
  266.   public class LocalSystemVariables : DynamicObject, IDisposable, IEnumerable<LocalSystemVariable>
  267.   {
  268.     Collection items = new Collection();
  269.  
  270.     public LocalSystemVariables()
  271.     {
  272.     }
  273.    
  274.     public bool Contains( string name )
  275.     {
  276.       return items.Contains( name );
  277.     }
  278.    
  279.     public override bool TryGetMember( GetMemberBinder binder, out object result )
  280.     {
  281.       result = LocalSystemVariable.GetValue( binder.Name );
  282.       return result != null;
  283.     }
  284.    
  285.     public override bool TrySetMember( SetMemberBinder binder, object value )
  286.     {
  287.       this[binder.Name] = value;
  288.       return true;
  289.     }
  290.    
  291.     public object this[string name]
  292.     {
  293.       get
  294.       {
  295.         return LocalSystemVariable.GetValue( name, true );
  296.       }
  297.       set
  298.       {
  299.         if( ! items.Contains( name ) )
  300.           items.Add( new LocalSystemVariable( name, value ) );
  301.         else
  302.           LocalSystemVariable.SetValue( name, value );
  303.       }
  304.     }
  305.    
  306.     public void Remove( string name )
  307.     {
  308.       items.Remove( name );
  309.     }
  310.  
  311.     public void Clear()
  312.     {
  313.       items.Clear();
  314.     }
  315.    
  316.     public int Count
  317.     {
  318.       get
  319.       {
  320.         return items.Count;
  321.       }
  322.     }
  323.    
  324.     public void Dispose()
  325.     {
  326.       if( items != null )
  327.       {
  328.         try
  329.         {
  330.           using( IEnumerator<LocalSystemVariable> e = items.Reverse().GetEnumerator() )
  331.           {
  332.             DisposeNext( e );
  333.           }
  334.         }
  335.         finally
  336.         {
  337.           items = null;
  338.         }
  339.       }
  340.     }
  341.    
  342.     // Recursively dispose each item in a try/finally block
  343.     // to ensure that all items are disposed. This could be
  344.     // viewed as being very dangerous, but because the number
  345.     // of elements is always small, the risk of overflowing
  346.     // the stack would be virtually impossible.
  347.    
  348.     void DisposeNext( IEnumerator<LocalSystemVariable> e )
  349.     {
  350.       if( e.MoveNext() )
  351.       {
  352.         try
  353.         {
  354.           e.Current.Dispose();
  355.         }
  356.         finally
  357.         {
  358.           DisposeNext( e );
  359.         }
  360.       }
  361.     }
  362.    
  363.     public IEnumerator<LocalSystemVariable> GetEnumerator()
  364.     {
  365.       return items.GetEnumerator();
  366.     }
  367.    
  368.     IEnumerator IEnumerable.GetEnumerator()
  369.     {
  370.       return this.GetEnumerator();
  371.     }
  372.  
  373.     class Collection : KeyedCollection<string, LocalSystemVariable>
  374.     {
  375.       public Collection() : base( StringComparer.OrdinalIgnoreCase )
  376.       {
  377.       }
  378.      
  379.       protected override string GetKeyForItem( LocalSystemVariable value )
  380.       {
  381.         return value.Name.ToUpper();
  382.       }
  383.     }
  384.   }  
  385.  
  386. }
  387.  
  388.  


kdub:edit code=csharp
« Last Edit: September 02, 2012, 11:15:12 AM by Kerry »

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET Layout,ViewPort,Plot Routines
« Reply #5 on: July 16, 2012, 02:27:15 AM »
Quote
Great work.
Thanks, Tony.

Quote
we need to not allow a changed system variable's value to remain changed when our command exits
You're absolutely right.
I should have used a try/finally block to reset BACKGROUNDPLOT

Your 'disposable' Local System Variables class is amazing. I enjoy it as I enjoyed the 'ai-sysvar' LISP function when I discovered it.

To follow here.
« Last Edit: July 16, 2012, 03:05:29 PM by gile »
Speaking English as a French Frog

TheMaster

  • Guest
Re: .NET Layout,ViewPort,Plot Routines
« Reply #6 on: July 19, 2012, 02:49:53 AM »
And now for some relatively-legitimate nit-picking (of my own code).

In the above code, the LocalSystemVariables class uses a scheme that attempts to ensure that every element in the collection is disposed, even if one or more of their Dispose() implementations throws an exception. 

While the scheme works, it is unnecessarily stack-intensive, recursing one level for every element in the list (and as I mentioned in my comments, a stack overflow would be extremely unlikely).

This version of DisposeNext() will only recurse into itself if a call to Dispose() throws an exception:

Code - C#: [Select]
  1.  
  2.     // LocalSysemVariables.DisposeNext() revised:
  3.     // This revised version of DisposeNext() is only recursive if an
  4.     // element's Dispose() implementation throws an exception, and
  5.     // limits the depth of recursion to the number of elements whose
  6.     // Dispose() throws an exception:
  7.  
  8.     void DisposeNext( IEnumerator<IDisposable> e )
  9.     {
  10.       while( e.MoveNext() )
  11.       {
  12.         try
  13.         {
  14.           e.Current.Dispose();
  15.         }
  16.         catch
  17.         {
  18.           DisposeNext( e );
  19.           throw;
  20.         }
  21.       }
  22.     }
  23.  
  24.  

One more comment, which is why a class derived from KeyedCollection<TKey, TValue> was used rather than a Dictionary. Aside from the fact that KeyedCollection is an ordered collection, it also uses a hybrid algorithm for storage and lookup. When the number of elements in the collection is small, it stores them in a list and uses a simple linear search for lookups, which can be faster than a Dictionary when the number of items is below a certain threshold. Once the number of elements grows beyond the threshold, the items are stored in, and retrieved from a Dictionary, which is faster with a larger number of items.


kdub:edit code=csharp
« Last Edit: September 02, 2012, 11:16:02 AM by Kerry »

Dale Bartlett

  • Mosquito
  • Posts: 17
Re: .NET Layout,ViewPort,Plot Routines
« Reply #7 on: October 06, 2012, 03:46:49 AM »
Thanks again Tony for sharing, really valuable.
I have an error in one line as follows:
368: IEnumerator IEnumerable.GetEnumerator()
Using the generic type 'System.Collections.Generic.IEnumerator<T>' requires 1 type arguments

Been unable to resolve unfortunately, have I missed something?
Regards, Dale

Jeff H

  • Needs a day job
  • Posts: 6144
Re: .NET Layout,ViewPort,Plot Routines
« Reply #8 on: October 06, 2012, 04:00:20 AM »
While you wait for Tony to reply, try adding
  using System.Collections;

Dale Bartlett

  • Mosquito
  • Posts: 17
Re: .NET Layout,ViewPort,Plot Routines
« Reply #9 on: October 06, 2012, 08:22:14 AM »
Just returned to say exactly that. Dumb me...
Thanks,
Dale

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET Layout,ViewPort,Plot Routines
« Reply #10 on: March 17, 2013, 06:24:18 AM »
Hi,

Here's a little class to be used to plot layouts to a single multi sheets PDF file.

Code - C#: [Select]
  1. using System.Collections.Generic;
  2. using System.IO;
  3. using System.Text;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.PlottingServices;
  6. using Autodesk.AutoCAD.Publishing;
  7. using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;
  8.  
  9. namespace Plottings
  10. {
  11.     public class MultiSheetsPdf
  12.     {
  13.         private string dwgFile, pdfFile, dsdFile, outputDir;
  14.         private int sheetNum;
  15.         IEnumerable<Layout> layouts;
  16.  
  17.         private const string LOG = "publish.log";
  18.  
  19.         public MultiSheetsPdf(string pdfFile, IEnumerable<Layout> layouts)
  20.         {
  21.             Database db = HostApplicationServices.WorkingDatabase;
  22.             this.dwgFile = db.Filename;
  23.             this.pdfFile = pdfFile;
  24.             this.outputDir = Path.GetDirectoryName(this.pdfFile);
  25.             this.dsdFile = Path.ChangeExtension(this.pdfFile, "dsd");
  26.             this.layouts = layouts;
  27.         }
  28.  
  29.         public void Publish()
  30.         {
  31.             if (TryCreateDSD())
  32.             {
  33.                 Publisher publisher = AcAp.Publisher;
  34.                 PlotProgressDialog plotDlg = new PlotProgressDialog(false, this.sheetNum, true);
  35.                 publisher.PublishDsd(this.dsdFile, plotDlg);
  36.                 plotDlg.Destroy();
  37.                 File.Delete(this.dsdFile);
  38.             }
  39.         }
  40.  
  41.         private bool TryCreateDSD()
  42.         {
  43.             using (DsdData dsd = new DsdData())
  44.             using (DsdEntryCollection dsdEntries = CreateDsdEntryCollection(this.layouts))
  45.             {
  46.                 if (dsdEntries == null || dsdEntries.Count <= 0) return false;
  47.  
  48.                 if (!Directory.Exists(this.outputDir))
  49.                     Directory.CreateDirectory(this.outputDir);
  50.  
  51.                 this.sheetNum = dsdEntries.Count;
  52.  
  53.                 dsd.SetDsdEntryCollection(dsdEntries);
  54.  
  55.                 dsd.SetUnrecognizedData("PwdProtectPublishedDWF", "FALSE");
  56.                 dsd.SetUnrecognizedData("PromptForPwd", "FALSE");
  57.                 dsd.SheetType = SheetType.MultiDwf;
  58.                 dsd.NoOfCopies = 1;
  59.                 dsd.DestinationName = this.pdfFile;
  60.                 dsd.IsHomogeneous = false;
  61.                 dsd.LogFilePath = Path.Combine(this.outputDir, LOG);
  62.  
  63.                 PostProcessDSD(dsd);
  64.  
  65.                 return true;
  66.             }
  67.         }
  68.  
  69.         private DsdEntryCollection CreateDsdEntryCollection(IEnumerable<Layout> layouts)
  70.         {
  71.             DsdEntryCollection entries = new DsdEntryCollection();
  72.  
  73.             foreach (Layout layout in layouts)
  74.             {
  75.                 DsdEntry dsdEntry = new DsdEntry();
  76.                 dsdEntry.DwgName = this.dwgFile;
  77.                 dsdEntry.Layout = layout.LayoutName;
  78.                 dsdEntry.Title = Path.GetFileNameWithoutExtension(this.dwgFile) + "-" + layout.LayoutName;
  79.                 dsdEntry.Nps = layout.TabOrder.ToString();
  80.                 entries.Add(dsdEntry);
  81.             }
  82.             return entries;
  83.         }
  84.  
  85.         private void PostProcessDSD(DsdData dsd)
  86.         {
  87.             string str, newStr;
  88.             string tmpFile = Path.Combine(this.outputDir, "temp.dsd");
  89.  
  90.             dsd.WriteDsd(tmpFile);
  91.  
  92.             using (StreamReader reader = new StreamReader(tmpFile, Encoding.Default))
  93.             using (StreamWriter writer = new StreamWriter(this.dsdFile, false, Encoding.Default))
  94.             {
  95.                 while (!reader.EndOfStream)
  96.                 {
  97.                     str = reader.ReadLine();
  98.                     if (str.Contains("Has3DDWF"))
  99.                     {
  100.                         newStr = "Has3DDWF=0";
  101.                     }
  102.                     else if (str.Contains("OriginalSheetPath"))
  103.                     {
  104.                         newStr = "OriginalSheetPath=" + this.dwgFile;
  105.                     }
  106.                     else if (str.Contains("Type"))
  107.                     {
  108.                         newStr = "Type=6";
  109.                     }
  110.                     else if (str.Contains("OUT"))
  111.                     {
  112.                         newStr = "OUT=" + this.outputDir;
  113.                     }
  114.                     else if (str.Contains("IncludeLayer"))
  115.                     {
  116.                         newStr = "IncludeLayer=TRUE";
  117.                     }
  118.                     else if (str.Contains("PromptForDwfName"))
  119.                     {
  120.                         newStr = "PromptForDwfName=FALSE";
  121.                     }
  122.                     else if (str.Contains("LogFilePath"))
  123.                     {
  124.                         newStr = "LogFilePath=" + Path.Combine(this.outputDir, LOG);
  125.                     }
  126.                     else
  127.                     {
  128.                         newStr = str;
  129.                     }
  130.                     writer.WriteLine(newStr);
  131.                 }
  132.             }
  133.             File.Delete(tmpFile);
  134.         }
  135.     }
  136. }
  137.  

Using example which creates a new instance of MultiSheetsPdf passing to the constructor the current drawing file name with "pdf" extension and all layouts except "Model":
Code - C#: [Select]
  1.         [CommandMethod("PlotPdf")]
  2.         public void PlotPdf()
  3.         {
  4.             Database db = HostApplicationServices.WorkingDatabase;
  5.             short bgp = (short)AcAp.GetSystemVariable("BACKGROUNDPLOT");
  6.             try
  7.             {
  8.                 AcAp.SetSystemVariable("BACKGROUNDPLOT", 0);
  9.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  10.                 {
  11.                     List<Layout> layouts = new List<Layout>();
  12.                     DBDictionary layoutDict =
  13.                         (DBDictionary)db.LayoutDictionaryId.GetObject(OpenMode.ForRead);
  14.                     foreach (DBDictionaryEntry entry in layoutDict)
  15.                     {
  16.                         if (entry.Key != "Model")
  17.                         {
  18.                             layouts.Add((Layout)tr.GetObject(entry.Value, OpenMode.ForRead));
  19.                         }
  20.                     }
  21.                     layouts.Sort((l1, l2) => l1.TabOrder.CompareTo(l2.TabOrder));
  22.  
  23.                     string filename = Path.ChangeExtension(db.Filename, "pdf");
  24.  
  25.                     MultiSheetsPdf plotter = new MultiSheetsPdf(filename, layouts);
  26.                     plotter.Publish();
  27.  
  28.                     tr.Commit();
  29.                 }
  30.             }
  31.             catch (System.Exception e)
  32.             {
  33.                 Editor ed = AcAp.DocumentManager.MdiActiveDocument.Editor;
  34.                 ed.WriteMessage("\nError: {0}\n{1}", e.Message, e.StackTrace);
  35.             }
  36.             finally
  37.             {
  38.                 AcAp.SetSystemVariable("BACKGROUNDPLOT", bgp);
  39.             }
  40.         }
« Last Edit: March 24, 2013, 01:33:58 PM by gile »
Speaking English as a French Frog

Dale Bartlett

  • Mosquito
  • Posts: 17
Re: .NET Layout,ViewPort,Plot Routines
« Reply #11 on: March 24, 2013, 04:36:07 AM »
Hi Giles, "public MultiSheetsPdfPlot(" should be "public void MultiSheetsPdf(", if I am not wrong (always possible). Regards, Dale

gile

  • Gator
  • Posts: 2507
  • Marseille, France
Re: .NET Layout,ViewPort,Plot Routines
« Reply #12 on: March 24, 2013, 01:37:08 PM »
Oops !...

Indeed MultiSheetsPdfPlot should be MultiSheetsPdf (code edited), but not with void: it's a class constructor.
Speaking English as a French Frog

Dale Bartlett

  • Mosquito
  • Posts: 17
Re: .NET Layout,ViewPort,Plot Routines
« Reply #13 on: March 25, 2013, 05:13:33 AM »
Yes of course. I actually had it correct on the second go.. I've been trying to build your routine into a batch (db.ReadDwgFile) process, but am finding it difficult. AutoCAD responds "A plot or publish job is already being processed in the background" etc, and just does a couple. I would post my code but I have been down so many rabbit holes that it is beyond posting atthe moment. I also used some guidance from the ADN plugin BatchPublish. Hmmm tricky. If I manage something useful I'll post. Dale

BlackBox

  • King Gator
  • Posts: 3770
Re: .NET Layout,ViewPort,Plot Routines
« Reply #14 on: July 10, 2013, 06:47:08 PM »
Here's a quick Extension Method (no exception handling):

Code - C#: [Select]
  1. namespace Autodesk.AutoCAD.DatabaseServices
  2. {
  3.     public static class EntityExtensions
  4.     {
  5.         public static string LayoutName(this Entity ent, Transaction tr)
  6.         {
  7.             BlockTableRecord blockTableRecord =
  8.                 (BlockTableRecord)tr.GetObject(ent.BlockId, OpenMode.ForRead);
  9.  
  10.             Layout layout =
  11.                 (Layout)tr.GetObject(blockTableRecord.LayoutId, OpenMode.ForRead);
  12.  
  13.             return layout.LayoutName;
  14.         }
  15.     }
  16. }
  17.  

... Example:
Code - C#: [Select]
  1. // start transaction
  2.  
  3.     // get dbobject
  4.  
  5.     BlockReference br = (BlockReference)obj;
  6.  
  7.     string layoutName = br.LayoutName(tr);
  8.  
  9.     // commit()
  10.  
"How we think determines what we do, and what we do determines what we get."