Export DXFs of 2D (parallel projection) standard views in Ruby

Hi there, I’ve been looking for a simple ruby script that exports a series of 2D DXF profiles of the following standard camera views in parallel projection:

Front / Top / Left / Right

This is to try and save time doing this manually as I’ve got thousands of models to create 2D views of.

After a few hours or searching these forums and not finding exactly what I’m after, I’ve tried to hash together a bit of code myself (go easy - I’m a complete beginner!)

I’ve managed to get the camera changing but can’t figure out how to change it from ISO to 2D and the DXF export isn’t working. Haven’t even got round to setting up the batch process for the different views.

 eye = [200, 200, 200]
 target = [0, 0, 0]
 up = [0, 0, 1]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 show_summary = true
 status = model.export( 'C:/my_export.dxf', show_summary )

Any ideas where I’m going wrong?

Thanks,

Sam

You can get a top view e.g. with this parameters

 eye = [0, 0, 200]
 target = [0, 0, 1]
 up = [0, 1, 0]

See also:
Camera #initialize-instance_method Returns a new camera with eye (where the camera is) and targets (where the camera is looking).

The #eye method is used to retrieve the eye Point3d object for the Camera.
The #target method retrieves Point3d that the camera is pointing at.
The #up method is used to retrieve the up vector for the camera. This is the direction that the top of the camera is facing.
The #set method sets the camera orientation.

You can examine e.g. this: https://sketchucation.com/pluginstore?pln=default_scenes to get some ideas.

The Model #export method does not have a way to do 2d dxf export. The build in 2d dwg/dxf export is not exposed to API. You will need to write code for your own exporter.

See also the Exporter Options file for information on creating a valid hash for the various exporters.

1 Like

Thanks for your reply - really appreciate the feedback. The camera setup code now makes sense.

It’s a shame the 2D DXF export is not exposed to API. However, I think the script still could save me some time as I’ve found this bit of code that at least skips a few menu clicks:

 result = Sketchup.send_action 21237

Is there a way to automatically add a suffix to the default filename when the action 21237 is ran. Something that could correspond with the target view export?

_F (for Front View)
_L (for Left View)

For example Model_F.dxf (for Front View), Model_L.dxf (for Left View) e.t.c.

I’ve got the following code to work as a workaround so far.

 eye = [0, 300, 0]
 target = [0, 1, 0]
 up = [0, 0, 1]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 result = Sketchup.send_action 21237
 UI.messagebox("press 'ok' to continue")
 eye = [0, 0, 300]
 target = [0, 0, 1]
 up = [0, 1, 0]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 result = Sketchup.send_action 21237
 UI.messagebox("press 'ok' to continue")

Thanks again,

__
The best would be … UI method “exportpanel” ( like savepanel method ), but unfortunately that does not exists.
__
There is no way to add any other parameter for send_action method.

Note that in Sketchup #send_action-class_method :

On the PC only, you can also send these numeric values. (Note that these are officially “unsupported” and are not guaranteed to work in current or future versions of the API.)

Also take a look on this for drawbacks of send_action method.: https://github.com/SketchUp/api-issue-tracker/issues/542#issuecomment-704834945

__
BTW. Your snippet does not work in SU 22.0.354 as in your forum profile, but works in SU 23.1.340. … and looks weird while the double popup opened. :blush:


BTW2:

I did not checked but perhaps this can be used instead:
https://extensions.sketchup.com/extension/081a38b7-f5a7-4c89-98f6-c399373039e4/skema-2d-projection

1 Like

That’s a shame. The send_action feature definitely feels like a bit of a bodge.

My final code is as follows (if it happens to help somebody in the future).

eye = [0, -300, 0]
 target = [0, 0, 0]
 up = [0, 0, 1]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 result = Sketchup.send_action 21237
 UI.messagebox("You've Saved A Front View")

 eye = [-300, 0, 0]
 target = [0, 0, 0]
 up = [0, 0, 1]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 result = Sketchup.send_action 21237
 UI.messagebox("You've Saved A Left View")

 eye = [300, 0, 0]
 target = [0, 0, 0]
 up = [0, 0, 1]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 result = Sketchup.send_action 21237
 UI.messagebox("You've Saved A Right View")

 eye = [0, 300, 0]
 target = [0, 0, 0]
 up = [0, 0, 1]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 result = Sketchup.send_action 21237
 UI.messagebox("You've Saved A Back View")

 eye = [0, 0, 300]
 target = [0, 0, 0]
 up = [0, 1, 0]
 cam = Sketchup::Camera.new eye, target, up, perspective = false
 view = Sketchup.active_model.active_view
 view.camera = cam
 model = Sketchup.active_model
 result = Sketchup.send_action 21237
 UI.messagebox("You've Saved A Top View")

