18
Apr 11

Developing a Vectorworks 2011 Plug-in, TDD-style – Episode 3

In closing Episode 2 – Spicing up our class, we heard Dr. Bob calling bullshit on the x coordinate testing we came up with. We should be able to test the y & z coordinates of the cabinet parts, too. But this would sure look like Dr. Bob doing the same sketch twice. It would be way better (and more efficient) to calculate the 2D rectangles forming the basis of the different cabinet parts (assuming we could get away with simple extrudes for the cabinet’s parts). Let’s do that and rewrite the tests and code accordingly.

TEST_N(SimpleCabinetGetLeftSideBase)
{
	SimpleCabinet simpleCabinet(600, 720, 800);
	
	CHECK_EQUAL(WorldRect(0, 800, 
						  SimpleCabinet::kBoardThickness, 0), 
				simpleCabinet.GetLeftSideBase());
}

Making the test pass is supposed to pretty simple, we calculate the WordRect required using the depth of SimpleCabinet. But much to our dismay, there’s no such instance variable fDepth. Let’s add it (test first, of course):

TEST_N(SimpleCabinetWidthAndHeight)
{
	SimpleCabinet simpleCabinet(600, 720, 800);
	
	CHECK_EQUAL(600, simpleCabinet.GetWidth());
	CHECK_EQUAL(720, simpleCabinet.GetHeight());
	CHECK_EQUAL(800, simpleCabinet.GetDepth());
}

Of course, making the test pass is trivial. We add the accessor and instance variable to SimpleCabinet:


	WorldCoord GetDepth() const {
		return fDepth;
	}
	
	WorldCoord fDepth;

Now back to our initial task, coding GetLeftSideBase():

	WorldRect GetLeftSideBase() const {
		return WorldRect(0, fDepth, kBoardThickness, 0);
	}

With GetLeftSideBase() under our belt, let’s tackle the cabinet’s right side:

TEST_N(SimpleCabinetGetRightSideBase)
{
	SimpleCabinet simpleCabinet(600, 720, 800);
	
	CHECK_EQUAL(WorldRect(600 - SimpleCabinet::kBoardThickness, 800, 
						  600, 0), 
				simpleCabinet.GetRightSideBase());
}

As the basic shape of the right side will be identical to the left side’s shape, we choose to implement it by re-using LeftLeftSideBase():

	WorldRect GetRightSideBase() const {
		WorldRect r = GetLeftSideBase();
		r.Offset(WorldPt(fWidth - kBoardThickness, 0));
		return r;
	}	

With the base shape of the cabinet’s left & right side plus the cabinet’s height, we are pretty optimistic about being able to create the proper Vectorworks geometry. Let’s see if we can compute the top & bottom board’s basic shape too. The basic shape for both parts is:

TEST_N(SimpleCabinetGetTopBottomBase)
{
	SimpleCabinet simpleCabinet(600, 720, 800);
	
	CHECK_EQUAL(WorldRect(SimpleCabinet::kBoardThickness, 800, 
						  600 - SimpleCabinet::kBoardThickness, 0), 
				simpleCabinet.GetTopBottomBase());
}

Again, pretty simple to make this test pass:

	WorldRect GetTopBottomBase() const {
		return WorldRect(kBoardThickness, fDepth, 
						fWidth - kBoardThickness, 0);
	}

For the top board, we will sure need the z position, too. Let’s do this before we’re wrapping it up for today:

TEST_N(SimpleCabinetGetTopZPosition)
{
	SimpleCabinet simpleCabinet(600, 720, 800);
	
	CHECK_EQUAL(720 - SimpleCabinet::kBoardThickness, 
				simpleCabinet.GetTopZPosition());
}

Coding up SimpleCabinet::GetTopZPosition() is a breeze now that we’re on a roll 😉

	WorldCoord GetTopZPosition() const {
		return fHeight - kBoardThickness;
	}

