Entries from April 1, 2010 - April 30, 2010

Monday
Apr052010

IronPython: Stealing Colors with System.Drawing.Bitmap (and yes Visio)

I collect color sets for a hobby. Every time I see a page like this one about the traditional colors of Japan or this one about the of Crayola crayon colors I can’t help but capture them in a drawing so that I might be inspired later to use them in my own diagrams.

In this post, I’m going to show you how to use IronPython to help automate this task. Along the way we’ll learn a little about working with the System.Drawing namespace and of course I’ll do mix in some Visio interop.

FIRST THE COLORS

I found an interesting set of colors here: http://corte.si/posts/code/sortvis-fruitsalad/index.html

First step is to just save the image

I saved it as “D:\swatch.png”

Let’s start IronPython 2.6 with my favorite command-line options

"c:\Program Files (x86)\IronPython 2.6\ipy.exe" -X:TabCompletion -X:ColorfulConsole

Now import clr and System and add a reference to System.Drawing and then import System.Drawing

Now we are going to load the bitmap and fetch each pixel on the top row. Then we examine how many pixels we have (we should have 512 since that’s the width of the bitmap) and we’ll print out the contents of the first pixel

bmp = System.Drawing.Bitmap( "D:\\swatch.png" )
pixels = [ bmp.GetPixel(x,0) for x in xrange( bmp.Width ) ]
print len(pixels)
print pixels[0]

Now we’re going to do the Visio thing – we’ll launch it and get a reference to the active page

clr.AddReference("Microsoft.Office.Interop.Visio")
import Microsoft.Office.Interop.Visio
IVisio = Microsoft.Office.Interop.Visio
visapp = IVisio.ApplicationClass()
doc = visapp.Documents.Add("")
page = visapp.ActivePage

running those commands caused Visio to appear with a blank document …

Before we do the drawing let’s consider that we have 512 pixels

We could just draw a long line of squares, but that would make for an inconvenient drawing. We’d like something a bit more compact – place all the squares into a roughly rectangular area.

Thinking about this, I’ll approach the problem as one of mapping a series of numbers 0 to 511 to (x,y) coordinates

Now that means I have to figure out how many shapes I need on the edges … calling System.Math.Sqrt shows that I’ll need around 23 shapes for each side

So now I map the coordinates like this …

coords = [ (i%23,i/23) for i in xrange( len(pixels) ) ]

then I calculate the rectangle coordinates for all the shapes based on those coordinates

length = 0.25
rects = [ ( x*length, y*length, (x+1)*length, (y+1)*length ) for x,y in coords ]

 

then I draw the shapes

shapes = [ page.DrawRectangle( *rect ) for rect in rects ]

NOTE: there are better ways of drawing so many shapes (for example using the DropMany method) but to keep the example simple I’ll do it the simple way

What it rendered is this …

page.ResizeToFitContents()

Okay, that’s a bit easier to see.

So, now we get to the colors. We have a list of pixels. We have a list of shapes. what we want to do is set the FillForegnd cell of each shape to the appropiate rgb formula

for a single shape it would look like this…

shape1.Cells("FillForegnd").Formula = “=rgb(255,128,0)”

ok, so we calculate the formulas like this

formulas = [ "=RGB({0},{1},{2})".format(p.R, p.G, p.B) for p in pixels ]

and then we pair each formula with a shape and set the cell accordingly

for s,f in zip(shapes,formulas) : s.Cells("FillForegnd").Formula = f

And this yields…

 

 

 

 

 

 

 

 

 

 

 

 

And here’s the full script

import clr
import System
clr.AddReference("System.Drawing")
import System.Drawing
# Get the colors from the bitmap
bmp = System.Drawing.Bitmap( "D:\\swatch.png" )
pixels = [ bmp.GetPixel(x,0) for x in xrange( bmp.Width ) ]
# Start Visio
clr.AddReference("Microsoft.Office.Interop.Visio")
import Microsoft.Office.Interop.Visio
IVisio = Microsoft.Office.Interop.Visio
visapp = IVisio.ApplicationClass()
doc = visapp.Documents.Add("")
page = visapp.ActivePage
# Draw the shapes
coords = [ (i%23,i/23) for i in xrange( len(pixels) ) ]
length = 0.25
rects = [ ( x*length, y*length, (x+1)*length, (y+1)*length ) for x,y in coords ]
shapes = [ page.DrawRectangle( *rect ) for rect in rects ]
#Adjust the page
page.ResizeToFitContents()
# Set the colors
formulas = [ "=RGB({0},{1},{2})".format(p.R, p.G, p.B) for p in pixels ]
for s,f in zip(shapes,formulas) : s.Cells("FillForegnd").Formula = f

 

The full script running looks like this…

PARTING THOUGHTS

  • I hope you noticed instead of a lot of for loops (there’s only one at the end) – I used a functional approach and mapped the input (the pixels) to the output (the shapes and colors) using Python’s list comprehension syntax.
  • For the Pythonistas – yes I could have used generators instead of all those lists, but I thought it would be an easier example
  • you can get the final visio diagram here: http://cid-19ec39cb500669d8.skydrive.live.com/self.aspx/Public/Blog%20Posts/2010/Hilbert-Colors-^52010-04-05^6/HilbertColors-^52010-04-05^6.vsd
Thursday
Apr012010

Natively viewing Visio Diagrams in your web browser – a practical use of Embedding SVG in XHTML

You noticed, I hope my latest two posts on SVG - this one and this one – and for my third I’m going to give you a preview of a small tool I’m working on. It will be a part of my Visio Power Tools add in (which is found in my VisioAutomation project on CodePlex).

The new release of the tool is coming soon, and here’s what you’ll be able to do:

Load a nice Visio Diagram …

Select Power Tools > Import / Export > Export Selection as SVG + XHTML

A form will let you pick a filename (.XHTML) to save the drawing. (Yes, I know the form isn’t very nice – that’s why I’m calling this a preview)

Click OK and that XHTML file will be created .

You can then right-click on that file and open in FireFox 3.6 or Google Chrome (and eventually IE9)

And you will see them render your diagram …

This is what FireFox 3.6 shows

This is what Chrome shows  …

 

HOW IT WORKS

This is pretty simple. My code exports the Selection as SVG (you can see the temporary svg file in one of the screenshots above) and then wraps that SVG in some XHTML tags. The code is very, very simple.

WHERE TO GET THE POWER TOOLS

I plan on publishing version 2.5 of my VisioAutomation library in a week or two. You can get the latest version of my Visio Power Tools then.

If you are super-curious you can visit the VisioAutomation project on CodePlex and then download the latest source code and build it with Visio Studio 2008.