I have to manually add the extension on the filename but saves a fair bit of time setting up the camera angles. The popup boxes are also annoying, but supposedly it’s required to stop the send_action command from bugging out:

The first line opens the 2D export dialog (then the user has to save the file manually). Unfortunately the send_action method is asynchronous which was messing up my script, but creating a messagebox on the next line successfully blocks the script until the save dialog closes.

Weirdly my script seems to work fine on 22.0.354, not sure why!

Thanks again for all your help and input.

1 Like

Take a look at examples of TIG exporter codes. Maybe this will help you a little to add yours.
https://sketchucation.com/forums/viewtopic.php?p=183609#p183609
https://sketchucation.com/forums/viewtopic.php?p=183756#p183756

1 Like

It is asynchronous. Meant for toolbar buttons not really for scripting because it does not wait for the export to finish. The following statements will be immediately executed. I see you’ve placed a messagebox to stop execution of following exports.

You may be able to use the Windows Scripting Host SendKeys method to automate the dialog.

def export_dxf_view(shell, filename)
  Sketchup.send_action(21237)
  sleep 2.0
  # Type the export filename:
  shell.SendKeys("#{filename}.dxf")
  # Tab to the Filetype, and choose DXF:
  shell.SendKeys("{TAB}aa")
  # Tab to the Export button:
  shell.SendKeys("{TAB}{TAB}{TAB}")
  # Press ENTER:
  shell.SendKeys("{ENTER}")
end

def export_all_views
  require 'win32ole' unless defined?(WIN32OLE)
  shell = WIN32OLE.new("WScript.Shell")
  #
  model = Sketchup.active_model
  view = model.view
  basename = model.title
  #
  cameras = {}
  #
  # set up cameras with viewname as hash key
  #
  cameras.each do |viewname, camera|
    view.camera= camera
    filename = "#{basename}_#{viewname}"
    export_dxf_view(shell, filename)
    UI.messagebox("Press OK when the #{viewname} file has been written.")
  end
end
4 Likes

Thanks both for your input.

@DanRathbun Your code looks great and exactly what I’m after however, I’ve tried running it and it although it executes, it doesn’t open the dialog boxes as intended.

My (complete beginner) coding logic dictates that I need to add the camera setup element of my current code, into the cameras = {}

Something like:

cameras = 
    { eye: [0, -300, 0], target: [0, 0, 0], up: [0, 0, 1], viewname: "Front" },
    { eye: [-300, 0, 0], target: [0, 0, 0], up: [0, 0, 1], viewname: "Left" },
    { eye: [300, 0, 0], target: [0, 0, 0], up: [0, 0, 1], viewname: "Right" },
    { eye: [0, 300, 0], target: [0, 0, 0], up: [0, 0, 1], viewname: "Back" },
    { eye: [0, 0, 300], target: [0, 0, 0], up: [0, 1, 0], viewname: "Top" }

Have spent a good few hours trying to get this to work with different combinations of code, but feel I’ve reached the point where I need to ask for help again.

Any ideas?

e.g. :
You need one hash where the key- value pairs are the viewname (String) - camera (Object)
(Did not checked if the given eye, target, up arrays are okay or not)

cameras = 
  { 
    "Front": Sketchup::Camera.new( [0, -300, 0],  [0, 0, 0], [0, 0, 1] ), 
    "Left": Sketchup::Camera.new( [-300, 0, 0], [0, 0, 0],  [0, 0, 1] ),
    "Right": Sketchup::Camera.new( [300, 0, 0], [0, 0, 0], [0, 0, 1] ),
    "Back": Sketchup::Camera.new( [0, 300, 0], [0, 0, 0],  [0, 0, 1] ),
    "Top": Sketchup::Camera.new( [0, 0, 300],  [0, 0, 0], [0, 1, 0] )
  }

Class: Hash (Ruby 2.7.2) (ruby-doc.org)

2 Likes

Thanks for your input. The code seems to be running and it executes the “export_all_views” function. But doesn’t change any of the camera angles and no dialog boxes appear.

I’m going to keep reading the documentation and testing, but will leave my latest code here in case someone spots anything really obvious that I’m doing wrong!

def export_dxf_view(shell, filename)
  Sketchup.send_action(21237)
  sleep 2.0
  # Type the export filename:
  shell.SendKeys("#{filename}.dxf")
  # Tab to the Filetype, and choose DXF:
  shell.SendKeys("{TAB}aa")
  # Tab to the Export button:
  shell.SendKeys("{TAB}{TAB}{TAB}")
  # Press ENTER:
  shell.SendKeys("{ENTER}")
end

