Through the Interface

March 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        



Twitter





March 06, 2015

Autodesk Memento now in public beta

One of the announcements at the recent REAL 2015 conference – and if you missed the event, as I did, you can see a great summary here – was the fact that the much-appreciated mesh manipulation tool from our Reality Solutions division, Autodesk Memento, has now entered public beta. It has graduated from Autodesk Labs and is now available for download without you having to log into the beta portal. It also has a brand new web-site describing why you might want to use it.

Memento

For those of you who have been tracking its progress, release on release, here are the main features in the posted v1.0.14.2:

  • FBX with cameras export
  • Camera location preview in canvas (note that you will need to reprocess your scenes in order to see the cameras)
  • Bridge tool exposed under the Edit menu
  • New ground plane that helps with orienting the model in space
  • Separated orient model and coordinate system
  • Improved dashboard
  • RCM package file format that now contains the texture as well, so your mesh is all in one native Memento file
  • Memento now checks for low disk space situations, notifying the user when they don’t have enough space to save
  • Zooming into empty space now stops at certain distance away from the object. Zooming out Or ‘Fit-to-view’ will bring the model back into view
  • Improvement in 'waiting in queue' time for ReCap processing

Here’s a video overview of the product’s features:




If you’re interested in using Memento, I recommend watching this recorded webinar that talks about how best to take photos which, when processed, will result in a well-formed mesh:




Over the coming weeks an online gallery will be available from the product, too, showcasing some of the amazing projects people have used Memento to create.

March 04, 2015

Morgan at the Geneva Motor Show 2015

I headed over to Geneva yesterday afternoon for the first press day of this year’s Geneva Motor Show. I was there courtesy of our friends at the Morgan Motor Company who launched their new Aero 8 (it was actually nice being there as a guest, in contrast to last year’s collaboration). Here’s the launch video for this exciting car:



I love this video, but I did have one minor gripe for the Morgan team: why was Jon Wells, Morgan’s Head of Design, shaving a piece of clay rather than using Autodesk tools to develop the design? I was joking – I can understand why for the aesthetics of the video they’d rather focus on the analog aspects of the process – but they did explain they had in fact shot footage of our tools being used but it had ended up on the cutting room floor (or whatever the virtual equivalent is). Understandably they just had too much great material for a video lasting 2 minutes and change.

I was just happy to know they’d used Autodesk software to help design this gorgeous vehicle.

Aero 8

In case anyone’s wondering how much this car costs… and my apologies if we’re getting into “product placement” territory, but I’m sure people are curious…

The previously generation of the Aero retailed at around £130K, but Morgan has worked hard to streamline the BOM so the base Aero 8 model now costs a touch under £58K excluding VAT (£66.5K including VAT). It seems as though this is being sold in the US at $130K (including a number of options not included in the base UK model). Full prices are here.

Something I hadn’t realised: Morgan now has a partnership with The Balvenie (one of my favourite Scotch whiskeys). During the evening, James Buntin, Brand Ambassador for The Balvenie, led a tasting session on the Morgan stand. Here’s a video describing some background to The Balvenie’s collaboration with Morgan. It also does a great job of painting a picture of Morgan as a company, something I didn’t do justice in the blog post describing my own factory visit.



Struthers London were also present on the stand: they’re the London-based watchmakers who hand-make the co-branded Morgan watches. They launched the Aero 8 edition of the Struthers for Morgan line at the show.

So did I place my order for an Aero 8 while I was there? No, I didn’t, although I do expect a lot more people to be interested in the car given the new pricing model. The first 5 or so minutes of this class from AU 2015 might help explain why I’m not in the market for one…

 

March 02, 2015

AutoCAD and Prince of Persia

Every so often I get hit by a wave of computing nostalgia. This weekend it was a veritable tsunami triggered by the discovery that a number of old MS-DOS games are available to play online in your browser, including the seminal Prince of Persia. This game has a strong connection with AutoCAD, for me, so today I’m blogging about that.


Prince of Persia intro 

I first started working with AutoCAD while I was still at high school – it must have been around 1989. After a successful (but mind-numbingly boring) summer project at a local manufacturing company, converting their old engineering calculation routines from PET BASIC to GW-BASIC (which mostly involved typing code in from dot matrix print-outs, as far as I recall), I started reading the manuals for their new CAD system, AutoCAD R10. In due course I was creating LISP routines to automate the 2D drawing of nozzles and flanges for their plastic pressure vessels. This work set me up with some spending money for my later studies, and I have very fond memories of that period of my life.

Back when I got started, our drawing office used Compaq DeskPro 386 machines. These were great machines, not only for running AutoCAD for DOS but also for running early PC video games. The game that captured our collective imagination, from around 1990-91, was Prince of Persia. We would play it at any opportunity outside normal working hours.

This game was simply unbelievable. Until then I’d probably been most impressed, character animation-wise, by ZX Spectrum games such as Tir Na Nog and (to some degree) Heavy on the the Magick. But Prince of Persia blew these out of the water – the way the main character moved was incredibly realistic.


Prince of Persia 

So on Saturday afternoon, after playing the game for a few minutes – sadly my muscle memory for the gameplay has faded to nothing and I don’t have the patience to reacquire it – I decided to look into its origins. Fairly quickly I came across the original User’s Guide for the game, at which point I realised the game was developed in San Rafael, Marin County. Close to where I’d myself lived and worked for a number of years at Autodesk!

This struck me as pretty amazing: both pieces of software I was using at the time had been developed in the same Northern Californian town. (OK, in fairness AutoCAD was developed in Mill Valley and Sausalito during the period in which Prince of Persia was developed – Autodesk moved its HQ to San Rafael in 1994 – but this was definitely a topic worth looking into.)

The creator of Prince of Persia was Jordan Mechner, at the time a twentysomething from New York and recent Yale graduate. He’d previously written the popular Karateka game (not one I played myself) and ended up moving out to the Bay Area to work on a new title – which became Prince of Persia – for Brøderbund Software. These were the days when mainstream computer games were still often written by individual – albeit highly talented – programmers, something that’s now only really possible in the indie games scene or for rare breakouts such as Minecraft. Although interestingly – and perhaps this is also something that makes him exceptional – Jordan’s passion was cinema: he was also working hard during this period to succeed as a screenwriter.

Jordan kept a journal during his years developing the game, which you can buy from Amazon or access via a historical snapshot of his blog. The journal makes fascinating reading, talking about the animation process, going from videoed real-world footage – often of his younger brother, David – to individual frames. This rudimentary, rotoscoped motion-capture approach has arguably become the foundation for most 3D games, today.

Prince of Persia rotoscopy

Image copyright Jordan Mechner.

