This is a tag page.

Adjust Xfig figure depths with figdepth

John Peck
Published

Xfig's depth settings can create some headaches when you use figure libraries. Library figures can use depths that conflict with your current drawing. Say I have this drawing of the Teensy Audio Board and a red circle,

Teensy audio board and a red circle

...and then I move the red circle over the board:

Overlapping objects without depth correction

This is clearly not what I want. The circle has a depth of 50, and the board has depths between 10 and 60. The circle ends up on top of some objects and not others. If you were using PowerPoint, you would just select Bring to Front or Send to Back to arrange the objects. But Xfig requires setting the depth of each member object individually, which you should really do with a script like figdepth.

Using figdepth

Figdepth has some options, the most important of which is the minimum depth argument

Figdepth command options

...which sets the depth of the topmost object in a fig file. The script simply increments all depths by some number to make this minimum depth to be your setting. You will, of course, need to have everything you want to modify in its own fig file. You can then run figdepth like

Figdepth use

...to write your new depth-adjusted figure. Use Xfig's merge command to bring the new figure into your old drawing. My new drawing looks like this:

Fixed overlap

...with all objects in the Teensy Audio Board at a deeper depth than the red circle.

How does it work?

The Fig file format is really simple, which is one reason why I like Xfig so much. You can figure out what kind of object each line describes by looking at the first character. You then figure out which number in the line is the object's depth and increment it. The switch statement below shows how one line is processed.

proc increase_object_depth { xfig_line depth_increase} {
    # Return a new xfig line with the depth increased
    #
    # Arguments:
    #   xfig_line -- Single line from an xfig file
    #   depth_increase -- Amount to increase the depth
    set entry_list [split $xfig_line]
    set first_character [lindex $entry_list 0]
    switch $first_character {
    "0" {
        # Color object -- nothing to do
        return $xfig_line
    }
    "1" {
        # Ellipse
        set depth_index 6
        set old_depth [get_object_depth $xfig_line]
        set new_depth [expr $old_depth + $depth_increase]
        set new_entry_list [lset entry_list $depth_index $new_depth]
        set new_line [join $new_entry_list]
        return $new_line
    }
    "2" {
        # Polyline (also imported picture bounding boxes)
        set depth_index 6
        set old_depth [get_object_depth $xfig_line]
        set new_depth [expr $old_depth + $depth_increase]
        set new_entry_list [lset entry_list $depth_index $new_depth]
        set new_line [join $new_entry_list]
        return $new_line
    }
    "3" {
        # Spline
        set depth_index 6
        set old_depth [get_object_depth $xfig_line]
        set new_depth [expr $old_depth + $depth_increase]
        set new_entry_list [lset entry_list $depth_index $new_depth]
        set new_line [join $new_entry_list]
        return $new_line
    }
    "4" {
        # Text
        set depth_index 3
        set old_depth [get_object_depth $xfig_line]
        set new_depth [expr $old_depth + $depth_increase]
        set new_entry_list [lset entry_list $depth_index $new_depth]
        set new_line [join $new_entry_list]
        return $new_line
    }
    "5" {
        # Arc
        set depth_index 6
        set old_depth [get_object_depth $xfig_line]
        set new_depth [expr $old_depth + $depth_increase]
        set new_entry_list [lset entry_list $depth_index $new_depth]
        set new_line [join $new_entry_list]
        return $new_line
    }
    "6" {
        # Compound -- nothing to do
        return $xfig_line
    }
    default {
        # Some non-object -- nothing to do
        return $xfig_line
    }
  }
}

Having trouble with cmdline?

Figdepth uses the cmdline package from tcllib to handle command line arguments. I usually run scripts out of Eshell in Emacs, so I have this in my .emacs:

eshell setup

...to set the TCLLIBPATH environment variable. This tells Tcl where to find Tcllib's packages.

Where is the figdepth script?

Figdepth is part of my xfigart repository.

Xfigart: Oscilloscope probe

John Peck
Published