def export_all_views
  require 'win32ole' unless defined?(WIN32OLE)
  shell = WIN32OLE.new("WScript.Shell")
  
  model = Sketchup.active_model
  basename = model.title
  
  cameras = {
    "Front": Sketchup::Camera.new([0, -300, 0], [0, 0, 0], [0, 0, 1]),
    "Left": Sketchup::Camera.new([-300, 0, 0], [0, 0, 0], [0, 0, 1]),
    "Right": Sketchup::Camera.new([300, 0, 0], [0, 0, 0], [0, 0, 1]),
    "Back": Sketchup::Camera.new([0, 300, 0], [0, 0, 0], [0, 0, 1]),
    "Top": Sketchup::Camera.new([0, 0, 300], [0, 0, 0], [0, 1, 0])
  }

  cameras.each do |viewname, camera|
    view.camera = camera
    filename = "#{basename}_#{viewname}"
    export_dxf_view(shell, filename)
    UI.messagebox("Press OK when the #{viewname} file has been written.")
  end
end

Thanks again for everyone’s help.

The build in 2D dwg/dxf export is a rather worthless 20 year old hack anyway, as it does not retain origo or tag information and cuts from export anything outside what’s shown at the screen at the moment of the export, so its basically a vector version of “print screen”.

1 Like

Sorry the topic 's owner.

You can try Export DXF from my extension here (a paid plugin but has free trial in 30 days). It will feed your need with:

  • 2D export
  • Included layers with ACI Color and line types.
  • Preserve SketchUp model 's origin
  • Maintain SketchUp user 's unit
  • Included hatches polyline for section if using Curic Section.

You missing this line “view = model.active_view”

The code below now run for me.

def export_dxf_view(shell, filename)
  Sketchup.send_action(21237)
  sleep 2.0
  # Type the export filename:
  shell.SendKeys("#{filename}.dxf")
  # Tab to the Filetype, and choose DXF:
  shell.SendKeys("{TAB}aa")
  # Tab to the Export button:
  shell.SendKeys("{TAB}{TAB}{TAB}")
  # Press ENTER:
  shell.SendKeys("{ENTER}")
end

def export_all_views
  require 'win32ole' unless defined?(WIN32OLE)
  shell = WIN32OLE.new("WScript.Shell")
  
  model = Sketchup.active_model
  view = model.active_view

  basename = model.title
  
  cameras = {
    "Front": Sketchup::Camera.new([0, -300, 0], [0, 0, 0], [0, 0, 1]),
    "Left": Sketchup::Camera.new([-300, 0, 0], [0, 0, 0], [0, 0, 1]),
    "Right": Sketchup::Camera.new([300, 0, 0], [0, 0, 0], [0, 0, 1]),
    "Back": Sketchup::Camera.new([0, 300, 0], [0, 0, 0], [0, 0, 1]),
    "Top": Sketchup::Camera.new([0, 0, 300], [0, 0, 0], [0, 1, 0])
  }

  cameras.each do |viewname, camera|
    view.camera = camera
    filename = "#{basename}_#{viewname}"
    export_dxf_view(shell, filename)
    UI.messagebox("Press OK when the #{viewname} file has been written.")
  end
end

I have finished the DXF Exporter for my plugin, and I learned something I want to share with you.

  • Maybe you need to check the view is a Perspective view or not?
  • The camera view should be created from a bounding box of selection (to define position eye and target)
2 Likes

I am sorry. It was not actually meant to be a complete working code example.

I was only trying to show you how to automate the use of the a dialog using the Windows Scripting Host’s SendKeys method.

Yes, thank you Dezmo. This is correct.

Ah … yes sorry abou this. Correct the error occurs without that statement …