If you’re interested in seeing how this process translated into the game at various stages of development, be sure to check out the videos Jordan has posted on Vimeo.

Reading the journal was heavily nostalgic for me: Jordan describes the video camera’s battery dying while he was trying to pull himself upwards, hanging off the bus shelter at North San Pedro Road (the freeway exit I used to take to go home to the place we rented in Santa Venetia). Basically in an attempt to capture the frames for climbing up onto a ledge from a hanging position (which is understandably really hard to do).

Given the benefit of hindsight, the journal is also poignant in places. In a number of entries Jordan describes the beauty of Tina LaDeau, the 18-year old actress – and daughter of a colleague of Jordan’s at Brøderbund – who came in to be captured for the role of the princess. In a later entry she’s described by a friend as having “the ephemeral beauty of an 18-year-old.” That turned out to be all too true: Tina tragically passed away in August 2012 at the far-too-young age of 41.

Jordan talks about many of the struggles behind making Prince of Persia a success: for instance, it very nearly flopped due to lackluster marketing as well as having been developed initially for the dying Apple II platform (its success eventually came once it was ported to the IBM PC). He also paints a vivid picture of his life at the time, describing personal relationships and current events such as the first Gulf War and the 1989 earthquake. He mentions eating at Marin Joe’s and the Royal Thai as well as a picnic at the Marin County Civic Center duck pond – all of which I remember well from living in San Rafael in the early 2000s.

After reading the journal for the Prince of Persia years in one sitting – it was compulsive reading – I decided to check out the game’s source code. While fans have been sharing disassembled versions for years, Jordan rediscovered the source for the original Apple II game a few years ago and posted it to GitHub. While the code itself is mostly incomprehensible to me, I’m always fascinated to see the how programmers during the early part of the personal computing era overcame significant resource constraints to push boundaries and essentially create works of art, much as Ian Bell did when creating Elite. One example from Jordan’s diary: the “Shadowman” character was born because so much of the available memory was taken with the main character’s animation frames. XORing the frames with a single pixel offset allowed Jordan to introduce another character with near-zero memory overhead – and it took about two minutes to implement. Later on additional adversaries were able to be introduced into the game, but you have to admire the ingenuity behind this initial one.

Here’s the point at the end of level 4 where Shadowman gets created as you jump through a mirror. Totally cool.


Birth of Shadowman 
Prince of Persia’s success didn’t end with the initial game (although admittedly I personally haven’t followed its progress since): the first game spawned a successful series of games and even a movie – which is great for Jordan finally to have made the crossover from video games to cinema, having written the first screenplay and been an executive producer for Prince of Persia: The Sands of Time. Interestingly the whole Assassin’s Creed series was essentially a Prince of Persia spin-off (the initial game was being developed by Ubisoft as “Prince of Persia: Assassins”). Describing this game’s impact on the entertainment industry as significant would be quite an understatement.

So if you feel like playing this game – whether for the first time or, as I did, to remind you of good times – head on over to the Internet archive and take it for a spin. There are lots of other gems posted there, too: is your favourite MS-DOS game?

February 26, 2015

Enabling global commands on localized AutoCAD versions using .NET

Here’s a quick piece of code to finish up the week to complement what we saw earlier. The idea is that on localized AutoCAD versions this code will allow the user to enter English commands without needing the underscore prefix. The code works by detecting an “unknown” command and then attempting to execute it again after prefixing an underscore to launch a global command. Which may or may not work, of course, so we certainly need to set a flag to avoid descending into an infinite loop of commands being called while prefixed by an ever-expanding legion of underscores.

Aside from that we have some code disabling auto-correct and auto-complete, as these certainly get in the way of the code working properly. These aren’t strictly system variables, so I haven’t jumped through the hoops to make sure they get set back properly afterwards. So be aware these capabilities are likely to be disabled – and then require manual re-enabling – once you’ve run this code.

Here’s the C# code in question:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.Runtime;

 

namespace CommandHelper

{

  public class Commands

  {

    // Mutex to stop unknown command handler re-entrancy

 

    private bool _launched = false;

 

    [CommandMethod("CMDS")]

    public void CommandTranslation()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      // AutoComplete and AutoCorrect cause problems with

      // this, so let's turn them off (we may want to warn

      // the user or reset the values, afterwards)

 

      doc.Editor.Command(

        "_.-INPUTSEARCHOPTIONS",

        "_autoComplete", "_No",

        "_autocoRrect", "_No",

        ""

      );

 

      // Add our command prefixing event handler

 

      doc.UnknownCommand += OnUnknownCommand;

    }

 

    [CommandMethod("CMDSX")]

    public void StopCommandTranslation()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      // Remove our command prefixing event handler

 

      doc.UnknownCommand -= OnUnknownCommand;

    }

 

    async void OnUnknownCommand(

      object sender, UnknownCommandEventArgs e

    )

    {

      var doc = sender as Document;

 

      // Check to make sure we're not re-entering the handler

 

      if (doc != null && !_launched)

      {

        try

        {

          // Set the mutex flag and call our command

 

          _launched = true;

          await doc.Editor.CommandAsync("_" + e.GlobalCommandName);

        }

        catch { } // Let's not be too fussy about what we catch

        finally

        {

          // Reset our flag, now we're done

 

          _launched = false;

        }

      }

    }

  }

}

Be warned: this code won’t do anything useful on English versions of AutoCAD, as in that context local commands also happen to be global. So a) you won’t get an unknown command event when you call a global command and b) if you do, prefixing an underscore ain’t gonna help. :-)

You’re also going to need at least AutoCAD 2015 for this code to work, as it depends on Editor.CommandAsync().

Next week I’m officially back from vacation, so you can expect my – as it turns out uninterrupted – posting schedule to return to (i.e. carry on as) normal.

February 25, 2015

Adding a global keyword menu to AutoCAD using WPF – Part 2

After introducing this project in the last post, now it’s time to share some code. The project, as it currently stands, contains three source files: the first one relates to AutoCAD – it implements the various commands we’ll use to attach event handlers to tell us when to display (or hide) keywords and the other two files relate to the UI we’ll use to display them. We’re going to use an invisible window which has a child popup containing a listbox of our keywords.

Here’s the application in action – for now in English AutoCAD, as that’s what I have installed – helping us with the keywords during the PLINE and HATCH commands:


KeywordHelper

Now for the source. Let’s start with the AutoCAD-related C# file, which I’ve called keyword-helper.cs:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using System;

using System.Collections.Specialized;

 

namespace KeywordHelper

{

  public class Commands

  {

    // The keyword display window

 

    private KeywordWindow _window = null;

 

    // List of "special" commands that need a timer to reset

    // the keyword list

 