This is a generic oscilloscope probe from the xfigart library. Get the source for this and many other images by cloning or downloading the library.

xfigart pic

Fix Xfig's landscape postscript output for printing

John Peck
Published

It's important to get the page orientation correct if you think your Xfig output might ever be printed. I was trying to make a cable drawing inside a titleblock with Xfig. The postscript output viewed by qpdfview is shown below.

raw ps output

You can see that this will never print properly, and the ps2pdf command simply gives you a pdf version of the same problem. Ghostscript's ps2write device can fix the orientation with:

gs -dSAFER -dBATCH -sDEVICE=ps2write -dNOPAUSE -sOutputFile=out.ps xfig_input.ps

where the other command-line options are documented here. This produces the output below:

fixed ps output

...which can be converted to pdf and printed.

Illustrate concurrency with timebars

John Peck
Published , updated

I needed to document some concurrent processes, so I put together a script that generates figures like these:

mario example

Download single-file executables here.

Creating the input file

The input file is simply Tcl, and is sourced by the main program. Starting the program with no inputs will create an example.tbi file in an input directory. This is just some basic organization -- the program doesn't care about the input file location or its extension.

The seconds_per_inch variable controls how large timeslices will be relative to their labels. If your timeslice labels don't fit, try reducing this number.

Creating timeslices

The timeslice::timeslice function creates a timeslice. For example:

set mario_airborne [timeslice::timeslice 100e-3 red "Mario has jumped"]

...creates a 100ms-long red timeslice with a label. Recognized colors are black, blue, green, cyan, red, magenta, yellow, and white.

Creating reference lines (timestamps)

The timeslice::reference function creates a reference line. For example:

set mario_lands [timeslice::reference "Mario lands on goomba"]

...creates a reference line with a label. The program will assign a timestamp to the reference line depending on where it lands in its timeline.

Creating timelines

The timeline::timeline function assembles lists of timeslices into a timeline. For example:

set mario_list [list $mario_airborne $mario_lands $mario_recovers]
set mario_timeline [timeline::timeline "Mario" $mario_list]

...creates the mario_timeline timeline. Notice that the mario_lands reference line is in the list of timeslices along with regular timeslices.

Processing the input file

I have a history of creating Tcl software used by manufacturing technicians. These people usually use Windows machines, and they like being able to choose which version they can run. My executables thus have their version numbers in their filenames. Use the -h option to see the usage:

~/projects/eventual/timebars/starpacks $ ./timebars-1.0.2-win32-ix86.exe -h
timebars usage: timebars [options] filename
 -d value             Minimum depth <50>
 -o value             Output file name <timebars.fig>
 --                   Forcibly stop option processing
 -help                Print this message
 -?                   Print this message

If you don't specify an input file (call the script with no arguemnts), the script will generate an input directory and an example file in that directory.

The "minimum depth" parameter is for Xfig. If you already have graphics drawn in Xfig and you want to add this figure, you'll need to make sure the depths cooperate to make overlapping elements visible.

Create arcs of boxes with xfig

John Peck
Published , updated

I needed to add an array of 0603 LEDs to a loudspeaker drawing (shown below). Since I've done some scripting for xfig files before, I thought I'd try that again.

loudspeaker face

You can see the tiny green LEDs distributed over the big middle driver. You can find the script here. You'll need Tcl and Tcl's cmdline package to run it. Once you've got things set up, you can see the usage with

~/p/eventual/scripts/boxarc λ tclsh boxarc.tcl -h
boxarc usage: boxarc [options]
-l value             Box length (mils) <100>
-w value             Box width (mils) <100>
-d value             Minimum depth <50>
-r value             Arc radius (mils) <1000>
-s value             Start angle (degrees) <0>
-e value             End angle (degrees) <90>
-n value             Number of boxes <10>
-o value             Output file name <none>
--                   Forcibly stop option processing
-help                Print this message
-?                   Print this message

...and you can get the LEDs shown in the above image with

tclsh boxarc.tcl -l 63 -w 31 -r 2500 -s 30 -e 150 -n 32 -d 10

