30
Apr 11

Twitter Updates for 2011-04-30


27
Apr 11

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

Now that we’re able to place a grid of front tiles / segments, the only thing left is the placement of the right / top blind front panels – if the front tiles don’t fit properly. As always, we’re starting with a very basic test. The right blind front (if there’s one) will be placed on the bottom board, therefore start at z == SimpleCabinetModel::kBoardThickness.

TEST_N(FrontModelRightBlindFront)
{
	SimpleCabinetModel simpleCabinet(2 * 200 + 2 * SimpleCabinetModel::kBoardThickness + 50, 
									 3 * 150 + 2 * SimpleCabinetModel::kBoardThickness + 70, 800);
	
	FrontModel frontModel(simpleCabinet.GetFrontModelOrigin(), simpleCabinet.GetFrontWidth(), simpleCabinet.GetFrontHeight());
	
	CHECK_EQUAL(SimpleCabinetModel::kBoardThickness, frontModel.GetRightZBlindFront());
}

Well, you could see that one coming, making the test pass is rather trivial – but it gets us on the track to more sophisticated functionality.

	WorldCoord GetRightZBlindFront() const {
		return fOrigin.z;
	}

Let’s add another check to the test. The height of the right blind front should be the height of the front model.

	CHECK_EQUAL(3 * 150 + 70, frontModel.GetRightBlindFrontHeight());

Quite obvious and should be easy to implement. There’s already an instance variable fHeight in the FrontModel:

	WorldCoord frontModel.GetRightBlindFrontHeight() const {
		return fHeight;
	}

The third component needed to generate the geometry for the right blind front model is the base rectangular shape for the extrude. Another check for our test.

	CHECK_EQUAL(WorldRect(SimpleCabinetModel::kBoardThickness + frontModel.GetNumColumns() * FrontModel::kSegmentWidth, 
						  FrontModel::kSegmentDepth,
						  SimpleCabinetModel::kBoardThickness + simpleCabinet.GetFrontWidth(), 0), 
				frontModel.GetRightBlindFrontBase());

Introducing two additional constants for the tile/segment’s width and depth, making the test pass requires a few lines of code:

class FrontModel {

	WorldRect GetRightBlindFrontBase() const {
		WorldCoord xStart = fOrigin.x + GetNumColumns() * kSegmentWidth;
		WorldCoord xEnd = fOrigin.x + fWidth;
		
		WorldRect r(0, 0, 0, 0);
		if (xStart != xEnd)
			r.Set(xStart, kSegmentDepth, xEnd, 0);

		return r;
	}
};

