January 2015

Sun Mon Tue Wed Thu Fri Sat
        1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

January 07, 2015

Recreating the Star Wars opening crawl in AutoCAD using F# – Part 2

After introducing this series in the last post, today we’re going to address the first 3 items on our TODO list:

  1. The initial blue text
  2. The theme music
  3. The star field
  4. The disappearing Star Wars logo
  5. The crawling text

The following two items are fairly significant, in their own right, so they’ll each take a post of their own to complete. Oh, and I’ve thrown in a surprise item 6, which I’ll unveil when we implement the crawling text.

Before we dive in, it’s important to make some points about the code: because this is mainly just a bit of fun, this code hasn’t been generalised to work with any drawing and any view, etc. I’ve hardcoded a lot of values that just work on a specific drawing and on my system. The timing works well for me, but may be off when working on systems with different performance profiles. Mileage may vary, as they say.

That’s one reason I’m providing a drawing with the appropriate views set up. I originally planned on putting the layers in there, too, but then decided to create those at runtime (as it was simple to do so). I could have done that with the views, too, but it didn’t seem worth the extra effort.

Another thing I should mention: in my first run at this I used transient graphics to display the intro and crawl text. I then decided to switch to db-resident objects, as I thought I could place them on layers that I turn off when they’re no longer needed. I ended up finding that didn’t work – as I have a single transaction making all the drawing modifications in the command, and couldn’t find a way to have the graphics system reflect the pending database changes – so I went ahead and erased them instead. I decided to stick with db-resident rather than transient graphics, nonetheless, but using transient graphics remains a viable approach for this: the reason I’m not doing so isn’t especially significant.

With that, here’s a look at the code in this post running inside AutoCAD:

A few task-specific comments:

1. The initial blue text

This was straightforward to implement. I ended up creating MText with the font information embedded in the contents, rather than a separate style. This is mainly because it’s the approach I used for the crawl text, later on, which uses multiple fonts. Overkill for this text, of course, but it saves me creating the style.

We’re using the intro text to do a bunch of things, behind the scenes. It’s essentially our splash screen for doing things like downloading the MP3 file for the theme music, etc.

2. The theme music

I came across an online version of the crawl music accessed in this great HTML implementation of the opening crawl. Seems like an ideal candidate for SWAPI integration, extending it beyond the single episode. :-) Here’s the code associated with it, if you want to take a look.

I found out that the System.Windows.Media namespace contains a MediaPlayer object that allows you to access/play an MP3 file via a URL. All that remained was to work out some of the timings related to the music – it starts at 8.5 second in, for instance – and apply these to the code. In this version of the code, the player stops either when the music finishes or when AutoCAD is closed, whichever happens first.

3. The star field

This was a fairly simple matter of generating a bunch of random numbers and using them to define stars. I didn’t want to pass around large numbers of AutoCAD objects, though – whether DBPoints or even Point3ds – so I used a list of F# tuples (with x and y values, each of which holds a float between 0 and 1) that later gets transformed into DBPoints somewhere in the screen space. Although for the surprise item 6 I extended that space to be twice as high as currently needed.

Here’s the F# code implementing our first pass at the EPISODE command:

module StarWars.Crawler


open Autodesk.AutoCAD.ApplicationServices

open Autodesk.AutoCAD.ApplicationServices.Core

open Autodesk.AutoCAD.DatabaseServices

open Autodesk.AutoCAD.Geometry

open Autodesk.AutoCAD.Runtime

open System

open System.Windows.Media


// The intro music MP3 file


let mp3 =



// The layers we want to create as a list of (name, (r, g, b))


let layers =


    ("Stars", (255, 255, 255));

    ("Intro", (75, 213, 238))



// Create layers based on the provided names and colour values

// (only creates layers if they don't already exist... could be

// updated to make sure the layers are on/thawed and have the

// right colour values)


