Main > Software

July 19, 2013

Converting CAD files to STL using FreeCAD on the Command Line

I'm working on a client project where we are receiving hundreds of CAD submissions. We need to sort, filter, and rank these submissions in order to ease analysis. Ultimately, these submissions will be 3D printed. I needed an automated way to convert the files from STEP or IGS CAD format to STL on the command line.

I discovered that the venerable FreeCAD has a Python-based command line interpreter. After a few bumps, I was able to drive it to create a simple tool to convert my files.

First, I had to add the dynamic library loading path of FreeCAD to my OSX command line environment:

$ export DYLD_FALLBACK_LIBRARY_PATH=\
/Applications/FreeCAD.app/Contents/Frameworks/lib:\
$DYLD_FALLBACK_LIBRARY_PATH

Next, I wrote this little script. I figured out the syntax by first recording some macros in FreeCAD.

#!/Applications/FreeCAD.app/Contents/MacOS/FreeCADCmd
import FreeCAD
import Part
import Mesh

import sys
in_fn, out_fn = sys.argv[2], sys.argv[3]

Part.open(in_fn)
o = [ FreeCAD.getDocument("Unnamed").findObjects()[0] ]
Mesh.export(o, out_fn)

It's used like this: step_stl.py input.step output.stl

I am fairly certain that the input file could be any input format understood by FreeCAD. Enjoy!

Posted by jordanh at 12:08 PM | Comments (0) | TrackBack |

Main > Software

April 21, 2008

Python Rosetta Stone Automation

Many, many months ago I had a long, long layover in the airport in Madrid. On the flight over from the States I had grown increasingly frustrated with the interface to "The Rosetta Stone" language learning software. Now I must confess, there actually is little to get wrong in this interface as the majority of the interaction with the software is done via clicking one of four buttons. However, I come from the UNIX world where I prefer everything to be able to be driven from a set of keyboard shortcuts and so I committed my layover to automating The Rosetta Stone.

Tonight I find myself writing from yet another long, long layover and I find myself revisiting The Rosetta Stone to brush up on my Spanish. I remembered my little application I wrote in Spain and so I made a few tweaks and decided to share it, just in case anybody should find it useful.

If anything, it demonstrates two concepts:

Here is the entire application. It is less than 50 meaningful lines of code:

import os
import sys
import time
from msvcrt import getch

sys.path.append("../win32automation")

import win32automation

os.system("title KeySetta")
win32automation.spawnProcess(
    r"C:\Program Files\The Rosetta Stone\The Rosetta Stone\TheRosettaStone.exe")
while 1:
    print "Waiting for application to start..."
    result = win32automation.windowFocus("The Rosetta Stone")
    if result:
        print "Window found!"
        break
    time.sleep(1.0)
print "Waiting 5 seconds for login screen to appear..."
time.sleep(5.0)
win32automation.sendKeys('jordanh{ENTER}')

print """
   Keyboard to Mouse Macros Enabled:

     - Answer selection:
       [7] [9]
       [1] [3]
       
    Please focus this window to enable them...
"""

coord_map = {"7": (180, 300),
             "9": (480, 300),
             "1": (180, 475),
             "3": (480, 475)}

while 1:
    win32automation.windowFocus("KeySetta")
    ch = getch()
    print "Last key pressed: %s\r" % (ch),
    if ch in ('1','3','7','9'):
        win32automation.windowFocus("The Rosetta Stone")
        x, y = coord_map[ch]
        win32automation.mouseMoveToRelative("The Rosetta Stone", x, y)
        win32automation.mouseClick(button="left")
    elif ch.lower() == 'q':
        print "Quitting!"
        break
    else:
        print "WARNING: Unknown key-macro event '%c'." % (ch)

sys.exit()

Granted the silly "win32automation" module I wrote is a little stupid. It is based on the excellent example provided over at http://del.icio.us/amoebapr/python. Notice for example none of the functions take an opaque handle to an object. Instead, all of the functions inside of the module require some sort of hint (such as a window title) in order to re-find the desired object before executing an operation on it.

Hopefully however sharing this code will spark somebody to develop a better, free Windows automation module for Python.

Download keysetta.zip (md5sum: a4336d883fcb10b11b8b660451e522a3)

Posted by jordanh at 11:30 PM | Comments (2) | TrackBack |

Main > Software

December 31, 2005

Progress Bars with GD2 and Ruby

Just in time for the new year...

I went milling about for source for progress bars that would be suitably embedded within a Ruby on Rails application I'm working on and came up with nada. Sure, sure there are lots of bars that have to do with live uploading and whatnot (see: here); but nothing that grabbed me with the interface I was looking for (call function, return data).

Like cheating on a lover, I went looking into PHP territory and found a really quite wonderful and straightforward example here by Bård (Bob) Johannessen.