That’s it for today. We have been able to come up with a representation of the different cabinet part’s basic shape, which, if mixed with the models information about the cabinet’s height, should enable us to – finally – create some geometry in Vectorworks.

Tune in next week when you hear Dr. Bob say “It’s time to create some Vectorworks objects or the patient will die of boredom…”.

P.S.: Just in case you’re wondering about Dr. Bob – Check out The Veterinarian’s Hospital on Wikia or on Youtube. This should give me a minute or two to prepare Episode 4.

Previous Episode | Next Episode


17
Apr 11

Developing a Vectorworks 2011 Plug-in, TDD-style – Episode 2

In Episode 1 – How to start?, we’ve cleared the first hurdle – we actually wrote a passing test. It was rather trivial, but we ended up with a class representing a cabinet with the properties “Width” and “Height”. What’s next? How about spicing up the SimpleCabinet class? (At the current state, just adding some salt to the class will spice it up substantially). How about allowing us to calculate the position of the left & right side of the cabinet? The left side of the cabinet should be located at x = 0 (in the local coordinate space of the plug-in object):

TEST_N(SimpleCabinetGetLeftSideXLocation)
{
	SimpleCabinet simpleCabinet(600, 800);
	
	CHECK_EQUAL(0, simpleCabinet.GetLeftSideXLocation());
}

Passing this test is (again) rather trivial. We add the following message to the SimpleCabinet class:

	WorldCoord GetLeftSideXLocation() const {
		return 0;
	}

If the left side’s located at x = 0, then the right side’s x location should be the width of the cabinet minus the board thickness (assuming that the width property denotes the overall width of the cabinet).

TEST_N(SimpleCabinetGetRightSideXLocation)
{
	SimpleCabinet simpleCabinet(600, 800);
	
	CHECK_EQUAL(600 - SimpleCabinet::kBoardThickness, 
				simpleCabinet.GetRightSideXLocation());
}

Adding the following method to SimpleCabinet will make the test pass. We didn’t plan for this in episode 1, but we can re-use the fWidth instance variable for the purpose of deriving the right side’s x location.


	static const WorldCoord kBoardThickness;

	WorldCoord GetRightSideXLocation() const {
		return fWidth - kBoardThickness;
	}	

While we’re at it, we could also test the x location of the cabinet’s bottom, which should be at x = kBoardThickness (as the cabinet is supposed to have continuous sides):

TEST_N(SimpleCabinetGetBottomXLocation)
{
	SimpleCabinet simpleCabinet(600, 800);
	
	CHECK_EQUAL(SimpleCabinet::kBoardThickness, 
				simpleCabinet.GetBottomXLocation());
}

Making the test pass is simple:

	WorldCoord GetBottomXLocation() const {
		return kBoardThickness;
	}

Where are we at? At last, we’re starting to actually compute something. Our “model” class is able to deliver a cabinet’s overall width & height plus the x location of the cabinet’s left side, right side & bottom. We sticked to writing the tests first. Not an awful lot of sophistication here (yet), but progress.

Tune in next week when you hear Dr. Bob say: “I’m calling bullshit on this x coordinate testing…”.

Previous Episode | Next Episode


17
Apr 11

Developing a Vectorworks 2011 Plug-in, TDD-style – Episode 1

After Episode 0 – Introduction, let’s try to get some real coding done in this episode.

For a TDD newbie, the first questions are always along these lines: “How do I start? What’s the first test? How do I know what to test?”

Starting with a blank slate, let’s assume we have a class SimpleCabinet – we sure need something like that. Instantiating a SimpleCabinet object with a given width & height, we should be able to derive the width & height from the object. Not very elaborate or challenging, but it’s a start. Let’s see where this leads us.

#include "CppUnitLite2.h"

TEST_N(SimpleCabinetWidthAndHeight)
{
	SimpleCabinet simpleCabinet(600, 800);
	
	CHECK_EQUAL(600, simpleCabinet.GetWidth());
	CHECK_EQUAL(800, simpleCabinet.GetHeight());
}