let createLayers (tr:Transaction) (db:Database) =

  let lt =

    tr.GetObject(db.LayerTableId, OpenMode.ForWrite) :?> LayerTable

  layers |>

  List.iter (fun (name, (r, g, b)) ->

    if not(lt.Has(name)) then

      let lay = new LayerTableRecord()

      lay.Color <-

        Autodesk.AutoCAD.Colors.Color.FromRgb(byte r, byte g, byte b)

      lay.Name <- name

      lt.Add(lay) |> ignore

      tr.AddNewlyCreatedDBObject(lay, true)



// Get a view by name


let getView (tr:Transaction) (db:Database) (name:string) =

  let vt =

    tr.GetObject(db.ViewTableId, OpenMode.ForRead) :?> ViewTable

  if vt.Has(name) then

    tr.GetObject(vt.[name], OpenMode.ForRead) :?> ViewTableRecord




// Add an entity to a block and a transaction


let addToDatabase (tr:Transaction) (btr:BlockTableRecord) o =

  btr.AppendEntity(o) |> ignore

  tr.AddNewlyCreatedDBObject(o, true)


// Flush the graphics for a particular document


let refresh (doc:Document) =




// Transform between the Display and World Coordinate Systems


let dcs2wcs (vtr:AbstractViewTableRecord) =

  Matrix3d.Rotation(-vtr.ViewTwist, vtr.ViewDirection, vtr.Target) *

  Matrix3d.Displacement(vtr.Target - Point3d.Origin) *



// Poll until a music file has downloaded fully

// (could sleep or use a callback to avoid this being too

// CPU-intensive, but hey)


let rec waitForComplete (mp:MediaPlayer) =

  if mp.DownloadProgress < 1. then


    waitForComplete mp


// Poll until a specified delay has elapsed since start

// (could sleep or use a callback to avoid this being too

// CPU-intensive, but hey)


let rec waitForElapsed (start:DateTime) delay =

  let elapsed = DateTime.Now - start

  if elapsed.Seconds < delay then


    waitForElapsed start delay


// Create the intro text as an MText object relative to the view

// (has a parameter to the function doesn't execute when loaded...

// also has hardcoded values that make it view-specific)


let createIntro _ =


  let mt = new MText()

  mt.Contents <-

    "{\\fFranklin Gothic Book|b0|i0|c0|p34;" +

    "A long time ago, in a galaxy far,\\Pfar away...}"

  mt.Layer <- "Intro"

  mt.TextHeight <- 0.5

  mt.Width <- 10.

  mt.Normal <- Vector3d.ZAxis

  mt.TransformBy(Matrix3d.Displacement(new Vector3d(1., 6., 0.)))



// Generate a quantity of randomly located stars... a list of (x,y)

// tuples where x and y are between 0 and 1. These will later

// get transformed into the relevant space (on the screen, etc.)


let locateStars quantity =


  // Create our random number generator


  let ran = new System.Random()


  // Note: _ is only used to make sure this function gets

  // executed when it is called... if we have no argument

  // it's a value that doesn't require repeated execution


  let randomPoint _ =


    // Get random values between 0 and 1 for our x and y coordinates


    (ran.NextDouble(), ran.NextDouble())


  // Local recursive function to create n stars at random

  // locations (in the plane of the screen)


  let rec randomStars n =

    match n with

    | 0 -> []

    | _ -> (randomPoint 0.) :: randomStars (n-1)


  // Create the specified number of stars at random locations


  randomStars quantity


// Take locations from 0-1 in X and Y and place them

// relative to the screen


let putOnScreen wid hgt dcs (x, y) =


  // We want to populate a space that's 2 screens high (so we

  // can pan/rotate downwards at the end of the crawl), hence

  // the additional multiplier on y


  let pt = new Point3d(wid * (x - 0.5), hgt * ((y * -1.5) + 0.5), 0.)



// Commands to recreate the open crawl experience for a selected

// Star Wars episode



let episode() =


  // Make sure the active document is valid before continuing


  let doc = Application.DocumentManager.MdiActiveDocument

  if doc <> null then


    let db = doc.Database

    let ed = doc.Editor


    // Start our transaction and create the required layers


    use tr = doc.TransactionManager.StartTransaction()

    createLayers tr db


    // Get our special Initial and Crawl views


    let ivtr = getView tr db "Initial"

    let cvtr = getView tr db "Crawl"


    if ivtr = null || cvtr = null then


        "\nPlease load StarWarsCrawl.dwg before running command.")



    let btr =

      tr.GetObject(doc.Database.CurrentSpaceId, OpenMode.ForWrite)

        :?> BlockTableRecord


    // Set the initial view: this gives us higher quality text




    // First we create the intro text


    let intro = createIntro ()

    intro |> addToDatabase tr btr


    // Make sure the intro text is visible


    doc |> refresh



    // We'll now perform a number of start-up tasks, while our

    // initial intro text is visible... we'll start vy recording

    // our start time, so we can synchronise our delay


    let start = DateTime.Now


    // Get our view's DCS matrix


    let dcs = dcs2wcs(cvtr)


    // Create a host of stars at random screen positions


    locateStars 1000 |>


      (fun xy ->

        let p = putOnScreen cvtr.Width cvtr.Height dcs xy

        let dbp = new DBPoint(p)

        dbp.Layer <- "Stars"

        dbp |> addToDatabase tr btr)


    // Open the intro music over the web


    let mp = new MediaPlayer()

    mp.Open(new Uri(mp3))


    // Wait for the download to complete before playing it


    waitForComplete mp


    // Have a minimum delay of 5 seconds showing the intro text


    waitForElapsed start 5


    // Start the audio at 8.5 seconds in


    mp.Position <- new TimeSpan(0, 0, 0, 8, 500)



    // Switch to the crawl view: this will also change the

    // visual style from 2D Wireframe to Realistic




    // Remove the intro text




    tr.Commit() // Commit the transaction

That’s it for this part in the series. In the next part we’ll take a look at the code to show the disappearing Star Wars logo inside AutoCAD.

January 05, 2015

Recreating the Star Wars opening crawl in AutoCAD using F# – Part 1

Happy New Year, everyone!

As mentioned in the last post, I took a couple of weeks off over the holiday period, extending Autodesk’s annual “week of rest” between Christmas and New Year to a full fortnight.

