Tom Van Schaijk Tom Van Schaijk - 1 year ago 81
C# Question

Rotating & printing squares

Got a quick question about an assignment in c#, wpf. The task is to read in an XML file, containing one description for a root panel, and from then on it follows a fixed pattern where each panel has a number of child panels, each of which can have a number of child patterns. Pretty obvious. I can read it in just fine, and traversing the model is no problem.

The thing is: I have to print these panels on a wpf canvas. The relationship between parent and child panels is the following:

  • the root panel has X Y coordinates to determine its starting point. Other panels do not

  • each panel, including the root, has a width and height (not necessarily the same)

  • each panel (except the root panel) has a property 'attachedToSide' which has a value from 0 to 3. The value signifies the side of the parent the child should be placed against.

  • When printing a panel against a parent panel, we should always put the '0' side of the panel against the parents' side.

So to demonstrate: look at the draft below. The root panel is printed. The root has 4 children. Taking the panel to the right of the root. That panel would have a property attachedToSide='1' to signify it should be stuck against the 1-side of its parent panel. Now since the rule is that the 0 side is the one that should stick to the parent, we have to flip it 90°.
And the story goes on like that.

enter image description here

Now, printing itself is no problem. Where I'm kinda struggling is calculating the actual positions of each square. The first children of the parent are easy, but from then on, I have to do some calculations to position them correctly, based on the previous panel, and I don't want to take the route of nested if-statements. There probably exists a really simple algorithm to fix this, but since I'm not home in that field, I'm struggling a bit. Can anyone give me a nudge in the right direction?

Detail: doing it all purely mvvm too (just for the heck of it), so 0 code in the code-behind. The shapes are an itemcollection with a custom itemspaneltemplate and itemtemplate, I'm doing the rotation by binding the rotation angle to a property in my model.

Answer Source

The model for each panel consists of

  X,Y coordinates
  W,H dimensions
  R   rotation value (one of four choices)
  C   a list of up to four children
  A   attached to side

The rotation value can be encoded as: an angle in degrees, an angle in radians, or just a number between 0 and 3. I would choose the 0 to 3 encoding, where the number represents the side at the bottom. So the root panel has a rotation value of 0.

You are given a complete set of parameters (ignoring A) for the root panel. For all the other panels, you have parameters W,H,C,A but you're missing X,Y,R. So your task is to compute X,Y,R for each panel to complete the model.

Computing the rotation value for the child

Consider the following cases which show the four possible children for each orientation of the parent:

enter image description here

The sequences below the drawings are the child R values, ordered by the child's A value. For example, if the parentR is 0, and the childA is 0, the childR is 2. If parentR is 0 and childA is 1, childR is 1, etc.

First thing to note is that the first number in each sequence is the number at the top of the parent. Second thing to note is that the numbers decrease by 1 (as the childA increases), wrapping to 3 after 0.

So if you take the parent's R value, add 6, and subtract the child's A value, and then apply modulo 4, you get the child's rotation value:

childR = (parentR + 6 - childA) % 4;

Computing the Y value for the child

Note that the location of the child depends primarily on the child's rotation value. If the childR is 0, the child is above the parent. If childR is 1, the child is to the right, etc.

So if the childR is odd, the child has the same Y value as the parent. If the childR is 0, then the childY is the parentY adjusted by the child's height. When the childR is 2, then the childY is the parentY adjusted by either the parent width (parentR odd), or the parent's height (parentR even).

Which results in an if-else chain that looks like this:

if ( childR % 2 )                // odd values, child left or right
    childY = parentY
else if ( childR == 0 )          // child above
    childY = parentY - childH
else if ( parentR % 2 )          // odd values, adjust by parent width
    childY = parentY + parentW
else                             // even values, adjust by parent height
    childY = parentY + parentH   

(I'm assuming here that the X,Y coordinate represents the location of the upper-left corner of the panel, and positive Y is down.)

The X calculations are similar to the Y calculations.

So you start at the root, compute X,Y,R for the children of the root, and recursively compute those parameters for each child's children.

That completes your model. Displaying the panels on the view is easy enough, since you have X,Y,W,H,R for each panel.