Programming


21
Apr 11

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

In Episode 4 we finally saw the fruits of our labor – TDD-style. What’s left? We need to come up with some code for the front tiles / segments.

Let’s get right to it. Here the first test. We assume that there’s class FrontModel with a method GetOrigin(), which returns the position of the leftmost bottom front tile.

TEST_N(FrontModelPositionOfFirstSegment)
{
	SimpleCabinetModel simpleCabinet(600, 720, 800);

	FrontModel frontModel;
	simpleCabinet.GetFrontModel(frontModel);

	CHECK_EQUAL(WorldPt(SimpleCabinetModel::kBoardThickness, 0),
				frontModel.GetOrigin());

}

This is another one of these “Get’s us going” tests. Not an awful lot of thought need to make it pass, but it puts us in the right mood for the next challenge.


class SimpleCabinetModel {

public:

	// ...

	void GetFrontModel(FrontModel& model) const {
		model.SetOrigin(WorldPt(kBoardThickness, 0));
	}
};	

class FrontModel {

public:

	FrontModel() : fOrigin(WorldPt (0, 0)) {
	}

	WorldPt GetOrigin() {
		return fOrigin;
	}

	void SetOrigin(const WorldPt& origin) {
		fOrigin = origin;
	}

private:

	WorldPt fOrigin;

};

What’s next? Look at the “spec” in Episode 0, it looks like the front tiles are made of rows and columns – but let’s not jump to conclusions yet. Dr. Bob is just a quack, not a CS major. The next simple test which comes to our mind is checking for the position of the second front tile – which should be located to the right of the first front tile (we assume that both tiles fit into the cabinet, of course).

TEST_N(FrontModelPositionOfSecondSegment)
{
	SimpleCabinetModel simpleCabinet(600, 720, 800);

	FrontModel frontModel;
	simpleCabinet.GetFrontModel(frontModel);

	CHECK_EQUAL(frontModel.GetOrigin() + WorldPt(SimpleCabinetModel::kSegmentWidth, 0),
				frontModel.GetPositionOfSecondSegment());
}

In order to calculate the second tiles position, we need the width of a front tile. For now, we’re accessing SimpleCabinetModel::kSegmentWidth from FrontModel::GetPositionOfSecondSegment() for this purpose.

	WorldPt GetPositionOfSecondSegment() const {
		return fOrigin + WorldPt(SimpleCabinetModel::kSegmentWidth, 0);
	}

Doesn’t feel absolutely right yet, but we’re confident that this will be smoothed out over the next tests & episodes.

After the raving success with SimpleCabinetCreator in Episode 4, let’s see if we can create the Vectorworks geometry for the first & second tile before continuing our journey fleshing out the FrontModel. We have the uneasy feeling that The reason why

Let’s start with the plug-in object’s Recalculate() function. We spice up SimpleCabinetCreator a bit, as we need access to both the SimpleCabinetModel and FrontModel. Asking SimpleCabinetModel to “fill in the blanks” in FrontModel didn’t feel right, so let’s change it and pass the origin as a parameter to FrontModel's constructor. Way better.

EObjectEvent CObject_EventSink::Recalculate()
{
	VWParametricObj object(fhObject);
	double width = object.GetParamReal("Width");
	double height = object.GetParamReal("Height");
	double depth = object.GetParamReal("Depth");

	SimpleCabinetModel simpleCabinetModel(width, height, depth);

	FrontModel frontModel(simpleCabinetModel.GetFrontModelOrigin());

	SimpleCabinetCreator creator(object);
	creator.CreateFrom(simpleCabinetModel, frontModel);

	return kObjectEventNoErr;
}

Where does SimpleCabinetCreator get the front symbols name from? FrontModel, of course. We hard-wire the name of the symbol for now:

class SimpleCabinetCreator {

public:

	SimpleCabinetCreator(const VWParametricObj& pio) : fSimpleCabinet(pio) {
	}

	void CreateFrom(const SimpleCabinetModel& cabinetModel, const FrontModel& frontModel) {

		CreateFrom(cabinetModel);
		CreateFrom(frontModel);
	}

private:

	void CreateFrom(const SimpleCabinetModel& cabinetModel) {
		// ...
	}