This time of year clearly has religious significance for many. Some go to church, to temple or even to Stonehenge, some watch one or more of the Star Wars trilogies – arguably which they choose depends on how devout they are. I only ended up watching one Star Wars film, over the holiday period, and while it was from the Despecialized Edition, I don’t necessarily consider myself a fundamentalist. ;-)

A sneak peek at the Star Wars opening crawl inside AutoCAD

I did spend some time on a Star Wars-related coding project, though. The project was initiated when I received notification, just before Christmas, that the long-awaited (by me, at least :-) Star Wars API was available for Beta-testing. This is a Data-as-a-Service REST API that provides access to all manner of information about the various episodes in the Star Wars movie franchise, including which characters appeared in which film, the weapons and vehicles they used, etc.

I put on my thinking hat to see if there was some compelling way this API might be used inside AutoCAD. There are lots of CAD-centric allusions throughout the Star Wars movies  – Death Star plans, 3D targeting systems, etc. – and I felt that for sure there was some fun to be had with it. In the end I settled on using AutoCAD to recreate the famous opening crawl.

Here’s the original opening crawl from the 1977 version of Star Wars episode IV, for context:

There are a few components to the opening crawl that we’re going to recreate inside AutoCAD over the course of this week. This page was helpful when looking at the specifics of how to implement some of these items.

  1. The initial blue text
  2. The theme music
  3. The star field
  4. The disappearing Star Wars logo
  5. The crawling text

We’ll actually only be using the Star Wars API to retrieve the opening crawl text for step 5, as the command we end up implementing will create a crawl for each of the episodes covered by the API (which means I-VI at the time of writing, but also for VII once released in December 2015).

To access the Star Wars API, we’ll once again use the F# Type Provider mechanism for this, just as we used it with Typepad in this recent post. That’s actually the main reason for using F# for this project, to see how it simplifies access to DaaS APIs. At some point I may well throw together a C# version of this code, but we’ll see: the AutoCAD-centric API calls should be relevant to anyone using AutoCAD’s .NET API from another language, of course.

In the next post we’ll take a look at knocking off the first few items from the above list.

December 19, 2014

Signing off from 2014

Thank you for your readership and support during the last year. It’s really the interactions with this blog’s readers that motivate me to keep posting after 8½ years.

As a small gesture of appreciation, here’s a simple HTML card that serves up random posts from this blog’s first 1,220 (or so). If you’re not interested by the post it shows when it loads, just click on the “Merry Christmas!” banner until you find one you feel like reading. The provided link will launch the post in a separate browser tab.

In case you’re interested in the implementation details, the shadow text is based on this project which uses the Less CSS pre-processor – I just pre-processed the CSS and added some JavaScript code to make the main text glow – and the falling snowflakes are from here. The random post serving is done completely in client-side JavaScript (which is why it’s quick once the page has loaded) and is based on the implementation I did some time ago for the “10 Random Posts” list in this blog’s right-sash.

I’m planning on taking two full weeks off during the holiday period, returning to work on January 5th. That said, if the snow doesn’t hit soon I may well end up playing around with stuff over the holidays and even writing the odd blog post, so we’ll see how it goes. :-)

All the best to you and yours for the holiday season. Keep the feedback and post suggestions coming, and here’s looking forward to a great 2015!

December 17, 2014

Extracting code from blog posts using F# – Part 1

I mentioned some weeks ago that I was looking to automate the validation of a bunch of source files I have on disk before uploading them to GitHub. I decided to get started on that, this week, to see what was involved. As mentioned in the previous post, I wanted to programmatically query posts from Typepad, extract any embedded code from them and compare it with what I had on disk, to see which files were already correct and which needed to be created or removed.

The first step was to choose a language. The fact this is really an OS-level task – with a bunch of string processing and file I/O, but nothing whatsoever to do with AutoCAD – gave me some freedom to choose pretty much any language that can be run on OS X or Windows. I briefly considered Python or Ruby, but ended up going back to F#: it’s nicely integrated into Visual Studio (my primary development tool) and a functional approach makes a lot of sense for this kind of problem. I also had the itch to do more with F# after my recent (too brief) foray into machine learning. One final factor in the decision process – not that it precludes the use of F#, at all – is that as this is a one-off activity I’m not at all worried about performance or memory constraints. It doesn’t matter if it takes 10 minutes to complete, for instance. Just as long as it completes. ;-)

Armed with F#, I then started looking at the problem itself. First up was pulling data down from the blog: I needed an API to access my blog’s content on Typepad – thankfully there’s a simple REST API which gives access to its posts’ complete content – and then had some additional choices to make about how to access the data.

To simplify working with the blog’s post data, I decided to use F# Type Providers. These allow you to code against data-oriented services as if they had local object models. Which is exactly what happens, I guess: when the code instantiates a JSON Type Provider against a particular resource – I downloaded a sample JSON file from the Typepad API for this – it’s contents are then accessible via a locally-generated set of objects and properties.

The next problem was the “screen scraping”: we need to extract HTML code and convert it to plain text to compare with the local files. I opted for the HtmlAgilityPack for this: it comes with an Html2Txt sample that I converted to F#. I haven’t followed it exactly, as I wanted to keep some amount of whitespace in the generated plain text, but it got me a good part of the way there.

