Developing a Vectorworks 2011 tool plug-in, TDD-style – Episode 4

Now that we’re able to create real Vectorworks geometry with our tool, what’s next? The “spec” laid out in Episode 0 calls for creating a red or green line depending on where the user sets the clicks. Tackling the click’s location test first sounds like an interesting challenge.

What’s the simplest way to start? If the user doesn’t click on a polygon object, our tool should ignore the clicks. How does RedGreenLineTool know if the clicks happen on a polygon object? For know, we let it know by calling MouseOverPolygonObject() before adding tool points. Sounds reasonable.

TEST_N(ToolPointsShouldBeReflectedAsStartEndPointOfLineObject)
{
	MockRedGreenLineTool vwTool;
	vwTool.MouseOverPolygonObject();
	
	vwTool.AddPoint(WorldPt3(0, 0, 0));
	vwTool.AddPoint(WorldPt3(50, 0, 0));

	CHECK_EQUAL(2, vwTool.GetNumToolPoints());
}

If we don’t call MouseOverPolygonObject() before adding points, they shouldn’t be added to the list of points tracked.

TEST_N(ToolPointNotOnPolygonShouldBeIgnored)
{
	MockRedGreenLineTool vwTool;
	
	vwTool.AddPoint(WorldPt3(0, 0, 0));
	vwTool.AddPoint(WorldPt3(50, 0, 0));
	
	CHECK_EQUAL(0, vwTool.GetNumToolPoints());
}

Implementation, of course, is rather trivial (for now ;-). We just add a bool fMockMouseOverPolygonObject to MockRedGreenLineTool, set it MouseOverPolygonObject(), test it in AddPoint() and we’re done.

Well, not really. We do have an implementation for MockRedGreenLineTool only, we need to move the implementation up the inheritance tree to RedGreenLineTool to turn it into something useful.

class RedGreenLineTool {

	virtual bool IsMouseOverPolygonObject() = 0;

	void AddPoint(const WorldPt3& p) {
		if (IsMouseOverPolygonObject())
			fToolPoints.PushData(p);
	}
};

Implementation of IsMouseOverPolygonObject() is rather trivial for MockRedGreenLineTool, we just return fMockMouseOverPolygonObject. Of course, VectorworksRedGreenLineTool needs a more sophisticated implementation, but (as intended) a rather simple one:

class VectorworksRedGreenLineTool {

	virtual bool IsMouseOverPolygonObject() {
		MCObjectHandle overObject = NULL;
		short overPart;
		long code;
		gSDK->TrackTool(overObject, overPart, code);
	
		return (overObject && ! VWPolygon2DObj::IsPolygon2DObject(overObject));
	}
};

Now this rather straightforward implementation doesn’t quite work yet, as this code doesn’t reflect the state Vectorworks is in. every mouse click is registered by Vectorworks first, then an event is sent to the tool to process it. If we choose to ignore the mouse click, we need to notify Vectorworks of this fact. Thus, we need to make some minor changes to the code to accommodate for this behavior.

class RedGreenLineTool {

	virtual void PopLastToolPoint() = 0;

	void AddPoint(const WorldPt3& p) {
		if (IsMouseOverPolygonObject())
			fToolPoints.PushData(p);
		else
			PopLastToolPoint();
	}
};

While MockRedGreenLineTool gets away with an empty implementation for PopLastToolPoint(), VectorworksRedGreenLineTool needs to notify Vectorworks that the last tool point registered should be removed:

class VectorworksRedGreenLineTool {

	virtual void PopLastToolPoint() {
		gSDK->PopLastToolPt();
	}
};

Where are we at? We do have a basic implementation of mouse tracking in place. RedGreenLineTool is able to tell if the mouse is over a polygon object while the mouse is clicked. In the next episode, we will tackle tracking mouse clicks on a single or two different polygons in order to create red or green lines.

Previous Episode