    private readonly string[] specialCmds = { "MTEXT" };

 

    [CommandMethod("KWS")]

    public void KeywordTranslation()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      var ed = doc.Editor;

 

      if (_window == null)

      {

        _window = new KeywordWindow();

        _window.Show();

        Application.MainWindow.Focus();

      }

 

      // Add our various event handlers

 

      // For displaying the keyword list...

 

      ed.PromptingForAngle += OnPromptingForAngle;

      ed.PromptingForCorner += OnPromptingForCorner;

      ed.PromptingForDistance += OnPromptingForDistance;

      ed.PromptingForDouble += OnPromptingForDouble;

      ed.PromptingForEntity += OnPromptingForEntity;

      ed.PromptingForInteger += OnPromptingForInteger;

      ed.PromptingForKeyword += OnPromptingForKeyword;

      ed.PromptingForNestedEntity += OnPromptingForNestedEntity;

      ed.PromptingForPoint += OnPromptingForPoint;

      ed.PromptingForSelection += OnPromptingForSelection;

      ed.PromptingForString += OnPromptingForString;

 

      // ... and removing it

 

      doc.CommandWillStart += OnCommandEnded;

      doc.CommandEnded += OnCommandEnded;

      doc.CommandCancelled += OnCommandEnded;

      doc.CommandFailed += OnCommandEnded;

 

      ed.EnteringQuiescentState += OnEnteringQuiescentState;

    }

 

    [CommandMethod("KWSX")]

    public void StopKeywordTranslation()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      var ed = doc.Editor;

 

      if (_window == null)

      {

        _window.Hide();

        _window = null;

      }

 

      // Remove our various event handlers

 

      // For displaying the keyword list...

 

      ed.PromptingForAngle -= OnPromptingForAngle;

      ed.PromptingForCorner -= OnPromptingForCorner;

      ed.PromptingForDistance -= OnPromptingForDistance;

      ed.PromptingForDouble -= OnPromptingForDouble;

      ed.PromptingForEntity -= OnPromptingForEntity;

      ed.PromptingForInteger -= OnPromptingForInteger;

      ed.PromptingForKeyword -= OnPromptingForKeyword;

      ed.PromptingForNestedEntity -= OnPromptingForNestedEntity;

      ed.PromptingForPoint -= OnPromptingForPoint;

      ed.PromptingForSelection -= OnPromptingForSelection;

      ed.PromptingForString -= OnPromptingForString;

 

      // ... and removing it

 

      doc.CommandWillStart -= OnCommandEnded;

      doc.CommandEnded -= OnCommandEnded;

      doc.CommandCancelled -= OnCommandEnded;

      doc.CommandFailed -= OnCommandEnded;

 