In a general sense, here’s what the algorithm needs to do:

  1. Parse the various C# files on disk and create an index that links a comma-separated command-list with the source filename
    • The order isn’t significant: if the commands don’t come in the same order then the files will be different
  2. Extract the post content from my blog and parse it for code fragments
    • I’ve used the same CopyAsHtml tool since this blog’s inception, so all code sections are enclosed in a similar-looking <div>
    • For now I only care about posts with a single code section. There are certainly posts where I’ve used this tool to copy smaller fragments for illustrative purposes, so at some point I need the code to pick these up, too
  3. For the posts with a single code section, extract the code and convert it to plain text
  4. Extract the commands implemented in the code and check for which files have the same commands – in the same sequence – on disk
  5. Perform a lower-level comparison between the code extracted from HTML with the files of disk
    • This still needs some work: there are files which should match that for some reason don’t, right now, but overall it’s working quite well
    • For debugging purposes I’m currently writing any unmatched code fragments as files in another folder, so that I can go through and see what problems are worth fixing
  6. The ultimate output is a list of post titles with the matching local filename
    • It’ll be a simple matter to copy these files programmatically into a local folder that will sync with GitHub

Here’s the code I have, so far:


#r "Z:/GitHub/FSharp.Data/bin/FSharp.Data.dll"

#r "packages\HtmlAgilityPack.1.4.9\lib\Net45\HtmlAgilityPack.dll"

#r "System.Xml"



open FSharp.Data

open HtmlAgilityPack

open System.Xml

open System

open System.IO


let codeHeader = "<div style="

let blogRoot = "http://api.typepad.com/blogs/6a00d83452464869e200d83452baa169e2/post-assets.json"

let csFolder = @"Z:\data\Blogs\Projects\Basic C# app"

let tmpFolder = @"Z:\data\Blogs\Projects\Basic C# app\Notfound"

let csTest =

  @"Z:\data\Blogs\Projects\Basic C# app\enumerate-sysvars.cs"

let cmdAttrib = "[CommandMethod("


type Post = JsonProvider<"data.json">


// Use TypePad's REST API to retrieve batches of posts


let getPosts m n =

  let url = String.Format("{0}?max-results={1}", blogRoot, m)

  let url2 =

    match n with

    | 0 -> url

    | _ -> url + "&start-index=" + (m * n).ToString()

  let doc = Post.Load(url2)



// Count the number of times a substring appears in a string


let countOccurrences (sub:string) (text:string) =

  match sub with

  | "" -> 0

  | _ ->

    (text.Length - text.Replace(sub, @"").Length) / sub.Length


// These are HTML entity codes etc. that need to be replaced

// as we convert from HTML to plain text