	void CreateFrom(const FrontModel& frontModel) {

		MCObjectHandle firstFrontSegment = gSDK->PlaceSymbolByNameN(frontModel.GetFrontSymbolName(), frontModel.GetOrigin());
		fSimpleCabinet.AddObject(firstFrontSegment);

		MCObjectHandle secondFrontSegment = gSDK->PlaceSymbolByNameN(frontModel.GetFrontSymbolName(), frontModel.GetPositionOfSecondSegment());
		fSimpleCabinet.AddObject(secondFrontSegment);
	}

	VWParametricObj fSimpleCabinet;

};

Firing up Vectorworks 2011, we are supposed to be - once again - pleased with the results. However, a closer inspection reveals that the front tiles collide with the bottom board - ah, we forgot the tiles z position in our model.

Episode 5

Tune in next week when you heard Dr. Bob say: "Nurse Piggy, would you mind slapping this developer here with a rolled-up newspaper?"

Previous Episode | Next Episode


19
Apr 11

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

In Episode 3 – Not doing the same sketch twice, we made some good progress on a class finally capable of describing the geometry of the cabinet’s parts. In this episode, we will create Vectorworks geometry. It seems like a good idea to separate the creation of the geometry from the actual model (much in the way a View is separated from a Model in MVC). Let’s call the new SimpleCabinetCreator:

class SimpleCabinetCreator {

public:

	static void CreateFrom(const SimpleCabinetModel& model) {

		MCObjectHandle leftSide = gSDK->CreateExtrude(0, model.GetHeight());
		gSDK->AddObjectToContainer(gSDK->CreateRectangle(model.GetLeftSideBase()), leftSide);

		MCObjectHandle rightSide = gSDK->CreateExtrude(0, model.GetHeight());
		gSDK->AddObjectToContainer(gSDK->CreateRectangle(model.GetRightSideBase()), rightSide);

		MCObjectHandle top = gSDK->CreateExtrude(model.GetTopZPosition(), model.GetTopZPosition() + model.kBoardThickness);
		gSDK->AddObjectToContainer(gSDK->CreateRectangle(model.GetTopBottomBase()), top);

		MCObjectHandle bottom = gSDK->CreateExtrude(0, model.kBoardThickness);
		gSDK->AddObjectToContainer(gSDK->CreateRectangle(model.GetTopBottomBase()), bottom);

	}

};

Looks good so far. The main takeaway point is that the Creator class shouldn’t do any calculation. It should be translating the model straight into Vectorworks geometry without any logic on it’s own.

While we’re at it: How about renaming SimpleCabinet to SimpleCabinetModel? This would be a nice symmetrical way of naming the classes: SimpleCabinetModel and SimpleCabinetModel. Let’s do that.

Now for the grand finale of this episode. The plug-in object’s Recalculate() method is pretty straightforward. All the basic building blocks are already in place:

EObjectEvent CObject_EventSink::Recalculate()
{
	VWParametricObj object(fhObject);
	double width = object.GetParamReal("Width");
	double height = object.GetParamReal("Height");
	double depth = object.GetParamReal("Depth");

	SimpleCabinetModel simpleCabinetModel(width, height, depth);
	SimpleCabinetCreator::CreateFrom(simpleCabinetModel);

	return kObjectEventNoErr;
}

We get the width, height & depth from the parametric object, create a model and ask the SimpleCabinetCreator to create the geometry defined in the model. Let’s fire up Vectorworks and see if everything is working as expected. And – much to our surprise – it’s working out as expected:

Episode4

Sounds like a great way to end this episode. Let’s see if we can add the front tiles in the upcoming episodes. TDD-style, of course.

Make sure to tune in next week when you hear Dr. Bob say: “A third class? I’m a quack, not a CS major.”.

Previous Episode | Next Episode


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.


9
Oct 09

The Atlassian Dragons Exercise

The installation process for the Atlassian Starter suite – Crowd, Bamboo, Fisheye, JIRA, Greenhopper and Confluence – is quite daunting and takes about 5 hours+ (way more on my Parallels VM setup, but I did expect that).

