ImageJ macro tool - how to draw, move, and scale overlays while dragging a line


Hey guys,

I’m quite a novice at imagej macro programming and specifically with macro tools. I asked before in this thread how to program a macro tool. I’ve been more or less able to accomplish what I wanted to do, but I am stuck at a half functioning tool. the old thread is dead, so I thought I’d ask my specific problem again here.

I have a macro tool that is basically a line tool. But it draws two perpendicular lines on each end of the line as orientation (so you can measure things orthogonal to certain structures). A major drawback is, that the orientational line overlays are only drawn after you draw the “main line” that gets measured. I’d like them to be drawn live and change their location / shape accordingly while the user is dragging the “main line” (see unresolved last answer in previous topic).

I’ve searched all the ImageJ documentation on macros, macro tools and builtin functions, but I can’t really make sense of it. What I am missing is what specific mouse flags symbolize what action, and in what loop to put what function. I am only good at programming in python 3.6+ and I don’t know any java. So programming this is really difficult for me and the Jython extension also doesn’t really help.

I’d be super thankful if anyone could give me some hints / program it for me / point me to resources on how to do it.

Best regards



On the ImageJ mailing list I made an example how to exchange the cursor and change interactively
the size of a custom cursor (a drawn circle) with macro capabilities (using a bit JavaScript called from the macro code to hide and set the cursor).

You might find this example useful to extend your script (Press SHIFT to increase the circle cursor, press ALT to decrease and SPACE to end the macro!):

isDown = false;
width = 50;
height = 50;
eval("script", "ImageCanvas.setCursor(Toolkit.getDefaultToolkit().createCustomCursor(new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB), new Point(0, 0), \"blank cursor\"),1)");
while (isDown == false) {
    isDown = isKeyDown("space");
    if (isKeyDown("shift")) {
    if (isKeyDown("alt")) {
        if (width > 1 && height > 1) {
    getCursorLoc(x, y, z, flags);
    Overlay.drawEllipse(x - (width / 2), y - (height / 2), width, height);;
eval("script", "ImageCanvas.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR),1)");


Thanks for the repy @Bio7. I feel honored :D. The isKeyDown() function is very handy, wish it had more options for keys. It is a lot more readable than the mouse flags. Anyways I invested some more time into the matter and found out how to index the flags getCursorLoc() returns. I modified my macro a bit and it now draws the line live.

You use it like a normal line tool, and you reset the macro by pressing space.

Here it is, in case anyone else find is useful:

// Author: Nils  Höche

macro "Perpendicular Line Tool - C000-B11-L0440-L3399-L8cc8" {
	var leftButton = 16;
	var isDown = false;
	var start = false
	while (true) {
		getCursorLoc(trash1, trash2, trash3, flags);
		if (!start && flags&leftButton!=0) {
			getCursorLoc(xStart, yStart, zStart, flags);

		while (flags&leftButton!=0) {
			getCursorLoc(x, y, z, flags);
	    	drawPerpendicularLines(xStart, yStart, x, y);
		// Reset with spacebar
		isDown = isKeyDown("space");
		if (isDown){
			run("Select None");

function drawPerpendicularLines(x1, y1, x2, y2) {
    makeLine(x1, y1, x2, y2);

    // get slope of line
    dx = (x2-x1);
    dy = (y2-y1);

    // calculate perp line endpoints
    xa = (x1 - dy/4); ya = (y1 + dx/4);
    xb = (x1 + dy/4); yb = (y1 - dx/4);

    xc = (x2 + dy/4); yc = (y2 - dx/4);
   	xd = (x2 - dy/4); yd = (y2 + dx/4);

    Overlay.drawLine(xa, ya, xb, yb);
    Overlay.drawLine(xc, yc, xd, yd);;

It’s still a bit hacky. Improvements I would wish for are:

  1. The ability to modify the line after it is drawn. In the current status you have to start over by pressing space.

  2. If you hold leftclick while pressing space it registers the 0,0 position as start

But for now the macro functionality is sufficient for me :slight_smile: