How To ... Develop a Bitmap Painting Tool - Strokes Support

new.gif NEW Tutorial:

In this step of the Bitmap Painting tool development, we will add some better stroke drawing code to avoid the dotted appearance.

Natural Language

 

MAXScript

macroScript MicroPaint category:"HowTo"

(

global MicroPaint_CanvasRollout

try(destroyDialog MicroPaint_CanvasRollout)catch()

local isDrawing = false

local bitmapX = bitmapY = 512

local theCanvasBitmap = bitmap bitmapX bitmapY color:white

local currentPos = lastPos = [0,0]

 

rollout MicroPaint_CanvasRollout "MicroPaint"

(

bitmap theCanvas pos:[0,0] width:bitmapX height:bitmapY bitmap:theCanvasBitmap

fn paintBrush pos = ( setPixels theCanvasBitmap pos #(color 0 0 0) )

fn drawStroke lastPos pos =

(

currentPos = lastPos

deltaX = pos.x - lastPos.x

deltaY = pos.y - lastPos.y

maxSteps = amax #(abs(deltaX),abs(deltaY))

deltaStepX = deltaX / maxSteps

deltaStepY = deltaY / maxSteps

for i = 0 to maxSteps do

(

paintBrush currentPos

currentPos += [deltaStepX, deltaStepY]

theCanvas.bitmap = theCanvasBitmap

)

on MicroPaint_CanvasRollout lbuttondown pos do

(

lastPos = pos

isDrawing = true

drawStroke lastPos pos

)

on MicroPaint_CanvasRollout lbuttonup pos do isDrawing = false

on MicroPaint_CanvasRollout mousemove pos do

(

if isDrawing do drawStroke lastPos pos

lastPos = pos

)

)

createDialog MicroPaint_CanvasRollout bitmapX bitmapY

)

 

Step-By-Step

--Code in green has not changes since the previous version!

 

macroScript MicroPaint category:"HowTo"

(

global MicroPaint_CanvasRollout

try(destroyDialog CanvasRollout)catch()

local isDrawing = false

local bitmapX = bitmapY = 512

local theCanvasBitmap = bitmap bitmapX bitmapY color:white

 

local currentPos = lastPos = [0,0]

The first change we will have to do is to define the current and the last known position variables which will be used to keep track of the current stroke. We initialize both to [0,0].

 

rollout MicroPaint_CanvasRollout "MicroPaint"

(

bitmap theCanvas pos:[0,0] width:400 height:400 bitmap:theCanvasBitmap

 

fn paintBrush pos = ( setPixels theCanvasBitmap pos #(black) )

We will simplify the paintbrush function. It will only draw the point, but will not update the canvas. This is necessary because the stroke will contain a large number of brush points, but will have to be updated just once after the stroke to maintain interactivity.

 

fn drawStroke lastPos pos =

(

This will be the stroke drawing function called by the mouse handlers. It will be passed two arguments - the last mouse position (beginning of the stroke) and the current mouse position (the end of the stroke).

currentPos = lastPos

The current position variable is initialized to the beginning of the stroke. It will be used to draw the line from the start to the end point of the stroke.

deltaX = pos.x - lastPos.x

deltaY = pos.y - lastPos.y

In order to draw a straight line from the last to the new position, we must know how far these two points are from each other along both X and Y.

maxSteps = amax #(abs(deltaX),abs(deltaY))

Then we compare the absolute values along X and Y to see which one is larger. MaxSteps will be equal to either the number of pixels between the two along X or along Y.

deltaStepX = deltaX / maxSteps

deltaStepY = deltaY / maxSteps

Now we calculate the incement per step we will need for the X and Y axis. If maxSteps is equal to the distance along X, then deltaStepX will be +1 or -1, while deltaStepY will be a number less than 1.0. If maxSteps is equal to the distance along Y, then deltaStepY will be +1 or -1, and deltaStepX will be < 1.0.

for i = 0 to maxSteps do

(

Now we loop from 0 to the number of steps,

paintBrush currentPos

Paint a black dot at the current position by calling our custom paintbrush function,

currentPos += [deltaStepX, deltaStepY]

and then increment the current position with the deltaStep values until the current mouse position is reached.

theCanvas.bitmap = theCanvasBitmap

Finally, when the stroke is done, we update the bitmap in the rollout.

)

 

 

on MicroPaint_CanvasRollout lbuttondown pos do

(  

lastPos = pos

If the user pressed the mouse button, we will have to reset the stroke to the currently clicked point.

isDrawing = true

drawStroke lastPos pos

Instead of calling the PaintBrish function, we call the drawStroke function and pass the last and the current position (which are equal at this point) to it. This will create a single dot at the clicked point (the stroke has length 0, both deltaX and deltaY are 0, thus maxSteps is 0, but since the for i loop starts counting at 0, we get at least one iteration to draw the point!)

)

on MicroPaint_CanvasRollout lbuttonup pos do isDrawing = false

on MicroPaint_CanvasRollout mousemove pos do

(

if isDrawing do drawStroke lastPos pos

Instead of calling the PaintBrish function, we call the drawStroke function again. At this point, the lastPos and the pos values are different and require a longer stroke to be drawn.

lastPos = pos

After the stroke is drawn, we record the current mouse position into the lastPos variable to be used as the starting point of the next stroke.

)

)

createDialog MicroPaint_CanvasRollout bitmapX bitmapY

)

 

Result

 

MicroPaint_Step2.gif

 

Previous Tutorial:

How To ... Develop a Bitmap Painting Tool - Basic Utility

Next Tutorial:

How To ... Develop a Bitmap Painting Tool - Brush Size and Color

See also

"How To" Tutorials Index Page