let reps =

  [("&#0160;"," ");("&#160;"," ");("&nbsp;"," ");("&gt;",">");

   ("&lt;","<");("&#39;","'");("&quot;", "\"");("&ndash;","-");



let convertText (t : string) =


    (fun (a : string) (b : string, c : string) -> a.Replace(b,c))

      t reps


// Use the HtmlAgilityPack to convert from HTML to plain text


let rec convertTo (node : HtmlNode ) =

  match node.NodeType with

  | HtmlNodeType.Comment -> ""

  | HtmlNodeType.Document ->

      Seq.map convertTo node.ChildNodes |>

        Seq.fold (fun r s -> r + s) ""

  | HtmlNodeType.Text ->

      // script and style must not be output

      let parentName = node.ParentNode.Name

      if parentName = "script" || parentName = "style" then



        // get text

        let html = (node :?> HtmlTextNode).Text;


        // is it in fact a special closing node output as text?

        if HtmlNode.IsOverlappedClosingElement(html) then



          convertText html

  | HtmlNodeType.Element ->

      if node.Name = "p" then

        if node.HasChildNodes then

          (Seq.map convertTo node.ChildNodes |>

            Seq.fold (fun r s -> r + s) "") + "\r\n"



      else if node.HasChildNodes then

        Seq.map convertTo node.ChildNodes |>

          Seq.fold (fun r s -> r + s) ""



  | _ -> ""


// Take post data and extract the HTML fragment representing code


let extractCode (content : string) =

  let start = content.IndexOf(codeHeader)

  let finish = content.LastIndexOf("</div>") + 6

  let html = content.Substring(start, finish - start)

  let doc = new HtmlDocument()


  convertTo doc.DocumentNode


// If a post contains only 1 code segment, we'll extract it


let processPost (ent : Post.Entry) =

  let count = countOccurrences codeHeader ent.Content



  match count with

  | 1 -> extractCode ent.Content

  | _ -> ""


// List the files conforming to a pattern in a folder


let filesInFolder pat folder =

  try Directory.GetFiles(folder, pat, SearchOption.TopDirectoryOnly)

    |> Array.toList

  with | e -> []


// Get the indices at which a substring occurs in a string


let stringIndices (pat:string) (text:string) =

  let rec getIndices (pat:string) (text:string) (start:int) =

    match text.IndexOf(pat, start) with

    | -1 -> []

    | x -> x :: getIndices pat text (x+1)

  getIndices pat text 0


// Extract the command name from a CommandMethod attribute


let extractCommandName (text : string) =

  let delim = "\""

  let count = countOccurrences delim text

  match count with

  | 0 -> ""

  | 1 -> ""

  | 2 -> text.Substring(1, text.LastIndexOf(delim) - 1)

  | 3 -> ""

  | _ ->

      let idxs = stringIndices delim text

      text.Substring(idxs.[2] + 1, idxs.[3] - idxs.[2] - 1)


// Extract the various command names from a code segment


let rec commandsFromCode (text : string) =

  match text.Contains(cmdAttrib) with

  | false -> []

  | true ->

    let start = text.IndexOf(cmdAttrib) + cmdAttrib.Length

    let finish = text.IndexOf(")", start + 1)

    let name =

      text.Substring(start, finish - start) |> extractCommandName

    name :: commandsFromCode (text.Substring finish)


// Create a comma-separated string from a list of strings


let rec commaSepString (cmds : string list) =

  match cmds with

  | [] -> ""

  | x::[] -> x

  | x::xs -> x + "," + commaSepString xs


// Get the commands for a particular file on disk as a

// comma-separated list and return them with the filename


let commandsForFile file =

  File.ReadAllText file |>

  commandsFromCode |>

  commaSepString |>

  (fun x -> (x, file))


// Get the command names for a set of files on disk


let rec commandsForFiles files =

  match files with

  | [] -> []

  | file::xs -> commandsForFile file :: commandsForFiles xs


// Create an index from commands to files for a particular folder


let indexCommands (folder : string) =

  filesInFolder "*.cs" folder |> commandsForFiles


// From our index, get the files associated with a command-set


let filesForCommandsFromIndex index cmds =

  index |>

  List.filter (fun (a,b) -> a = cmds && a <> "") |>

  List.map (fun (a,b) -> b)


// Strip blank lines from a sequence of strings


let stripBlanks (s : seq<string>) =

  Seq.filter (fun x -> not(String.IsNullOrWhiteSpace(x))) s


// Compare sequences of strings, ignoring non-relevant whitespace


let compareSequences (s1 : seq<string>) (s2 : seq<string>) =


    (fun (a:string) (b:string) -> String.Compare(a.Trim(), b.Trim()))

    (stripBlanks s1) (stripBlanks s2)


// Write code to a temp file - for debugging only


let writeToTmpFile (code:string) =

  let rec getTmpFile i =

    let file = tmpFolder + "\\" + i.ToString() + ".cs"

    if not(File.Exists(file)) then



      getTmpFile (i+1)

  use wr = new StreamWriter((getTmpFile 0))



// Take a code fragment and a file and check them for equivalence


let checkCodeAgainstFile (code:string) (file:string) =

  let clines = code.Split("\n\r".ToCharArray())

  let flines = File.ReadAllLines(file)

  let s1 = Seq.ofArray clines

  let s2 = Seq.ofArray flines

  if compareSequences s1 s2 = 0 then





// Take a code fragment and a set of files and see if one matches


let checkCodeAgainstFiles code files =

  let rec checkAgainstFiles code files =

    match files with

    | [] -> []

    | x::xs ->

      checkCodeAgainstFile code x :: checkAgainstFiles code xs

  checkAgainstFiles code files |> List.concat


// Our main function



let main argv =


  // Build an index from commands to source files on the hard drive


  let index = indexCommands csFolder


  // Pull down post information from TypePad and process it


  let posts =

    [|0..25|] |>

    Array.map (getPosts 50) |> // Get 1250 posts in batches of 50

    Array.concat |>            // Flatten the nested arrays

    Array.map processPost      // Process the posts


  // Separate the posts into posts with code and those without


  let postsWith, postsWithout =

    Array.partition (fun (a,b,c) -> b > 0) posts


  // Separate the posts with code into those with one section

  // and those with more


  let postsWithOne, postsWithMore =

    Array.partition (fun (a,b,c) -> b = 1) postsWith



    "%d posts with zero, %d posts with one, %d posts with more"





  // We'll take the posts with a single code section and process

  // them


  let res =

    postsWithOne |>

    Array.map (fun (a,b,c) -> commandsFromCode c) |> // Get commands

    Array.map commaSepString |> // Make a comma-delimited cmd list

    Array.map (filesForCommandsFromIndex index) |> // Use our index

    Array.map2 (fun (a,b,c) d -> (a,c,d)) postsWithOne |> //

    Array.filter (fun (a,b,c) -> b <> "") |> // Strip codeless


      (fun (a,b,c) ->


        let x = checkCodeAgainstFiles b c

        if x = [] then writeToTmpFile b // This is for debugging

        x) |>

    Array.filter (fun (a,b) -> b <> []) // Strip fileless


  0 // return an integer exit code

Right now it finds 108 source files that are “correct” on disk. This is a reasonable start, but there are certainly more to be found.

By the way, while I don’t currently have a second part of this series planned, specifically, I know I’m going to need one to share the final version of the code. Which I’ll also place on GitHub, of course. :-)