      ed.EnteringQuiescentState -= OnEnteringQuiescentState;

    }

 

    // Event handlers to display the keyword list

    // (each of these handlers needs a separate function due to the

    // signature, but they all do the same thing)

 

    void OnPromptingForAngle(

      object sender, PromptAngleOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForCorner(

      object sender, PromptPointOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForDistance(

      object sender, PromptDistanceOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForDouble(

      object sender, PromptDoubleOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForEntity(

      object sender, PromptEntityOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForInteger(

      object sender, PromptIntegerOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForKeyword(

      object sender, PromptKeywordOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForNestedEntity(

      object sender, PromptNestedEntityOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForPoint(

      object sender, PromptPointOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForSelection(

      object sender, PromptSelectionOptionsEventArgs e

    )

    {

      // Nested selection sometimes happens (e.g. the HATCH command)

      // so only display keywords when there are some to display

 

      if (e.Options.Keywords.Count > 0)

        DisplayKeywords(e.Options.Keywords);

    }

 

    void OnPromptingForString(

      object sender, PromptStringOptionsEventArgs e

    )

    {

      DisplayKeywords(e.Options.Keywords);

    }

 

    void OnCommandEnded(object sender, CommandEventArgs e)

    {

      _window.ClearKeywords(true);

    }

 

    // Event handlers to clear & hide the keyword list

 

    void OnEnteringQuiescentState(object sender, EventArgs e)

    {

      _window.ClearKeywords(true);

    }

 

    // Helper to display our keyword list

 

    private void DisplayKeywords(

      KeywordCollection kws

    )

    {

      // First we step through the keywords, collecting those

      // we want to display in a collection

 

      var sc = new StringCollection();

      if (kws != null && kws.Count > 0)

      {

        foreach (Keyword kw in kws)

        {

          if (kw.Enabled && kw.Visible && kw.GlobalName != "dummy")

          {

            sc.Add(kw.LocalName); // Expected this to be GlobalName

          }

        }

      }

 

      // If we don't have keywords to display, make sure the

      // current list is cleared/hidden

 

      if (sc.Count == 0)

      {

        _window.ClearKeywords(true);

      }

      else

      {

        // Otherwise we pass the keywords - as a string array -

        // to the display function along with a flag indicating

        // whether the current command is considered "special"

 

        var sa = new string[kws.Count];

        sc.CopyTo(sa, 0);

 

        // We should probably check for transparent/nested

        // command invocation...

 

        var cmd =

          (string)Application.GetSystemVariable("CMDNAMES");

        _window.ShowKeywords(

            sa, Array.IndexOf(specialCmds, cmd) >= 0 //, append

          );

      }

    }

 

    internal static void launchCommand(string cmd)

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      if (doc == null)

        return;

 

      doc.SendStringToExecute(

        "_" + cmd + " ", true, false, true

      );

    }

  }

}

I was surprised that the English version of keywords on localized versions were accessible via the LocalName – rather than GlobalName – property. But apparently that’s how it works.

Next we have the XAML file for our KeywordWindow which, while invisible, contains the popup we’ll use to display the keywords. The file is called KeywordWindow.xaml.

<Window

  x:Class="KeywordHelper.KeywordWindow"

  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

  Title="KeywordWindow" Height="0" Width="0"

  WindowStyle="None" ShowInTaskbar="False" AllowsTransparency="True"

  Loaded="Window_Loaded">

  <Window.Background>

    <SolidColorBrush Opacity="0" Color="White"/>

  </Window.Background>

  <Grid>

    <Popup Name="KeywordPopup" Placement="Custom">

      <ListBox x:Name="Keywords" Width="100" Height="auto">Keywords

        <ListBox.ItemContainerStyle>

          <Style

           TargetType="{x:Type ListBoxItem}"

           BasedOn="{StaticResource {x:Type ListBoxItem}}">

            <EventSetter

             Event="MouseDoubleClick"

             Handler="ListBoxItem_MouseDoubleClick"/>

          </Style>

        </ListBox.ItemContainerStyle>

      </ListBox>

    </Popup>

  </Grid>

</Window>

And finally the C# code-behind, KeywordWindow.xaml.cs:

using System;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Controls.Primitives;

using System.Windows.Threading;

 

namespace KeywordHelper

{

  ///<summary>

  /// Interaction logic for KeywordWindow.xaml

  ///</summary>

 

  public partial class KeywordWindow : Window

  {

    private DispatcherTimer _t;

    private DateTime _lastOpened;

    private bool _special;

 

    public KeywordWindow()

    {

      InitializeComponent();

      KeywordPopup.CustomPopupPlacementCallback =

          new CustomPopupPlacementCallback(PlacePopup);

      _t = null;

    }

 

    public void ShowKeywords(

      string[] keywords, bool special = false, bool append = false

    )

    {

      // Store the flag in a member variable so we can access it

      // from a lambda event handler

 

      _special = special;

 

      // Get the listbox contents

 

      var items = ((ListBox)KeywordPopup.Child).Items;

 

      // The first test of difference is whether the number of items

      // is different

 

      if (append)

      {

        foreach (var kw in keywords)

        {

          items.Add(kw);

        }

      }

      else

      {

        bool different = keywords.Length != items.Count;

        if (!different)

        {

          // If lists are the same length, check the contents

          // item by item

 

          for (int i = 0; i < items.Count; i++)

          {

            var kw = keywords[i];

            var item = (string)items[i];

            if (String.Compare(kw, item) != 0)

            {

              different = true;

              break;

            }

          }

        }

 

        // If the items are different, let's clear the list and

        // rebuild it

 

        if (different)

        {

          items.Clear();

          foreach (var kw in keywords)

          {

            items.Add(kw);

          }

        }

      }

      KeywordPopup.IsOpen = true;

 

      // We're going to use a timer to close the popup in case

      // it isn't closed by one of the various callbacks we have

      // in place

 

      if (_t == null)

      {

        // Choose an interval of 2 seconds

 

        var ts = new TimeSpan(TimeSpan.TicksPerSecond * 2);

        _t = new DispatcherTimer { Interval = ts };

        _t.Tick += (s, e) =>

          {

            // If 2s or more has elapsed since the last popup

            // was displayed, close it

 

            if (_special && (DateTime.Now - _lastOpened >= ts))

              KeywordPopup.IsOpen = false;

          };

        _t.Start();

      }

 

      // Record when the latest popup was displayed

 

      _lastOpened = DateTime.Now;

    }

 

    public void ClearKeywords(bool hide)

    {

      // Optionally hide the popup

 

      KeywordPopup.IsOpen = !hide;

 

      // Clear the keyword contents

 

      ((ListBox)KeywordPopup.Child).Items.Clear();

    }

 

    private void ListBoxItem_MouseDoubleClick(

      object s, System.Windows.Input.MouseButtonEventArgs e

    )

    {

      // When an item is double-clicked, simply send it to the

      // command-line with an underscore prefix

 

      var item = (ListBoxItem)s;

      Commands.launchCommand((string)item.Content);

    }

 

    public CustomPopupPlacement[] PlacePopup(

      Size popupSize, Size targetSize, Point offset

    )

    {

      // We want to place the popup relative to the AutoCAD

      // main window

 

      var win =

        Autodesk.AutoCAD.ApplicationServices.Application.MainWindow;

 

      // Calculate the bottom-right of the popup - both x and y -

      // relative to the location of the parent window (this)

 

      var x =

        win.DeviceIndependentLocation.X +

        win.DeviceIndependentSize.Width - this.Left;

 

      // 33 is the height of the bottom window border/status bar

 

      var y =

        win.DeviceIndependentLocation.Y +

        win.DeviceIndependentSize.Height - this.Top - 33;

 

      // The above values need scaling for DPI

 

      var s =

        Autodesk.AutoCAD.Windows.Window.GetDeviceIndependentScale(

          IntPtr.Zero

        );

 

      // Get our scaled position, taking into account the

      // size of the popip

 

      var p =

        new System.Windows.Point(

          (int)(x * s.X - popupSize.Width),

          (int)(y * s.Y - popupSize.Height)

        );

 

      // Return that position as our custom placement

 

      return new CustomPopupPlacement[] {

        new CustomPopupPlacement(p, PopupPrimaryAxis.Vertical)

      };

    }

  }

}

So far I’ve had to code a few caveats for command behaviour: the HATCH command displays a selection prompt – without keywords – within a point prompt (which does have keywords). So I make sure we don’t clear the menu, in this case. Then there’s the MTEXT command, which performs a point selection for the window area – with keywords – before displaying its IPE (in-place editor). We use a timer to close the popup in the case that 500ms elapses without a request for keywords to be displayed. I have no doubt other commands will present other quirks, but we’ll address those as they crop up.

There’s still some work to do to hide the popup when AutoCAD is minimized – as well as to make sure our invisible window doesn’t appear in the Alt-Tab program switcher – but that’s left for the reader (or for another day, we’ll see).

One additional requirement I do want to address – probably in the next post – is to automatically prefix underscores on unknown commands, to see if someone has entered an English command by mistake on a localization version of AutoCAD.

February 23, 2015

Adding a global keyword menu to AutoCAD using WPF – Part 1

I’m up in the mountains, supposedly on vacation, but as one of our children woke up with a fever, I’m skipping the morning session on the slopes to stay home with him. Which gives me the chance to start writing up a little project I’ve been working on for our Localization team.

Here’s the idea… apparently it’s relatively common in certain countries for AutoCAD users to learn the product in English but then end up working with a localized version of the software. While it’s always possible to use global commands and keywords by prefixing an underscore, it’s not always something people remember to do. Which can understandably leads to frustration.

Our Localization team is keen to find a way to help these users, such as by streamlining their ability to use global commands and keywords in a localized AutoCAD product. One suggestion was to build an app that displays a list of the global keywords they can use either as an aide-memoire or to launch the keyword itself.

This seemed like a fun little project, so I ended up putting together an initial version over the weekend. Here are a few comments on what I ended up building:

1. An app that uses WPF to display a Popup in the bottom right of the AutoCAD window.

Keyword popup

2. The list gets populated by global keywords that AutoCAD prompts for. Luckily there are events that provide us with this information.

3. When an item in the list is double-clicked the keyword gets sent to the command-line – with the underscore prefix, of course.

4. Some commands need to be treated as “special”: as they request input but then don’t complete straightaway, we need to use a timer to close the popup after a certain interval elapses. For example, the MTEXT command prompts for a window for the text area, but then launches the in-place editor (IPE) rather than completing or having AutoCAD become quiescent. We want to close our keyword popup while the IPE is active.

That’s basically the scope of the project – for now, at least. As it’s so far possible to implement using published APIs, I’ll go ahead and share the code during the course of this week.

February 20, 2015

Styling your HTML5 progress meter with CSS3

After yesterday’s fun with creating an HTML5-based progress meter for AutoCAD, today we’re going to have some more fun styling it with CSS.

To recap, here’s the progress meter that comes “out of the box”, with the default styling from Chromium on Windows.


Original progress meter

The first thing we need to do for our various changes is to use CSS to disable the default styling, at which point we can then use CSS to override it.

      progress {

        width: 100%;

        -webkit-appearance: none;

      }

Here’s how our progress meter looks when unstyled:


Unstyled progress meter

Now that it’s stripped bare, we can apply some styling to make it look as we want. For inspiration I started with some code from this page:

      progress[value]::-webkit-progress-value {

        background-image:

          -webkit-linear-gradient(

            -45deg,

            transparent 33%, rgba(0, 0, 0, .1) 33%,

            rgba(0, 0, 0, .1) 66%, transparent 66%

          ),

          -webkit-linear-gradient(

            top,

            rgba(255, 255, 255, .25),

            rgba(0, 0, 0, .25)

          ),

          -webkit-linear-gradient(left, #09c, #f44);

 

        border-radius: 2px;

        background-size: 35px 20px, 100% 100%, 100% 100%;

      }

Which makes it nice and colorful, but for some reason makes me think of licorice.


Colourful progress meter

So to see what difference certain tweaks make, I changed the first colour in the 2nd “-webkit-linear-gradient” to #ccc and the second to #fff. This washed the colours away:


Monochrome progress meter

For the next iteration I borrowed from this page, which helped add some warmth back in:


Nice progress meter

From there I decided to try the blue-within-blue (yes, that’s a Dune reference ;-) styling used by AutoCAD’s standard progress meter. I didn’t manage to get the exact colours, but they look OK:


Blue progress meter

Then I decided to round off the corners, which is a simple matter of setting the border-radius to 50px in the main progress meter style but also to add a style for the background doing much the same:

      progress::-webkit-progress-bar {

        background: gray;

        border-radius: 50px;

        padding: 0px;

        box-shadow: 0 1px 0px 0 rgba(255, 255, 255, 0.2);

      }


Blue progress meter with rounded corners

At this point things were shaping up nicely. The final touch was to replicate the same kind of barber’s pole striping to the background, making it look like a striped reservoir filling with water. Overall it’s an effect I like a lot. And you can very easily tweak this to better suit your own application’s requirements, of course.


Final progress meter

Here’s the completed CSS which can be pasted into the <style> element in yesterday’s post (or placed in its own file and referenced from the HTML page, if you prefer that approach):

      progress[value]::-webkit-progress-value {

        background-image:

          -webkit-linear-gradient(

            -45deg,

            transparent 33%, rgba(0, 0, 0, .1) 33%,

            rgba(0, 0, 0, .1) 66%, transparent 66%

          ),

          -webkit-linear-gradient(

            top,

            rgba(255, 255, 255, .25),

            rgba(0, 0, 0, .25)

          ),

          -webkit-linear-gradient(left, #335EC4, #1F71F4);

 

        border-radius: 50px;

        background-size: 35px 20px, 100% 100%, 100% 100%;

      }

      progress::-webkit-progress-bar {

        background-image:

          -webkit-linear-gradient(

            -45deg,

            transparent 33%, rgba(0, 0, 0, .1) 33%,

            rgba(0, 0, 0, .1) 66%, transparent 66%

          ),

          -webkit-linear-gradient(

            top,

            rgba(255, 255, 255, .25),

            rgba(0, 0, 0, .25)

          ),

          -webkit-linear-gradient(left, #333, #666);

        border-radius: 50px;

        background-size: 35px 20px, 100% 100%, 100% 100%;

      }

      body {

        overflow: hidden;

        width: 98%;

        height: 98%;

      }

      hidden {

        display: none;

      }

      progress {

        width: 100%;

        -webkit-appearance: none;

      }

      .td-center  {

        text-align: center;

      }

      .td-right {

        text-align: right;

      }

      .center-div  {

        width: 100%;

        padding: 25% 0;

      }

      div {

        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

        font-size: large;

        font-weight: bold;

      }

Next week I’m hitting the slopes with my family as the kids are off school, the reason I’m not able to make it across to REAL 2015. Hopefully I’ll get the chance to post to this blog at least once, but there’s some chance I’ll end up taking the whole week off.

February 19, 2015

Creating your own AutoCAD progress meter using HTML5 and JavaScript

This week I’ve spent quite a bit of time looking into future API features. For one of them I needed to create a progress meter, and thought to myself “why not create one in HTML5?” And as it’s nothing specific to a future product release, I decided to go ahead and post it now.

For context, here’s the way AutoCAD’s standard progress meter currently looks, displayed using the code from this previous post:


Standard progress meter

So why would you go head and create your own progress meter? A few different reasons come to mind… yes, AutoCAD has its own, but perhaps you want something more visible (not tucked away in the bottom right corner of the application frame), pausable or more explicitly cancellable. Or perhaps you just want to style it differently – something we’ll take a look at in tomorrow’s post.

Even if you don’t want to create your own progress meter, the techniques shown in today’s post will be valuable if you want to create an HTML UI that’s tightly integrated with AutoCAD.

Overall the code is fairly straightforward: as with most HTML5 projects I’ve embarked upon, I ended up spending more time than expected to get the vertical alignment on the page looking good (mainly because the “old” approach of using tables with the valign attribute no longer works in HTML5… apart from understanding how vertical-align now works, there are still a number of approaches for managing vertical space).

The other big sticking point was around getting the various page elements to display consistently. For instance, very often the caption wouldn’t display the first time the dialog was shown in a session… I hit my head against this for ages. In the end I found that having the HTML page call back into our .NET app to say “the page has loaded, we’re ready to roll” was the cleanest approach.

Here’s the progress meter in action, running to completion. You’ll notice the dialog is quite big… that’s the minimum size of a modeless dialog. We could also use another modeless container, of course – such as an HTML palette or even a non-DWG document window – but for this scenario a modeless window made most sense.


Progress meter - completed

And here it is when it’s cancelled partway through:


Progress meter - cancelled

Here’s the HTML code:

<!doctype html>

<html>

  <head>

    <title>Progress</title>

    <style>

      body {

        overflow: hidden;

        width: 98%;

        height: 98%;

      }

      hidden {

        display: none;

      }

      progress {

        width: 100%;

      }

      .td-center  {

        text-align: center;

      }

      .td-right {

        text-align: right;

      }

      .center-div  {

        width: 100%;

        padding: 25% 0;

      }

      div {

        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;

        font-size: large;

        font-weight: bold;

      }

      </style>

    <script

      src="http://app.autocad360.com/jsapi/v2/Autodesk.AutoCAD.js">

    </script>

    <script type="text/javascript">

      var progbar, limit, loaded = false;

 

      function updateProgress(value) {

        progbar.max = limit;

        progbar.value = value;

        progbar.getElementsByTagName('span')[0].innerHTML =

          Math.floor((100 / limit) * value);

      }

 

      function displayValue(prop, val) {

 

        if (prop == "progress") {

          updateProgress(val);

        }

        else if (prop == "limit") {

          limit = val;

        }

        else {

          // Display the specified value in our div for the specified

          // property

 

          var div = document.getElementById(prop);

          if (div != null) {

            if (typeof val === "string") {

              div.innerHTML = val;

            }

            else {

              div.innerHTML = val.toFixed(2);

            }

          }

        }

      }

 

      function showControls(show) {

        var prog = document.getElementById("progress");

        var butt = document.getElementById("cancel");

        if (show) {

          prog.classList.remove("hidden");

          butt.classList.remove("hidden");

        }

        else {

          prog.classList.add("hidden");

          butt.classList.add("hidden");

        }

      }

 

      function start() {

        showControls(true);

      }

 

      function ready() {

        return loaded;

      }

 

      function stop() {

        showControls(false);

        self.close();

      }

 

      function updateControls(args) {

 

        var obj = JSON.parse(args);

 

        var propName = obj.propName;

        var propVal = obj.propValue;

 

        // If the string represents a double (we test using

        // a RegExp), round it to 2 decimal places

 

        var val = 0.0;

        var found = false;

 

        if (typeof propVal === "number") {

          val = propVal;

          found = true;

        }

        else if (typeof propVal === "string") {

          var re = /^[+-] ?[0-9]{0,99}(?:\.[0-9]{1,99})?$/;

          if (propVal.match(re)) {

            val = parseFloat(propVal);

          }

          else {

 

            // Otherwise just display the string

 

            displayValue(propName, propVal);

          }

        }

        if (found) {

          displayValue(propName, val);

        }

      }

 

      // Shaping layer extensions

 

      function pageLoaded() {

        var jsonResponse =

          exec(

            JSON.stringify({

              functionName: 'Ready',

              invokeAsCommand: false,

              functionParams: undefined

            })

          );

        var jsonObj = JSON.parse(jsonResponse);

        if (jsonObj.retCode !== Acad.ErrorStatus.eJsOk) {

          throw Error(jsonObj.retErrorString);

        }

        return jsonObj.result;

      }

 

      function cancelOperation() {

        var jsonResponse =

          exec(

            JSON.stringify({

              functionName: 'CanOp',

              invokeAsCommand: false,

              functionParams: undefined

            })

          );

        var jsonObj = JSON.parse(jsonResponse);

        if (jsonObj.retCode !== Acad.ErrorStatus.eJsOk) {

          throw Error(jsonObj.retErrorString);

        }

        return jsonObj.result;

      }

    </script>

  </head>

  <body>

    <table class="center-div">

      <tr>

        <td class="td-center">

          <div id="caption">&nbsp;</div>

        </td>

      </tr>

      <tr>

        <td class="td-right" width="100%">

          <progress id="progress" class="hidden"

                    value="0" max="100">

            <span>0</span>%

          </progress>

        </td>

        <td class="td-right">

          <button id="cancel" class="hidden"

                  onclick="cancelOperation();">

            Cancel

          </button>

        </td>

      </tr>

      <tr>

        <td class="td-center">

          <div id="extra"></div>

        </td>

      </tr>

    </table>

    <script type="text/javascript">

      (function () {

        registerCallback("updval", updateControls);

        registerCallback("start", start);

        registerCallback("stop", stop);

        progbar = document.getElementById('progress');

 

        document.onkeydown = function (evt) {

          evt = evt || window.event;

          if (evt.keyCode == 27) {

            cancelOperation();

          }

        };

        window.onload = pageLoaded;

      })();

    </script>

  </body>

</html>

I created a C# class that mimics the ProgressMeter protocol – in fact it derives from the standard ProgressMeter class, adding a few additional capabilities – to make it easier to switch between the two, as needed. You won’t want to put yours in the Autodesk.AutoCAD.Runtime namespace – I simply did so for my own convenience.

using Autodesk.AutoCAD.ApplicationServices;

using System;

using System.IO;

using System.Reflection;

using System.Runtime.InteropServices;

 

namespace Autodesk.AutoCAD.Runtime

{

  // Use the standard ProgressMeter protocol

 

  public class ProgressMeterHtml : ProgressMeter

  {

    private static bool _ready;

    private static bool _cancelled;

    private int _pos;

 

    [DllImport(

      "AcJsCoreStub.crx", CharSet = CharSet.Auto,

      CallingConvention = CallingConvention.Cdecl,

      EntryPoint = "acjsInvokeAsync")]

    extern static private int acjsInvokeAsync(

      string name, string jsonArgs

    );

 

    // Called by Progress.html when the page has loaded

 

    [JavaScriptCallback("Ready")]

    public string ReadyToStart(string jsonArgs)

    {

      _ready = true;

      return "{\"retCode\":0}";

    }

 

    // Called by Progress.html to cancel the operation

 

    [JavaScriptCallback("CanOp")]

    public string CancelOperation(string jsonArgs)

    {

      _cancelled = true;

      return "{\"retCode\":0}";

    }

 

    // Constructor

 

    public ProgressMeterHtml()

    {

      // Initialize static members

 

      _ready = false;

      _cancelled = false;

 

      // Load Progress.html from this module's folder

 

      var asm = Assembly.GetExecutingAssembly();

      var loc =

        Path.GetDirectoryName(asm.Location) + "\\progress.html";

 

      Application.ShowModelessWindow(new System.Uri(loc));

 

      // Wait for the page to load fully to avoid refresh issues

 

      while (!_ready)

      {

        System.Threading.Thread.Sleep(500);

        System.Windows.Forms.Application.DoEvents();

      }

 

      // Initialize our progress counter

 

      _pos = 0;

    }

 

    // Start the progress meter without a caption

 

    public override void Start()

    {

      acjsInvokeAsync("start", "{}");

    }

 

    // Start the progress meter with a caption

 

    public override void Start(string displayString)

    {

      Start();

      Caption(displayString);

    }

 

    // Set the limit

 

    public override void SetLimit(int max)

    {

      SendProperty("limit", max);

    }

 

    // Advance the progress meter

 

    public override void MeterProgress()

    {

      SendProperty("progress", ++_pos);

    }

 

    // Stop the progess meter, whether it's finished or the

    // operation has been cancelled

 

    public override void Stop()

    {

      Caption(_cancelled ? "Cancelled" : "Completed");

      AdditionalInfo(" ");

 

      // We'll wait for a second and then close the dialog

 

      System.Threading.Thread.Sleep(1000);

      acjsInvokeAsync("stop", "{}");

    }

 

    // Cancels the current operation

 

    public void Cancel()

    {

      _cancelled = true;

    }

 

    // Returns whether the operation has been cancelled

 

    public bool Cancelled

    {

      get { return _cancelled; }

    }

 

    // Sets the dialog's caption

 

    public void Caption(string displayString)

    {

      SendProperty("caption", displayString);

    }

 

    // Sets the additional information text

 

    public void AdditionalInfo(string displayString)

    {

      SendProperty("extra", displayString);

    }

 

    // Helper function to set a property in the HTML page

 

    private void SendProperty(string name, object val)

    {

      bool enclose = val.GetType() == typeof(String);

      var args =

        "{\"propName\":\"" + name + "\",\"propValue\":" +

        (enclose ? "\"" : "") + val.ToString() +

        (enclose ? "\"" : "") + "}";

      acjsInvokeAsync("updval", args);

    }

  }

}

The calling code is almost identical to what we saw in the original ProgressMeter post:

using Autodesk.AutoCAD.Runtime;

using System.Windows.Forms;

 

namespace ProgressMeterTest

{

  public class Cmds

  {

    [CommandMethod("PB")]

    public void ProgressBarHtml()

    {

      const int ticks = 50;

 

      var pm = new ProgressMeterHtml();

      pm.Start("Testing Progress Bar");

      pm.AdditionalInfo("Show something extra");

      pm.SetLimit(ticks);

 

      // Now our lengthy operation

 

      for (int i = 0; i < ticks; i++)

      {

        System.Threading.Thread.Sleep(50);

 

        // Increment progress meter...

 

        pm.MeterProgress();

        Application.DoEvents();

 

        if (pm.Cancelled)

          break;

      }

      pm.Stop();

    }

  }

}

That’s it for today’s post. Tomorrow we’ll take a look at styling the HTML to see what we can do with it.

February 16, 2015

An interview with Mike Aldred about the Dyson 360 Eye

Back in September, Dyson announced their long-awaited entry into the robo-vacuum market with the Dyson 360 Eye.




I was delighted to see an old friend of mine, Mike Aldred, in one of the launch videos:




Mike and I were undergraduates at the University of Kent at Canterbury (UKC) together, back in the ‘90s. I was studying European Computer Science (which meant I was on a 4-year degree programme with the 3rd year spent studying Computer Science in Paris) while Mike was studying Computer Systems Engineering. I vaguely recall having common classes together with Mike, although as Mike started at UKC the year after me it’s quite possible I’m mis-remembering.

The main reason I knew Mike at all was actually because we were both members of the university’s hockey club (yes, in England we just call it hockey – rather than field hockey – as we don’t really play ice hockey). Mike was the university’s top goalkeeper for a number of years, and while Mike played at the local grown-up hockey club at weekends we did play together on the same mid-week team from time to time. Anyway, over the years Mike and I got to know each other quite well.

UKC Hockey Club Men's 1st 1994-1995

Fast forward nearly twenty years: perhaps surprisingly, we’re both at the same companies we joined straight from University. In my case I joined Autodesk in 1995, in Mike’s case he joined Dyson in 1998 after completing his PhD. Seeing Mike on YouTube brought back lots of good memories, so I thought I’d get back in touch to see whether he’d be interested in talking to us about the Dyson 360 Eye. The interview doesn’t relate directly to Autodesk, as such, but as many of you are aware I’m personally very interested in computer vision and robotics and I’m sure this blog’s readership will also find what Mike has to say to be of great interest.

On a side note, while I’ve had a robotic lawnmower for 8 years, I haven’t yet found a robotic vacuum cleaner that’s interesting enough for me to take the plunge. It’s just possible that the Dyson 360 Eye – with its room mapping technology and cyclonic motor – is that device. Time will tell.

Here’s the interview, with my questions in bold and Mike’s responses in normal text...

How did you end up joining Dyson?

Whilst in my final year of my PhD at the University of Kent my supervisor was approached by Dyson who had decided they wanted to create a new robotics group at the company. They had visited many universities across the country but our practical approach and desire to build machines rather than live in the theoretical domain meant we were the ideal candidates. I started at Dyson in April of 1998 and spent the first 4 months writing behavioural software for the robot during the day and then writing up my thesis in the evening... a tough introduction to the world of work having spent six years at uni!

What was the subject of your doctoral thesis?

The title was "Visual methods for robot navigation". It covered many aspects of vision based robotics including work on a vision-based maze mouse but the main focus was on producing a vision system for a robot that could learn for itself to identify where the important bits of information within an image are. It was based on biologically inspired neural networks and enabled the system to automatically break a scene down into its basic parts e.g. "floor", "walls", "door", etc.

What roles have you held at Dyson, over the years?

Initially I was a software developer on the robot projects eventually progressing to senior and then on to robotics manager and electronics research manager. I am now electronics category lead, robotics which means I am responsible for the delivery of all electronics related elements of robotics projects (hardware, firmware, software, algorithms, sensing systems) on time, to cost and to spec.

How big a team has been working on the Dyson 360 Eye project?

The team has varied in size over the years. It started at 6 people and has averaged ~20. In recent years it has continued to grow and is currently 45 people. Over the coming year the intent is to increase the team further by around 20 heads.

I think it’s safe to say that people have been impatient for Dyson’s entry into the robo-vacuum market, especially after the DC06 was announced but never released. What happened to the DC06 project?

We got to a point where it worked as a robot but did not meet Dyson's standards as a product. It was too complex (containing 3 processors and 70 sensors!) and the cleaning performance was not quite where we wanted it. We felt we needed to go back to the drawing board.

Did your team feel much pressure to deliver a product sooner?

We continuously feel a pressure to deliver products but the major pressure has always been to deliver the right product. James [Dyson] will never allow a product to be released which is not better than other products and offers a genuine solution to a problem. Many times we had got to a stage where we had something that could have been released but it simply was not good enough to be called a Dyson.

What convergence of technology advancements has led to the Dyson 360 Eye being viable where the DC06 was not?

When we went back to the drawing two major advances made the difference.

1) We started using vision as our primary sensing system. We had recognised that if a robot is going to be able to behave in a truly methodical manner in real world homes it would have to have a very good understanding of its current location, where it had been and where it still needed to go. DC06 had relied on too many sensors making it impractical and even these did not provide sufficient information. We took the decision to use vision, and not just a standard camera but a panoramic image. This would allow the robot to see all around it in a single image, using a single sensor, which sounds sensible, but when you bear in mind that vision was not common at that time (mobile phones didn't even have cameras at that point) it was a very bold decision!

We started working with Andrew Davison at Imperial college to utilise his vision-based SLAM (Simultaneous Localisation And Mapping) system and take it out of the lab and into the real world. By the time the product was working reliably we had added around five times the number of algorithms on top of the basic SLAM systems in order for us to be able to deal with all of the dynamic complexity and edge cases that the real world throws at you.

2) Dyson developed their own small, powerful and efficient motor ideal for a robot and our handheld machines. The motor is a success story in its own right. It spins at 78,000 rpm and the company has invested £160m in its development over 15 years.

What is it that you feel separates the Dyson 360 Eye from other robo-vacuums?

It is a vacuum cleaner that is robotic, not the other way around. Being a Dyson it has to clean... we concentrated as much on the pick-up, cyclonic separation, and everything to do with getting dirt off the floor as we did on the robotic element. We were not going to produce a gimmick or toy, this had to be something that its owner will use to clean their house.

Robotics is a really interesting field. You guys are using advanced computer vision algorithms, for instance. How much of the tough stuff gets done in software vs. hardware?

Almost everything is done in software. We do have a camera which is very capable in low light to enable us to handle all kinds of lighting conditions, but other than that pretty much all of the work is about taking large amounts of image data and then doing a lot of analysis. As with any mass market product cost is critical, so we have a pretty modest processor which has meant significant amounts of work to take complex algorithms and optimise them so we don’t blow the processing budget.

How closely do you need to follow ongoing robotics research? How much time do you personally need to spend keeping up-to-date?

Not as closely as I would personally like, but I have a very talented team who are very up to speed with everything robotics. We ensure that team members attend conferences around the world and encourage them to read papers and journals to stay at the cutting edge. I have always found it a very difficult domain to keep on top of because of its breadth. In reality we have a group of highly talented engineers, some of whom have very specific expertise, and whose combined knowledge covers most of the field. I personally don't think there are that many people in the world who can honestly claim to be experts in robotics as a whole... it's simply too large a subject!

Now must be a really exciting time to be a roboticist. What kind of advances to do you expect to happen in the industry over the coming few years?

Robotics has always been exciting and I count myself very lucky to come to work every day to look at how we can make our machines better, whether that be making them more reliable, more capable, more cost effective... as an engineer I love every kind of challenge. However, my experience of developing robots over the last 16 years has taught me some bitter lessons in reality. Looking at the cutting edge work being done in academia it would be easy to believe that we are on the cusp of a robot revolution. If these technologies are going to become prevalent, though, they need to be able to work robustly, reliably, repeatably and – perhaps most importantly – safely in the real world. We implemented a basic system from scratch in a couple of years. It then took 6-8 years more, and extensive home trialling, to get the robot to work to a high standard in the huge variety of homes which it is required to work in around the globe. We faced challenges with power budgets, sensing systems, regulations and approvals, cost... you name it, we had issues to overcome.

I am very excited about future products and think that robotics can eventually make genuine differences to everyone’s day to day life. Initially they will be small. I don't think someone will suddenly produce a humanoid servant who relives us of all of our chores (in fact I suspect this may never happen). But step by step we will gradually see more and more of the things around us becoming automated. I think this gradual introduction is also important for user acceptance. A key factor for a robot to be able to do its job is for its user to trust it and to be confident enough to leave to "do its thing". I think a big bang, in your face, humanoid man servant would probably face problems with acceptance and its introduction would be fraught with the need to "educate the user". However, a more gradual introduction of robotics into our way of life should reduce the fear factor and mean that as robots become increasingly capable customers are already asking for the new functions rather than being fearful or suspicious of them.

I understand you may be biased, but do you have a favourite Dyson product?

For me it was the CR01 contra-rotating washing machine. It contained two drums which rotated in opposite directions. This means the washing was "agitated" more than with a standard machine where the clothes just get compressed against the edge of the drum. This "chaotic" motion meant dirt was released more quickly and effectively, resulting in a better clean in a much shorter time. Add in the fact it had the biggest drum of any machine at the time and a very clever auto-balancing system which meant the machine barely moved even when rotating at 1400rpm and you had a perfect example of "Dyson thinking". Unfortunately it was too expensive to make and the company ceased manufacture in 2004. I had my machine for nearly 8 years and have never found anything that comes close to it since.

That’s the end of the interview. Many thanks to Mike for taking the time to share this information with us!

To finish up, here’s a video sharing some additional technical details about the Dyson 360 Eye.


February 13, 2015

Reading from mobile devices: time for a redesign

It’s been several years since this blog received a redesign. I’ve tweaked the format from time to time, adding social buttons and various other widgets, but it’s starting to show its age, especially as more and more people are browsing from devices with smaller form factors.

The horror that is TtIf on mobileAnd this blog does look pretty bad on mobile devices…

The good news is that Typepad does have a number of responsive design templates which apparently look good on a variety of devices with a variety of form factors/resolutions. I’ve submitted a request to our creative team for an updated design, asking that whatever they conjure up be based on one of those.

To get a sense for where we are today, I had a quick check with Google Analytics to see what platforms people have used to read this blog over the last year or so.

Perhaps unsurprisingly, 93% are browsing from desktop devices (i.e. traditional PCs/Macs, whether desktops, notebooks or netbooks), followed by 4% from mobile and 3% from tablets. (If I look at just the last month, desktop usage has gone down to 92% while mobile has risen to 5%.)

The level of desktop use is high – as is the bounce rate for mobile users – because a) this blog is most likely to be used by developers who are working on desktop platforms and b) the blog looks pretty horrendous on mobile devices. I mean, it looks more or less the same on mobile devices as it does on the desktop, which these days frankly isn’t good enough.

So while I know the current situation skews the data towards browsing from the desktop, I don’t know by what extent. To help build a case for a mobile-friendly redesign – which I want to make happen in any case, if I’m honest – I’ve created my first ever web-based poll. Please take the time to click on one of the options below – it’ll really help me understand whether there’s an unfulfilled need out there.

The poll is intended to measure readers’ aspirations, rather than what happens today. For instance, if you like to read web-sites – whether or not you want to actually browse code – on your phone or tablet on your commute into the office, please choose “Multiple device types”, below. It may not be reality today – at least not as far as this blog is concerned – but do think about whether it’s something you’d expect to find beneficial at some point.



Thanks in advance for the feedback. I’ll certainly keep you posted on the results of the redesign.

Feed/Share

10 Random Posts