The top blind front is tackled the same way – We need it’s z position, it’s x dimensions and it’s height. Not an awful lot to see there. Now let’s check if we’re able to generate Vectorworks geometry with this model.

	void CreateFrom(const FrontModel& frontModel) {
		
		// ...

		WorldRect topBlindFrontBase = frontModel.GetTopBlindFrontBase();
		if (topBlindFrontBase != WorldRect(0, 0, 0, 0)) {
			MCObjectHandle topBlindFront = gSDK->CreateExtrude(frontModel.GetTopZBlindFront(), frontModel.GetTopZBlindFront() + frontModel.GetTopBlindFrontHeight());
			gSDK->AddObjectToContainer(gSDK->CreateRectangle(frontModel.GetTopBlindFrontBase()), topBlindFront);
		}
		
		WorldRect rightBlindFrontBase = frontModel.GetRightBlindFrontBase();
		if (rightBlindFrontBase != WorldRect(0, 0, 0, 0)) {
			MCObjectHandle rightBlindFront = gSDK->CreateExtrude(frontModel.GetRightZBlindFront(), frontModel.GetRightZBlindFront() + frontModel.GetRightBlindFrontHeight());
			gSDK->AddObjectToContainer(gSDK->CreateRectangle(frontModel.GetRightBlindFrontBase()), rightBlindFront);
		}
		

In fact, we are:

Episode 7

Tune in next weeks when you hear Dr. Bob say: “Let’s sum it up, post the code and I’m outta here”.

Previous Episode | Next Episode


27
Apr 11

Twitter Updates for 2011-04-27


26
Apr 11

Twitter Updates for 2011-04-26


24
Apr 11

Twitter Updates for 2011-04-24


24
Apr 11

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

We left off in Episode 5 trying to avoid Dr. Bob hitting us with a rolled-up newspaper as we forgot to account for the front tiles’s z position. Let’s see if we can fix this. First, we change the test for the front model origin to expect a WorldPt3 which, obviously, does contain a z-component.

TEST_N(FrontModelOrigin)
{
	SimpleCabinetModel simpleCabinet(600, 720, 800);
	
	FrontModel frontModel(simpleCabinet.GetFrontModelOrigin());
	
	CHECK_EQUAL(WorldPt3(SimpleCabinetModel::kBoardThickness, 0, SimpleCabinetModel::kBoardThickness), frontModel.GetOrigin());
}

Making this test pass requires a series of simple changes to FrontModel.

class FrontModel {

public:

	FrontModel(const WorldPt3& origin) :
		fOrigin(origin) {
	}

	WorldPt3 GetOrigin() const {
		return fOrigin;
	}

private:

	WorldPt3 fOrigin;
}

After making these changes gcc tells us that we should probably fix the tests FrontModelPositionOfFirstSegment and FrontModelPositionOfSecondSegment in order to make this thing compilable again. As in FrontModelOrigin, this requires us to replace WorldPt with WorldPt3 and we’re basically done.

With this out of the way, let’s concentrate on generalizing our FrontModel. So far, we’re able to calculate the position of the first and second segment / tile (assuming both fit into the cabinet). Looking at the screenshot in episode 0, it sure looks like the front tiles should be arranged in rows & columns. Let’s see if we can work towards this. How about computing the number of front tile rows & columns which “fit” into a given cabinet? Let’s start with a simple test – if the cabinet is too small, no front tiles will fit at all.

TEST_N(FrontModelNoRowsNoColumnsNotWideEnough)
{
	SimpleCabinetModel simpleCabinet(150, 720, 800);
	
	FrontModel frontModel(simpleCabinet.GetFrontModelOrigin(), 
						  simpleCabinet.GetFrontWidth(), 
						  simpleCabinet.GetFrontHeight());
	
	CHECK_EQUAL(0, frontModel.GetNumRows());
	CHECK_EQUAL(0, frontModel.GetNumColumns());
}

In order to be able to compute the number of rows & columns, we have to supply the FrontModel with the space available for placing front tiles. So, we need tests to for these functions. Let’s #if 0 the last test for a moment and write new tests for GetFrontWidth() and GetFrontHeight().

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

	CHECK_EQUAL(600 - 2 * SimpleCabinetModel::kBoardThickness, 
				simpleCabinet.GetFrontWidth());
	CHECK_EQUAL(720 - 2 * SimpleCabinetModel::kBoardThickness, 
				simpleCabinet.GetFrontHeight());
}

Let’s make this test pass.


class SimpleCabinetModel {

public:
	
	WorldCoord GetFrontWidth() const {
		return fWidth - 2 * kBoardThickness;
	}
	
	WorldCoord GetFrontHeight() const {
		return fHeight - 2 * kBoardThickness;
	}
};

Backtracking to our test FrontModelNoRowsNoColumnsNotWideEnough, we change FrontModel‘s constructor (again).

class FrontModel {

public:
	
	FrontModel(const WorldPt3& origin, WorldCoord width, WorldCoord height) : 
		fOrigin(origin), fWidth(width), fHeight(height) {
	}
};

What was our intital goal? Ah, computing the number of rows & columns (in order to make the test pass). Well, that’s straightforward:

class FrontModel {

public:
	
	long GetNumRows() const {
		return 0;
	}
	
	long GetNumColumns() const {
		return 0;
	}
};

Indeed, it’s pretty straightforward for the test we’re trying to make pass. We just write enough code to make the test pass. And returning 0 makes the test pass. Let’s move on to the next test.

TEST_N(FrontModelThreeRowsTwoColumnsFits)
{
	SimpleCabinetModel simpleCabinet(2 * 200 + 2 * SimpleCabinetModel::kBoardThickness, 
									 3 * 150 + 2 * SimpleCabinetModel::kBoardThickness, 
									 800);
	
	FrontModel frontModel(simpleCabinet.GetFrontModelOrigin(), 
						  simpleCabinet.GetFrontWidth(), 
						  simpleCabinet.GetFrontHeight());
	
	CHECK_EQUAL(3, frontModel.GetNumRows());
	CHECK_EQUAL(2, frontModel.GetNumColumns());
}