December 15, 2014

Creating an associative fillet operation using ObjectARX

This is really cool. Fellow architect on the AutoCAD team, Jiri Kripac – who originally wrote AutoCAD’s “AModeler” facet modeler and is the driving force behind AutoCAD’s Associative Framework – has written a really interesting ObjectARX sample to perform an associative fillet between two curves. Given Jiri’s background, this is as close to a canonical sample for implementing an operation using the Associative Framework – in this case by creating a custom AcDbAssocActionBody – as you’re likely to find.

Here’s a video showing this custom fillet in action, and how it can be used with parameters and expressions to do some really impressive things (for instance, Jiri demonstrates basing an extruded surface on curves linked via an associative fillet… really nice).

Jiri presented this implementation at AU 2014 and has also posted all his code to GitHub.

When building the app, be sure to follow Jiri’s instructions in the ReadMe: you’ll need to place the code in the samples\entity folder of the ObjectARX SDK for AutoCAD 2015 and then build it using Visual Studio 2012 (or at least the v110 “platform toolset” build tools, allowing you to use the VS 2013 IDE with the 2012 compiler). You will need to load the .vcxproj – no .sln is provided – and do remember to set the build configuration appropriately (the default is Win32 rather than x64, and this often isn’t directly obvious through the Visual Studio UI).

Many thanks to Jiri for providing this excellent sample!

December 11, 2014

AutoCAD 2015 APIs available via NuGet

We’ve received this request a few times in recent years: .NET developers have asked to be able to add dependencies on AutoCAD’s .NET reference assemblies – primarily acmgd.dll, acdbmgd.dll and accoremgd.dll – to their Visual Studio projects using the built-in package management tool, NuGet. For those of you unfamiliar with NuGet, it’s well worth checking out: it’s a really nice way to manage library dependencies in your .NET projects.

Well, Christmas has come a few weeks early: the AutoCAD team has posted the 2015 .NET reference assemblies to NuGet. This is interesting to .NET developers for a number of reasons.

The first reason is the simplicity of adding the assemblies to AutoCAD .NET projects. You simply select Project –> Manage NuGet Packages in your Visual Studio project:

Manage NuGet packages menu option

And then you can search for “AutoCAD.NET” and click install on the primary AutoCAD.NET package (the first in the list). This will install acmgd.dll and its related assemblies but also the other two dependent packages. You should install AutoCAD.NET.Core (i.e. acmgdcore.dll, etc.) if you want to create a Core Console-compatible project, of course. Either way AutoCAD.NET.Model (i.e. acdbmgd.dll, etc.) will get installed.

NuGet install

If you have issues with the naming conventions, then you can blame me. Other than this blog post, proposing these names was my only contribution to the publishing effort. ;-)

If you’re an old-school command-line junkie, you might prefer to use the NuGet console to install AutoCAD .NET packages. If you don’t already have the console in your UI, open it using Tools –> NuGet Package Manager –> Package Manager Console:

NuGet console menu option

From here you can enter “Install-Package AutoCAD.NET” to bring in all three packages:

Install via the NuGet console

Whether you initiated the installation via the NuGet UI or console, you should now have the appropriate (super-)set of assembly references available in your project:

Our reference set once finished

The second main reason NuGet support is interesting is that you can more easily manage dependencies from your own projects and libraries that you want to post or share (for instance via NuGet or GitHub). Previously you would have to include the reference assemblies inside your library/project when you shared it. Now you can use NuGet – which adds references into a packages.config file – when creating the project and then these dependencies are managed for you. So when installing a NuGet library that depends on AutoCAD.NET, for instance, you will get all three packages installed automatically. And if you clone a sample project from GitHub – such as this one we’ve posted for AutoCAD I/O (which was actually a primary driver for doing this) – the dependencies will get resolved and the assembly references added when you load it into Visual Studio.

We will continue to post .NET libraries for AutoCAD for installation NuGet, moving forwards, but I don’t expect versions prior to AutoCAD 2015 to be made available, at this stage. If that’s a requirement for you (bearing in mind that yesterday you couldn’t even get the libraries for 2015 via NuGet :-), then please do post a comment via this blog or via ADN and we’ll take the feedback into consideration.

December 10, 2014

Project Memento v1.10.12.1

The Memento product got some well-deserved airtime at Autodesk University 2014: it had prominent mentions during both the opening and closing keynotes.

Carl Bass on Memento during the opening keynote

Amar Hanspal on Memento during the closing keynoteA new version has now been pushed live to Autodesk Labs and comes with some really useful enhancements. Full details can be found on Scott’s blog.

I’ll start by talking about three enhancements I made use of for a specific project. My goal was to take some OBJ files – which had previously been generated from photos and exported by Memento – and set the coordinate system correctly for viewing in A360. Well, for use with the View & Data API, which is the way you can embed the A360 viewer in your own apps. (The actual goal was to get a couple of lightweight OBJ models into the Google Cardboard samples we’ve talked about in a number of recent posts, by way of the View & Data API.)

