GDI+ Methods – The DotNet Paint event

Oct 28, 2008 by     2 Comments    Posted under: 3dsMax, DotNet, Imaging

In order to use GDI+ to draw vector based shapes, outlines and objects in DotNet, one option available is to use the paint event.

I have been working on some GDI+ functions that can dynamically generate icons without the need for bitmaps. The idea of this came from a post on CGTalk some time ago by Dave Stewart who previewed a similar idea based around MXS and setpixel methods if i remember rightly. I wanted to write a dotnet equivalent. I am trying to include the sort of icons you use on dialogs for generic actions like arrows, numbers, pickbuttons etc.

One nice thing is using translatetransform() within the graphics surface, that way you can draw the icon in one position and then pass an angle in the function and it draws it pointing the correct direction. this simplifies things greatly and means you have one function for all arrow icons.

With the same thing in mind, the plus icon also becomes the cross icon with translatetransform, as does the square when it becomes the diamond. The numbers adjusts the font size to the largest possible according to the number of digits.

Each paint event function passes an outline colour and a fill colour along with the graphics surface so you can adapt the icons to whatever scheme you like, including the current 3dsmax one.

There are a few options with a GDI+ drawing surface when it comes to how you perform the drawing itself. I decided to opt for the graphicspath object method as you are able to treat it as a closed entity. This means you can fill it with colors, stroke the outline and apply linestyles and edge joins. Constructing the shape from individual lines always treats them as individual lines and not the overall shape. In order to make the corners sharp on a graphicspath object, one option in this heavilly overloaded function is to pass a byte array. I’m still not sure why this is neccesary, except for the fact that it doesn’t work properly without it. I think it must be something to do with how the corners are rendered.

I set up a loop to build the dotnet arrays in max, remember that dotnet arrays start at 0, not at 1 like in Maxscript.

-- specify an array of points

PlusGDIArray = dotNetObject "System.drawing.point[]" 12

-- and a byte array of the same size

ByteArray = dotNetObject "System.byte[]" 12

-- store the points you want to place in the array

local pointloop = #((pt1 = dotNetObject PointClass 8 2),
(pt2= dotNetObject PointClass 8 8),
(pt3= dotNetObject PointClass 2 8),
(pt4 = dotNetObject PointClass 2 16),
(pt5 = dotNetObject PointClass 8 16),
(pt6 = dotNetObject PointClass 8 22),
(pt7= dotNetObject PointClass 16 22),
(pt8 = dotNetObject PointClass 16 16),
(pt9 = dotNetObject PointClass 22 16),
(pt10 = dotNetObject PointClass 22 8),
(pt11 = dotNetObject PointClass 16 8),
(pt12 = dotNetObject PointClass 16 2))

-- and apply them into the dotnetarray
for i = 1 to pointloop.count do PlusGDIArray.setvalue pointloop[i] (i-1)

--repeat for the byte array
local byteloop = #((Byte1 = dotNetObject ByteClass 0),
(Byte2= dotNetObject ByteClass 1),
(Byte3= dotNetObject ByteClass 1),
(Byte4 = dotNetObject ByteClass 1),
(Byte5 = dotNetObject ByteClass 1),
(Byte6 = dotNetObject ByteClass 1),
(Byte7= dotNetObject ByteClass 1),
(Byte8 = dotNetObject ByteClass 1),
(Byte9 = dotNetObject ByteClass 1),
(Byte10 = dotNetObject ByteClass 1),
(Byte11 = dotNetObject ByteClass 1),
(Byte12 = dotNetObject ByteClass 129))
for i = 1 to byteloop.count do ByteArray.setvalue Byteloop[i] (i-1)
-- finally add both arrays to a graphicspath object
ClosedPath = dotnetobject "System.Drawing.Drawing2D.GraphicsPath" PlusGDIArray ByteArray

download script