It’s obvious that the different Atlassian products have been built by different teams, at different times and sometimes even different companies. Although, AFAIK, all products are built with basically the same base technology (J2EE), each product has some minor differences in

  • Installation
  • Configuration
  • Directory set-up
  • Starting up / Stopping products (e.g. there’s no shutdown command for Crowd, Bamboo automatically installs as a service)
  • Configuration files

If the suite has to be installed manually, consistency in the setup process trumps everything. This is even more relevant if the suite is installed by a non-IT, non-Java plain old-fashioned C++ hacker like me.
Generally, editing the configuration files was no big deal, although the sheer number of changes necessary induced cross-eyes at times.

Including Crowd into the installation process made the setup process quite involved and complicated. Although single sign-on is quite a feature, I wouldn’t consider it crucial for a 10 user set-up. I would’ve preferred to make integration with Crowd an optional exercise. Plus, removing Crowd from the standard equation would have enabled more detailed feedback on setting up the different applications’s integration features.

Kudos to the Atlassian documentation team responsible for the detailed step-by-step descriptions. It was close to perfect, just very very minor errata in terms of version numbers. A few more screenshots would have been helpful, but would have made the endeavour of documenting the suite’s installation process not only daunting, but outright impossible to maintain over time.

I was very disappointed that Crucible was neither part of the exercise nor part of the $10 offer. Atlassian, please make Crucible part of the Dragons exercise and part of the $10 / 10 users offer. I’m sure there were very good technical and/or business reasons not to include it, but if the Atlassian team can pull of a stunt like the Dragons exercise, I know they can pull off including Crucible, too. It just takes a few more beers, I suppose. German beer, of course. :-)


5
Apr 09

English as a second language

Jeff Attwood – The Ugly American Programmer:

Advocating the adoption of English as the de-facto standard language of software development is simple pragmatism, the most virtuous of all hacker traits.

Let’s face it: If you aren’t able to read & write English in a reasonably fluent fashion, you’re in trouble – you won’t be able to participate in any discussion or project on a global scale. Plus, you’ll be late to every party – and you like the latest fad as everyone else, don’t you?

Yup, this requires some work on your behalf. Get over it and start working on it. Right now.


15
Mar 09

On Functional Specifications

Reading through Christoph’s take on Joel’s latest “How to be a Program Manager“, I stumbled across the term “Functional spec”.
Coming from a C.S. background (admittedly, a sin of my youth), I continue to struggle with the term “specification”, as I associate it with

I don’t think you can fully understand the problem domain, the software to be written and it’s interaction with users or other systems by creating a text document in plain English (or any natural language of your choice) deliberately sprinkled with some graphics – of course you can’t do it with predicate logic either.

The main reason is that while creating your “specification” you will lack the feedback you get from actually implementing the system and from putting your code in front of users. This is way more valuable than sitting in an ivory tower waxing eloquently about a system’s intended features.

So I suggest to put another label on the document Joel is talking about – “Functional Overview”, “Functional Description” or “Functional Outline”. Don’t burden a useful document and its intended purpose with putting an inappropriate and misleading label on it.

Now that we this issue out of our way, we can start having a more useful discussion about what to put in the “Functional spec”, err, “Functional Overview”. Here’s my take on it:

Here’s what we know now. This is what we have to be aware of when starting out. This is how the interaction with the user might work out. Here are the external dependencies on other systems.

Just write enough “Functional Overview” that you are confident in your ability to move through the problem domain and take turns as you learn new things implementing the system and putting it in from of customers. You will be way better off investing your energy in defining the expected inputs & outputs of the system (and its components) as automated functional, acceptance and unit tests. And getting the system out to users & testers in order to get real feedback of a real system and learn about the shortcomings of your initial specification.

There. I said it.


15
Mar 09

Effective C++ – Third Editon

Re-Read Effective C++, Third Edition by Scott Meyers over the weekend – a fantastic book about the intricacies of C++.

The very existence of such a book is a testament to the many things which are wrong with C++.

[Update: As I'm writing this, Burce Eckel has posted a thoughtful article about The Positive Legacy of C++ and Java - which approaches the topic of C++ being a complex & convoluted language from a different angle]

[Update: John D. Cook mentions another interesting post (dating back from 2007) on this very topic]

[Update: The C++ FQA Lite sums up the main intricacies quite nicely]

Get Adobe Flash playerPlugin by wpburn.com wordpress themes