The first OBJ I wanted to manipulate was created from photos taken by Daniel Du from the ADN team in Beijing. The Daniel capturing the columnproblem with the generated OBJ was that the up direction was set to something the A360 viewer didn’t like. I’ve been told our photo-generated models are “Z up” – rather than A360’s preferred “Y up” – but my personal experience has been a bit different: we have some models – such as the Steampunked Morgan 3 Wheeler – which are “Z up”, but the same code didn’t work on these OBJ models at all. I have a feeling they are “X up”, but then it’s also possible something else is getting in the way.

The first enhancement I took advantage of in Memento was gimbal navigation. Three spherical axes appeared when in orbit mode, which certainly made it easier to rotate the view in a controlled way.

The column in Memento

The next enhancement I needed – in particular, given my goal – was set coordinate system. I wanted to set the model to “Y up”, so that it would work with my viewer apps. This was a little counter-intuitive to me – although admittedly this feature’s goal is to make this intuitive for non-CAD people – as I had to set the view to have the actual up direction aligned with the Y axis before setting the coordinate system as such. Which meant I had to be looking upwards at the base of the column for this to work properly. I know the Memento team is iterating quickly on this, so it may be that it ends up changing: the main thing is that I was able to fix the model to meet my needs.

If you’re interested in checking the results, I included new “Column” and “Tablet” models in the Google Cardboard samples. They look great.

The column in Cardboard

Just for fun, I used the new video output feature to create an AVI of the column model being rotated.

Besides these three enhancements there are some really cool new capabilities in this release: diff analysis allows you to compare two meshes. Really useful if tracking progress (or deterioration) over time or performing as-built analysis against a CAD model.

3D scanner support has also taken a few steps forward (even if it’s still a work in progress). Artec’s Spider scanner is now supported, alongside the previously supported Eva, and you can now perform a capture using multiple sweeps of the scanner, too.

All in all a very interesting release from the Memento team. As ever, be sure to let us know your feedback via the Labs feedback mechanism, so that we can keep making enhancements that meet your needs.

December 09, 2014

Titanic exhibition: that sinking feeling

Some friends from the UK were visiting Switzerland (although not staying with us) over the weekend, so we decided to meet them for dinner on Sunday at Geneva airport before their flight home. Thankfully I’d flown in from Las Vegas (and then Heathrow) to Zurich, the day before, otherwise visiting the same airport twice in one weekend would have seemed very weird.

My wife suggested we go a little early (despite me being Vegas-lagged) and visit an exhibition she’d heard about at Palexpo (which is right at Geneva airport, and is also where the Geneva Motor Show takes place). The exhibition was about the Titanic, and displayed more than 200 pieces recovered from the ship.


There were many interesting facets to this exhibition. Of course there were lots of recovered artifacts on display:

China and ceramics

Serving utensils, dishes and tiles

But there were also a number of interesting historical photographs. I know these will interest this audience, for instance:

Drawing office


There were areas reproducing parts of the vessel, such as the sleeping quarters for 1st and 3rd class passengers:

First class cabin

Third class cabin

Most impressive, perhaps, was the reproduction of the Grand Staircase:

Grand staircase

Edwardian England had a very pronounced class structure, and this was clearly reflected by the difference in services aboard the ship. A first class ticket was $2,500 (which would be $57,200 today), while a third class ticket was $40 (or $900 today). The most expensive suite would have been the $103,000 in today’s currency.

But people from all classes – and, of course, the crew – died when the Titanic went down. Here’s a quick look at the numbers:

Titanic survivors

Some time was spent looking at the salvage mission and the site of the shipwreck. There was some mention of many hours of 3D video footage having been taken, at the time, although I think the mission preceded many of the technological advances that have since been used for the USS Arizona.

Copyright 1998 RMS Titanic, Inc.

One thing I really liked about the exhibition was that it had a really personal element: each person entering was given a boarding card describing the background of someone who was on the Titanic and didn’t make it home. It really helped people identify with an event that has inevitably become more abstract as time has passed.

Overall the exhibition was simply fascinating, well worth the effort. If you get the chance to pass by Geneva before the exhibition closes – it has recently been extended to February 1st, 2015 – then do try to go. It’s a unique opportunity to learn the story of this incredible ship’s historic – and tragic – maiden voyage.


I’ve just found out by email that a blog reader visited a similar exhibition – which yes, did feature a wall with a piece of iceberg for people to touch – when it came to Florida in 2001. It’s great to know the exhibition has been elsewhere, which means that hopefully people will be able to catch it without having to travel to Switzerland.

And now that I’ve thought to look, it seems I could also have visited a similar exhibition in Las Vegas. This one in Belfast looks really impressive, too.

December 08, 2014

Autodesk University 2014: Day 3

Autodesk University 2014 ended really well on Thursday. Despite me nearly showing up late for my final speaking engagement (I somehow had it in my head that it started at 1:30pm rather than 1:00pm – thankfully I chose to show up 32 minutes early!), the session itself went very well. It was the only one of my sessions that ended up being recorded (it wasn’t about AutoCAD, at all – it was all about the Geneva Motor Show project which was implemented using Autodesk VRED), and it was a fun one.