Obviously, the code doesn’t compile. We need an implementation:


class SimpleCabinet {

public:
	
	SimpleCabinet(WorldCoord width, WorldCoord height)
		: fWidth(width), fHeight(height) {
	}

	WorldCoord GetWidth() const {
		return fWidth;
	}
	
	WorldCoord GetHeight() const {
		return fHeight;
	}
	
private:
	
	WorldCoord fWidth;
	WorldCoord fHeight;
};

Not an awful lot to see here, but at least we are able to compile & link successfully. Uh, and it runs and the test passes. Excellent. That’s a great start for a sunny sunday morning while the baby’s asleep.

You think this is way too trivial? Where’s the beef? With a small brain like mine, I prefer to take small steps at a time. Writing a small test, just a little bit of implementation and get feedback right away. As the old Japanese proverb says: “Even a thousand-mile journey begins with the first step.”. The point is not to stop with this first step. We will get to some actual Vectorworks SDK code.

Tune in next week when you’ll hear Dr. Bob say…

Previous Episode | Next Episode


16
Apr 11

Developing a Vectorworks 2011 Plug-in, TDD-style – Episode 0

During a recent internal TDD code camp at extragroup, we were working on some basic TDD style katas like StringCalculator.

Some question regarding TDD & Vectorworks came up during the day:

  • “How do I develop a Vectorworks plug-in TDD style?”
  • “Is it possible to develop a Vectorworks plug-in TDD style?”

We haven’t been able to answer this question in sufficient detail during the one-day code camp, so I set out to work through a simple project. My goal is to develop a Vectorworks plug-in object – a simple cabinet. It should be defined by its dimensions:

  • Overall Width
  • Overall Height
  • Overall Depth

The front should be made of “tiles” defined by a Vectorworks symbol (think of a rectangular grid of symbols, e.g. a simple panel or a frame). This will be the fourth plug-in object parameter. If the grid of tiles won’t fit the available space precisely, some additional blind front panels to the right and to the top of the grid should be added. To keep things simple, the cabinet should have continuous sides, all boards are supposed to be 19mm thick (or 3/4″ for you imperial guys out there) – after all, our goal is to learn how to develop a Vectorworks plug-in object TDD-style, not to create a shipping product.

Simplecabinet

In the upcoming episodes, I won’t go into details on the actual structure of a Vectorworks plug-in, for more details on how to build a Vectorworks plug-in, please refer to http://developer.vectorworks.net. There’s also a very promising tutorial by Ryan McCuaig: Getting Started with the Vectorworks SDK. The sole focus of this series is to explore how to develop a Vectorworks plug-in object TDD-style.

I’ll be using CppUnitLite2 as my C++ unit testing framework. In order satisfy the linker in the testing only target, a GSSDKStubs.cpp file contains stubs for all old-style GS_ API functions contained in the Vectorworks SDK. This enables us to create a separate testing target (command line tool) in our Xcode project linking the VW Foundation Classes while still mixing Vectorworks API calls and testing code in the same source freely. Having a separate testing target is a huge time-saver because it allows me to execute & test the pieces of our code which aren’t dependent on Vectorworks without actually starting up the Vectorworks application (which may take a moment or two).

Additionally, the testing code is also executed in the plug-in’s modules main function when loaded into Vectorworks:

if (action == kVCOMRegisterInterfaces) {
	TestResultStdErr result;
	TestRegistry::Instance().Run(result);
	if (result.FailureCount())
		Debugger();
}

That way I ensure that the testing code is run no matter which environment I’m in. If the tests work in the testing target, but fail when run in Vectorworks, I’m thrown into the debugger right away and get slapped with a rolled-up newspaper.

If you would like to go straight to the final code download, make sure to go straight to the Epilogue.

Enough of the preface. In the next episode, we’ll start coding.

Episodes so far:

You may follow this series via RSS or follow me on Twitter / Facebook.