...which puts 32 0.063 x 0.031 inch boxes on a 2.5 inch radius between angles 30 and 150 degrees. The depth setting of 10 puts the LEDs "on top" and visible in the image.

Fix xfig's font problems on Windows (Cygwin)

John Peck
Published , updated

Xfig is still a very convenient tool for me, and I need it to work under Cygwin. I've tried to document how I've been able to get it to work. You will, of course, need to start by installing Cygwin.

Download setup-x86.exe

Download the 32-bit (or 64-bit if you're feeling lucky) setup.exe from cygwin.com. I like to copy it to my Windows desktop so I can easily run it again when I need to add packages. The 64-bit version seemed to be missing a few packages the last time I tried it, and I haven't tried it since then.

Run the installer

Select install from internet. The default installation directory (C:\cygwin) works for me. I also like to store package information to C:\cygstore, but this isn't important. After selecting file locations, you can use the mirror sites page to find a local mirror.

Choose these packages for installation:

  1. xorg-server
  2. xinit
  3. xfig
  4. xfig-lib
  5. ghostscript
  6. ghostscript-fonts-other
  7. ghostscript-fonts-std

...and then Cygwin will decide which dependencies will also need to be installed. The initial installation takes me about 15 minutes.

Start the Cygwin Terminal

This creates your home directory and some useful configuration files.

Start the X server

Starting the X server with

export DISPLAY=:0
startxwin &

...will let you keep using the same terminal after the server starts. I've had some strange problems with this sequence of commands -- it works for me on a host OS, but it failed at first on a VirtualBox Windows 7 guest. The fix was to start the XWin Server via the start menu instead of the command line. After I did that, the command line started working. Maybe a config file got written? Anyway, now X is running.

Start xfig to see the problem

I've got xfig 3.2 patchlevel 5c. I add some text to the canvas, then zoom in. The error message is:

Font size 17 not found, using smaller 12 point

...and the glyphs don't scale with the rest of the canvas -- extremely annoying.

Install some font tools

I needed to install the:

  1. xset
  2. mkfontdir
  3. mkfontscale

...packages via the cygwin installer.

Download ghostscript fonts

I'm not sure where the fonts should be archived, but the file you want is ghostscript-fonts-std-8.11.tar.gz. It looks like it's hosted at SourceForge. I make the directory ~/fonts and unpack the file there. This will make its own fonts directory. Descend into this directory and:

mkfontdir
mkfontscale

Add this new fonts directory to the list known by X

With the X server running,

xset fp+ ~/fonts/fonts

...and you won't see any output.

Test the fix

Fire up xfig again and type some text. Enjoy the nice scaling glyphs when you change the zoom factor!

You'll probably want to add the export DISPLAY=:0 line to your .bash_profile or your .bashrc file. Unfortunately, the xset command won't work until X is actually running. You could add that line to your .bashrc, but you'd have to make sure you started X via the start menu before you started the Cygwin Terminal. You could also simply type bash after starting X to re-run the .bashrc commands. And there are probably many better ways of doing all this.

Update on 2021-Jan-20 — Cygwin's X server issues

I stopped using Cygwin's X server because it wasn't working well with my high resolution display. Xfig's buttons and popups were too tiny to be usable. The VcXsrv server does a much better job, but my xset command stopped working. I saw the error messages:

xset:  bad font path element (#97), possible causes are:
    Directory does not exist or has wrong permissions
    Directory missing fonts.dir
    Incorrect font server address or syntax

The fix was to replace the Cygwin file path with a Windows path:

xset fp+ "c:/cygwin64/home/me/fonts/fonts"

...where c:/cygwin64/home/me is my home directory.

Convert fig files to png using Ghostscript and Imagemagick

John Peck
Published , updated

The venerable xfig is still a nice vector drawing program with a clean user interface. Using ghostscript to convert its eps output to png allows setting the conversion resolution explicitly. The script below takes two arguments: the maximum dimension of the finished png in pixels, and the input fig file.