Jason Walter demoing the Morgan configurator

The final keynote was, once again, very inspiring. Amar Hanspal challenged the audience to innovate by asking ourselves “What If?”.

What If

The author of the brilliant xkcd web-comic, Randall Munroe, gave an entertaining talk about “what if?” questions he answers for people, such as what would happen if a car could keep accelerating forever.

Answers from xkcd

Mike Chen, from Made in Space, described taking additive manufacturing – 3D printing – to space is like “a wish for more wishes”. And who among us hasn’t wanted one of those?

A wish for more wishes

The people behind Arx Pax took the stage to demonstrate the awesome Hendo hoverboard – with the inevitable “Back to the Future II” reference – but went on to explain how this technology has great potential to (for instance) help deal with earthquakes.

The Hendo hoverboard in action

If you’d like to watch the keynote in its entirety, here it is:

From the keynote, we went straight on to the closing party at The Palms. Yes, incredibly we were all bussed across to a different hotel for the party, which would almost certainly have been disastrous if attempted anywhere else in the world: the logistics involved in moving several thousand people efficiently between hotels was jaw-dropping.

The party had a comic book theme, which was fun. And only in Vegas would you be able to play stretched table football…

Table football, Vegas-style

On Friday I attended a couple of internal meetings and did a little Christmas shopping before travelling back overnight, arriving home on Saturday evening.

Kean at the Bellagio

I’ve chatted to a few different people about the fact that AU just felt different, this year. For sure I liked the focus of the opening and closing keynotes – and admittedly I only got to attend one session, other that the ones I was presenting – but the feel of the event was subtly different. I think it was mostly the Exhibition Hall that did it, on balance. I asked Joseph Wurcher – one of the event’s organisers – about this... He explained that they’d deliberately designed the Exhibit Hall to have no straight corridors: you couldn’t really help but get lost as you wandered around, taking you to different places with interesting things to see. It certainly worked really well, from my perspective (I actually tried giving people directions to a few different exhibits but failed miserably :-).

It’s perhaps a bit clichéd, but this year’s AU was really the best one ever. Aside from the quality of the event itself (Joseph tells me the average session rating was higher than previous years), I personally found it incredibly motivating to interact with so many passionate AutoCAD users and developers. And it’s always great to hear from so many of this blog’s readers that they continue to find it valuable: it really motivates me to keep blogging for another year, at least.

Thanks again for all the feedback and support!

December 04, 2014

Autodesk University 2014: Day 2

It’s suddenly Thursday morning. I feel like I’ve been in one of those hyperlapse videos since my last blog post. Here’s a quick update on what I’ve been up to.

When I wrote my last post I was actually sitting in on Albert Szilvasy’s AutoCAD I/O session. This very interesting class was recorded so be sure to watch it when it comes online.

Albert I-O

From there I headed to the Exhibit Hall. This year the Exhibit Hall at AU is just incredible. I don’t know what’s changed, exactly, but I just love the way it’s been laid out. And I’ve really enjoyed wandering around talking to people on the various stands.

It was great to see the new Ember 3D printer in action:

Autodesk Ember

I snuck back into the Exhibit Hall when it was closed to have my photo taken sitting in the world’s first 3D printed car.

Sitting in the Local Motors Strati

It was great to see the Morgan configurator on display across the way:

The Morgan configurator

And yes, AutoCAD was everywhere. :-)

AutoCAD is everywhere

Wednesday was my busiest day, presentation-wise. In the morning I talked to a roomful of people interested in AutoCAD’s JavaScript API and then to a smaller (but very engaged) crowd about connecting AutoCAD to SensorTag in the afternoon. The day’s presentations ended with a panel session about Autodesk’s Cloud and Mobile APIs, which was really interesting.

After dropping my bag back in my hotel room, I headed on down to Minus5, to meet with some customers (it was great to meet you, Jim and Dan :-). It was quite surreal chatting about AutoCAD in a fur coat and hat, sipping a mojito from a glass made out of ice. That was a first for me, at least. Although I suppose going to an ice bar in the middle of the desert isn’t much stranger than having a sauna in Switzerland. ;-)

While in Minus5 I ran into Marion Pietsch, an old friend, and we had our picture taken in our “Narnia” get-up:

With Marion in the Minus 5 ice bar

From there I headed off to the Blogger and Social Meetup, in the Rí Rá Irish pub, where I had the chance to sit down and talk to a number of really interesting people. There were a number of representatives from Made in Space, who have used Autodesk tools to design the first zero-G 3D printer.

A highlight of the event was chatting at length with TJ McCue, fresh off his fascinating 3DRV tour. Here I am with TJ and Karen Mason, a friend and colleague from the AutoCAD team.

At the blogger social with Karen and TJ

Right now I’m sitting in the Answer Bar, but I’ll soon head off for a bite to eat ahead of my last commitment of this year’s AU, presenting a quick introduction at Jason Walter’s class on the Morgan configurator his team implemented for the Geneva Motor Show.

I’ll certainly live-tweet the closing session before heading to the closing party, marking the end of yet another great AU!


10 Random Posts