Now is the right time to flesh out GetNumRows() and GetNumColumns().

class FrontModel {

public:
	
	long GetNumRows() const {
		return MyMathUtils::fdiv(fHeight, SimpleCabinetModel::kSegmentHeight);
	}
	
	long GetNumColumns() const {
		return MyMathUtils::fdiv(fWidth, SimpleCabinetModel::kSegmentWidth);
	}
};	

In order to get the number of front tiles fitting into a row, we need to divide the overall width available for the front tiles and divide it (as in division of integers) by the width of a segment. Same for the number of front tiles fitting into a column.

Where are we at? We now have code for calculating the number of tiles in x direction (columns) and z direction (rows). If we would have a function computing the x, y & z position of a front tile at a given row & column we could certainly beef up SimpleCabinetCreator::CreateFrom() with a nice loop placing all the front tiles. Let’s write a test for this function computing a tiles position:

TEST_N(FrontModelPositionOfARowColumn)
{
	SimpleCabinetModel simpleCabinet(1200, 720, 800);
	
	FrontModel frontModel(simpleCabinet.GetFrontModelOrigin(), 
						  simpleCabinet.GetFrontWidth(), 
						  simpleCabinet.GetFrontHeight());

	WorldPt3 expectedPosition = frontModel.GetOrigin() +
								WorldPt3(3 * SimpleCabinetModel::kSegmentWidth, 0, 
										 2 * SimpleCabinetModel::kSegmentHeight)
	CHECK_EQUAL(expectedPosition, 
				frontModel.GetSegmentPosition(3, 2));
}

Here’s the code to make the test pass:

class FrontModel {

public:
	
	WorldPt3 GetSegmentPosition(long column, long row) const
	{
		WorldPt3 position(GetOrigin());
		
		position += WorldPt3(column * fSegmentWidth, 
							 0,
							 row * fSegmentHeight);
		
		return position;
	}
};

And for the grand final of this episode, let’s tackle placing the front tile symbols in SimpleCabinetCreator::CreateFrom(). With the preparatory work we’ve done in this episode it should be pretty straightforward – and it is:

	
	void CreateFrom(const FrontModel& frontModel) {
		
		for (long i = 0; i  frontModel.GetNumColumns(); i++) 
			for (long j = 0; j < frontModel.GetNumRows(); j++) {
				VWObject frontSegment = gSDK->PlaceSymbolByNameN(frontModel.GetFrontSymbolName(), WorldPt(0, 0));
				frontSegment.MoveObject3D(frontModel.GetSegmentPosition(i, j)); 
				fSimpleCabinet.AddObject(frontSegment);
			}	
	}

Episode 6

Tune in next week when you hear Nurse Piggy say: “Are we done yet? I have a date with a certain frog for dinner… “.

Previous Episode | Next Episode


22
Apr 11

Twitter Updates for 2011-04-22

  • Developing a @Vectorworks plug-in object TDD style – Episode 5 – A third class – http://t.co/30Vy9BP #in #fb #
  • Mir dünkt als ob sich gerade ein PR-Desaster entwickelt… #iphone #
  • Kann sich jemand noch an die Zeiten erinnern als wir jeden Tag eine neue Microsoft Sau durch's Dorf getrieben haben? #
  • The times they are a changing… #
  • RT @FridayBruceFix Bruce Springsteen, Stand On It, Janey Don't You Lose Heart http://bit.ly/ikAuUt #

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(&quot;Width&quot;);
	double height = object.GetParamReal(&quot;Height&quot;);
	double depth = object.GetParamReal(&quot;Depth&quot;);
	
	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


21
Apr 11

Twitter Updates for 2011-04-21


20
Apr 11

Twitter Updates for 2011-04-20

  • Andrea, mach' den Sack zu ich will Snooker schauen, dabei bin ich produktiver als bei Tennis… #eurosport #
  • Developing a @Vectorworks plug-in object TDD style – Episode 4 – Creating Vectorworks objects – http://t.co/bpslqoG #