It only took about 20 minutes to adapt to a halfway decent ruby class and now I'm rollin'. Here are some output examples:

OSX-ish:
rpb-output1.png

WinXp-ish:
rpb-output1.png

Generic LED-ish:
rpb-output1.png

Solaris-ish:
rpb-output1.png

You can download a tarball of my musings: ruby_progressbar.tar.gz.

Here's the source:

#!/bin/env ruby18
# copyright 2005, Jordan Husney <jordan@husney.com>
# (http://jordan.husney.com/main.html)
#
# derived directly from the following source and protected under
# the GNU GENERAL PUBLIC LICENSE, Version 2, June 1991
#
# http://db.org/demo/2003/02/17/progress-bar/?view=source
#
# php+gd dynamic progress bar image script.
# copyright 2003, B. Johannessen <bob@db.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version, and provided that the above
# copyright and permission notice is included with all distributed
# copies of this or derived software.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# A copy of the GNU General Public License is available from the GNU
# website at the following URL: http://www.gnu.org/licenses/gpl.txt


require 'rubygems'
require 'gd2'

class RubyProgressbar
  include GD2

  IMAGE_DIR = "./images/"
  IMAGE_STYLES = [ 'winxp', 'osx', 'led', 'solaris' ]

  public

  def initialize
    @image = nil
  end

  def create(style, width, completed, total)
    # Check arugments:
    raise ArgumentError, "style '#{style}' unknown" if
      not IMAGE_STYLES.include?(style)
    raise ArgumentError, "width must be between 64 and 1280 pixels" if
      width < 64 || width > 1280
    raise ArgumentError,
      "completed must be greater than 0 and less than total" if
        completed < 0 || completed > total
    raise ArgumentError, "total must be greater than 0" if
      total < 1

    # Open images according to defined style:
    begin
      bg        = Image.import(IMAGE_DIR + style + '-bg.png')
      fill      = Image.import(IMAGE_DIR + style + '-fill.png')
      bg_cap    = Image.import(IMAGE_DIR + style + '-bg-cap.png')
      fill_cap  = Image.import(IMAGE_DIR + style + '-fill-cap.png')
    rescue Exception => e
      raise "Unable to load required images for style #{style}!"
    end

    # calculate the fill width:
    fill_width = (((width - bg_cap.width) * completed) / total).round -
                  fill_cap.width

    # create the new image, and copy the fragments into it:
    image = Image::TrueColor.new(width, bg.height)
    image.copy_from(bg, 0, 0, 0, 0, bg.w, (width - bg_cap.w))
    image.copy_from(bg_cap, (width - bg_cap.w), 0, 0, 0, bg_cap.w, bg_cap.h)
    image.copy_from(fill, 0, 0, 0, 0, fill_width, fill.h)
    image.copy_from(fill_cap, fill_width, 0, 0, 0, fill_cap.w, fill_cap.h)

    @image = image

    # return success
    return true
  end

  def destroy
    @image = nil
  end

  def gif()
    raise Exception, "image not created" if @image.nil?
    return @image.gif()
  end

  def jpeg(compression = nil)
    raise Exception, "image not created" if @image.nil?
    return @image.jpeg(compression)
  end

  def png(compression = nil)
    raise Exception, "image not created" if @image.nil?
    return @image.png(compression)
  end

end

It can be used thusly:

require 'ruby_progressbar'

bar = RubyProgressbar.new
# OSX style, 320px wide, 3 of 10 units completed on the bar:
bar.create("osx", 320, 3, 10)
f = File.new("output.png", "w")
f.write(bar.png)
f.close

Please let me know if you've got any feedback or make any extensions!

Update, 2006-Jun-28: fixed recollation typographic error and added syntax highlighting.

Posted by jordanh at 5:43 PM | Comments (9) | TrackBack |

Main > Software

September 25, 2005

Software

The following is software that I have written over the years.

Some of its good, some of its bad. Feel free to write me to let me know what you think!

Name Description Initial Release Date Last Release Date Current Version
euc2html euc2html is a simple application that converts any double-byte EUC-encoded characters into HTML unicode entities. 12/01/2001 12/01/2001 1.01
jFlash jFlash is a multilingual, web-based flashcard system based on open web standards, PHP, and XML. 2/5/2004 2/5/2004 1.0
putmarks putmarks is a quick-n-dirty solution for use with an XBEL bookmark synchronizing package such as the wonderful "Bookmarks Synchronizer" Firefox extension by Torisugari. 11/8/2004 2/9/2005 1.1
ruby_integrator ruby_integrator is a set of Ruby classes for performing numerical integration estimates using a variety of techniques. 9/25/2005 9/25/2005 1.0

Posted by jordanh at 10:57 AM | Comments (0) |