export_all_views
#=>
#<NameError: undefined local variable or method `view' for main:Object>
<main>:29:in `block in export_all_views'

My original post said “You may be able to …

Yes, I just tried it and again. I am sorry the Export dialog is modal. So, it is blocking the shell.SendKeys calls.

I’ll try a timer block, … but no guarantees.

2 Likes

Okay the attached seems to work. ( Windows platform ONLY )

  • I did not package as SketchupExtension RBZ.
  • You can change the dialog delay via the DIALOG_DELAY constant.
  • I added a working directory selector dialog and a query for perspective / orthographic view.
  • I added a zoom extents before opening the Export dialog.
  • The example adds a command to the bottom of the File menu.
  • You can also fire the command via:
    • Example::BatchExportDXF.go

→ (See updated v 1.1.0 RBZ archive posted below)

NOTE: If any of the files (as named) already exist in the destination folder, the batch will be interrupted by an overwrite confirmation messagebox. So perhaps delete the files before starting the batch export.

4 Likes

Thanks so much Dan - this is more than I ever could have asked for! And you’re right - I shouldn’t have assumed your previous script was a complete working code example - you guys are helping off your own backs and it should be down to me to figure things out.

Therefore, I really appreciate the time you’ve taken to produce this script. This is going to save me so much time and hopefully help a bunch of others as well!

Frustratingly, I’ve ran the script and although the dialog boxes pop up correctly and it spends a little while exporting the DXFs before saying “DXF files have been written” - they don’t seem to appear in the target folder.

If it’s working correctly on your machine I think it must have something to do with my specific SU setup.

Just to clarify:

  • I’m on Windows
  • The target folder is empty when script is run (as recommended)
  • I’ve adjusted the Dialog Delay incrementally up to 10 seconds to test to see if this allows the exports to run
  • I’ve tried the code on both 2022 & 2023 and on two different machines
  • I’ve tried searching for the target model name in case it has exported to a temp folder by mistake
  • I’ve tried directly running from the Code Editor and from the GUI command

My next step is to try and get a Ruby debugger running to figure out exactly what’s playing up:

Again, appreciate all your help and input :slight_smile:

I was afraid of this.

The Export dialog uses a standard MS Window dialog from an operating system DLL library.
These dialogs use Windows MRU path settings from the registry. (MRU means Most Recently Used)
Normally, the path in the MRU settings are keyed by filetype (ie. “.txt", ".skp”, etc.)

But when the Export dialog first opens it has prechosen JPG as the filetype.
So, either the Windows operating system is opening the path where the last .jpg was saved to, or else to where some other filetype was last saved.

It did seem to work okay on my Win 10 system last night. I’ll do more testing.

SketchUp has an “Export” path in the Files panel of the Preferences dialog, but it does not appear to be used. The API does not have a means to “live” read (much less change) that “Export” path setting anyway.

The alternative is to TAB up to the location bar of the dialog and paste in the target path as chosen in the UI.select_directory dialog. I was hoping that using it to set the system working directory during the batch export would “help” the export dialogs “find their way.”

BUT, … a quick manual test shows that the Export dialog is opening into the path where the last successful save happened for any type of file. Ie, I did a test SaveAs of an open model, then tried a 2D Export and it opened into the D:/ path where I had just saved the model.
Then I right-clicked one of the “InModel” materials in the Materials inspector and did a local “Save” into my %AppData% materials path, followed again by a 2D Export and yes, it opened into that just used materials path.

This might be as the Export dialog was coded, but it certainly is not useful this way.

So, it looks as though, I will have to modify the export_dxf_view() method to TAB up to the location bar and paste in the chosen path.

Okay, … here is the next update.

  • This is packaged as a RBZ extension archive. You can install it with the Extension Manager.
    • If you do, remove the old file from “Plugins” or wherever so it does not load.

See updated version 1.1.1 below.

  • This is now packaged as a proper SketchupExtension RBZ.
    • It can be installed via Extension Manager
    • It is not signed
  • You can change the dialog delay via the DIALOG_DELAY constant.
  • I added a working directory selector dialog and a query for perspective / orthographic view.
  • I added a zoom extents before opening the Export dialog.
  • The example adds a command to the bottom of the File menu.
  • You can also fire the command via:
    • Example::BatchExportDXF.go
  • Results are displayed in the console and in a messagebox.
    • It asks if you wish to open the directory location.

A. Sometimes the first view does not want to export. There is sometimes a quirk that changes the underscores to dashes in the filename field (in the dialog) but Ruby doesn’t know this and it’s tests for file existence fail. The extension then thinks that one or more views failed to be written out.

Adding a little more delay might help. It’s set to 1.0 second now.

B. Sometimes there is a duplicate export dialog opening for the last view even though it was already exported.

It is the nature of SketchUp API timers and modal dialogs that weird things happen. For example, opening a modal dialog like a messagebox from within a timer block can lead to an infinite number of messageboxes and the need to force quit SketchUp via the Windows Task Manager.

Suggest always save the model before running a batch export if changes have been made.

2 Likes

Arg, It’s never simple is it…

Thanks so much Dan - this looks perfect! Although I’m not at my desk today but I’ll do some thorough testing tomorrow morning and feedback to you ASAP.

Thanks again for all your help and tenacity.

1 Like

So I’ve managed to do some testing this morning and unfortunately it doesn’t seem to work on my machine. It runs and thinks for a bit before opening the manual “Export 2D Graphic” dialog box. Once the DXF is saved manually it opens another dialog box: “DXF Batch Export Done. Written: 0, Errored: 5…”

I’ve tried:

  • Removing all instances of previous plugins
  • On both SU versions 2022 & 2023
  • A few different SU models as tests with varying filename lengths (interestingly on some files I’ve managed to get the filename_Top.dxf to export before the manual “Export 2D Graphic” dialog box pops up. Not sure how.
  • Changing Dialog_Delay incrementally to 5.

I’m going to share it with my collegues to see if they can run it on their machines. I have a feeling it’s because of my particular setup. Will keep you posted.

1 Like