Monday 27 April 2020

Chapter 13 // Exercise 6 - Principles & Practice Using C++

In this exercise I am using Visual Studio 2017 and the graphics files found here:
https://github.com/l-paz91/principles-practice/tree/master/Graphics%20Files

Chapter 13 // Exercise 6

Write a program that draws a class diagram like the one in Section 12.6. It will simplify matters if you start by defining a Box class that is a rectangle with a text label.

Github: https://github.com/l-paz91/principles-practice/tree/master/Chapter%2013/Exercise%206


I originally decided to derive the Box class and add text to it, however it proved a little cumbersome. So instead, I derived the Text class and gave it a Box as a member variable. The only difficult part was giving the box the correct length. I tried searching the FLTK documentation however there is no way to get the full length the Text string. There is a function called fl_width() which takes in a char and calculates the width of it in pixels using the font size given however, it always returned -1 for me so I don't think it works properly. So instead, I made the width the number of characters multiplied by the font size. It isn't perfect but it ensures the text box is always big enough.



EDIT 12/05/2020
So I found out the reason fl_width() is returning -1 is because the font_descriptor() is null in the graphics driver. This is because the font is never actually set, we just call the default Font constructor in Text::draw_lines().

To enable fl_width() to work the font must be intialised in the text constructor. 


fl_width() then takes in a c-style string and determines the width of pixels in each character. This does not account for spacing though so in some cases the box may not encapsulate the text. for this I just added on some padding. Not perfect but close enough.


I provided a function in the Text class to be able to do this:


Friday 24 April 2020

Chapter 13 // Exercise 5 - Principles & Practice Using C++

In this exercise I am using Visual Studio 2017 and the graphics files found here:
https://github.com/l-paz91/principles-practice/tree/master/Graphics%20Files

Chapter 13 // Exercise 5

Define the functions from exercise 4 for a Circle and an Ellipse. Place the connection points on or outside the shape but not outside the bounding rectangle.

Github: https://github.com/l-paz91/principles-practice/tree/master/Chapter%2013/Exercise%205


Following on from Exercise 4, Circle and Ellipse can now override the function in Shape. North, east, south and west are easy to draw for circle shapes from the center point and the other points can be easily obtained by using some trigonometry. You can just use the radius and given points to mark on the bounding box (Bjarne does allow it) but it's pretty simple to mark on the circle itself.

Ellipse is slightly different in that the radius can differ based on width/height.



Thursday 23 April 2020

Chapter 13 // Exercise 4 - Principles & Practice Using C++

In this exercise I am using Visual Studio 2017 and the graphics files found here:
https://github.com/l-paz91/principles-practice/tree/master/Graphics%20Files

Chapter 13 // Exercise 4

Define functions n(), s(), e(), w(), center(), ne(), se(), sw(), and nw(). Each takes a Rectangle argument and returns a Point. These functions define "connection points" on and in the rectangle. For example, nw(r) is the northwest (top left) corner of a Rectangle called r.

Github: https://github.com/l-paz91/principles-practice/tree/master/Chapter%2013/Exercise%204

Thinking about it, this makes a lot more sense to have as operations inside the Rectangle class than outside. The next exercise wants the same functions to be able to work with circles and ellipses so I decided to stick them in Shape. After some thinking and typing out all the function declarations, I figured there would be quite a bit of code duplication so I decided to create an enum of Point Directions that you can pass into one function and it returns the correct Point.

For a Circle and an Ellipse, the first initial point is the center whereas for a Rectangle its the top left. Instead of doing a dynamic_cast to determine what type of shape we are dealing with and then selecting on that; I decided to make the function virtual so a derived type can override it with it's own code if they wish.

Bjarne hasn't explicitly mentioned virtual and overriding outside a few brief mentions, but we are using that type of code so I don't consider it too advance for this stage.

In order to display all the marks I had to modify the graph files a bit as the Mark class was deriding from Marks which derides Marked_Polyline. This meant that all the marks were drawing lines between them which only Marked_Polyline should be doing. I uncommented the version of Marks below Mark and renamed it and did the same for the Marks::draw_line() function in Graph.cpp. 




Wednesday 22 April 2020

Chapter 13 // Exercise 3 - Principles & Practice Using C++

In this exercise I am using Visual Studio 2017 and the graphics files found here:
https://github.com/l-paz91/principles-practice/tree/master/Graphics%20Files

Chapter 13 // Exercise 3

Define a class Arrow, which draws a line with an arrowhead. 

Github: https://github.com/l-paz91/principles-practice/tree/master/Chapter%2013/Exercise%203


Again, I wanted to give a way to adjust the size of the arrow head so this took a little longer as I thought about it. Then as I spent a few hours banging my head against the wall I began to realise that this would take quite a bit of maths to solve. But I didn't know what maths. After more hours of googling I came across this stack overflow post which started to make some sense:
https://stackoverflow.com/questions/10316180/how-to-calculate-the-coordinates-of-a-arrowhead-based-on-the-arrow

I knew there had to be some vector stuff involved as when I went down the trigonometry route; I discovered that the two "back points" could potentially be infinite between the mid-base point of the triangle and the tip.

With vectors though (not std::vector), the direction of the arrow helps narrow down those two back points. Eventually I came across this post:
https://math.stackexchange.com/questions/927802/how-to-find-coordinates-of-3rd-vertex-of-a-right-angled-triangle-when-everything?rq=1

Which started to give me some actual formulas and "normal-ish" words to try and figure out what I had to do. And then I found this post off the side of it:
https://math.stackexchange.com/questions/2125690/find-coordinates-of-3rd-right-triangle-point-having-2-sets-of-coordinates-and-a

This link was the jackpot. Thank you Stack Exchange user Futurologist, you're a genius. This formula was exactly what I had been looking for.

Here is one of the arrows drawn by my program (with some annotations in paint):


We know the locations of the following points:
x1, y1 == p1.x, p1.y
x2, y2 == p2.x, p2.y
x3, y3 == p3.x, p3.y

I wanted the arrow tip to be at P2 with each side of the arrow to have a length of arrowSize. Point 3 was found by using a formula to find a point on a line given two points, the full distance and the distance away from Point 2.

The exact formula above produced the correct Y results but the X ones were a little off. I figured it was because the triangle was slightly different. I ended up using Point 3 (instead of x2,y2) to get the co-ordinates I was looking for. This gave me the left hand corner and to get the right; I simply reversed the P1 and P2 coordinates.

I started this at 4pm and just finished writing this post at 12:30am. At least I've learnt something extremely useful.

The arrow can change size, have a different fill colour to line colour, have the line style changed to dashes/dots, and increase/decrease the weight of the line; whatever takes your fancy.

Tuesday 21 April 2020

Chapter 13 // Exercise 2 - Principles & Practice Using C++

In this exercise I am using Visual Studio 2017 and the graphics files found here:
https://github.com/l-paz91/principles-practice/tree/master/Graphics%20Files

Chapter 13 // Exercise 2

Draw a box with rounded corners. Define a class Box, consisting of four lines and four arcs.

Github: https://github.com/l-paz91/principles-practice/tree/master/Chapter%2013/Exercise%202


This could've been done quickly however I wanted to be able to give users a way of adjusting the 'roundness' of the corners and I was stumped (I'm not good at this type of stuff...I just like to make things go faster). I got it wrong a couple of times due to not taking the time to understand how  fl_arc() draws.


The arc is basically determined by the radius of the circle it calculates, so by supplying a "roundness" variable we can use that to get the radius of the arcs (by simply dividing it by 2). Perhaps a better name for it would be "Circle Width" but I don't think and end user would find that useful.

The cyan box was given a 'roundness' of 20 and the black box 100. Fl_arc() really confused me in this because I didn't stop to think about the point at where each arc was being drawn from. Fl_arc() simply draws a portion of a given circle from the top left hand corner. So that would mean our top right-hand arc x,y co-ordinates lie before the end of the top vertical line (exactly the end minus the radius). 

When drawing the lines, they use the position of the arcs (plus either the radius or roundness) to determine the length/height they should be; thus allowing them to adapt to tighter corners (but not exceed the width/height supplied).

A quick note on fl_arc as well. Fl_arc() requires you to give two angles (with the first smaller or equal to the second). If you imagine a clock face, FLTK draws an arc clockwise with 3 being 0 (for some odd reason). So the top left-hand arc would be the angles 90 to 180 (or 9 to 12).

To fill in the box, fl_pie() just needs to be supplied with the same data given to fl_arc() and then draw 3 rectangles using fl_rectf().









Sunday 19 April 2020

Chapter 13 // Exercise 1 - Principles & Practice Using C++

In this exercise I am using Visual Studio 2017 and the graphics files found here:
https://github.com/l-paz91/principles-practice/tree/master/Graphics%20Files

Chapter 13 // Exercise 1

Define a class Arc, which draws a part of an ellipse. Hint: fl_arc().

Github: https://github.com/l-paz91/principles-practice/tree/master/Chapter%2013/Exercise%201


If you do a search for fl_arc in the current code you can see it's already used in the Circle and Ellipse draw_lines(). It pretty obvious to just copy the class code for a Circle or an Ellipse and simply call it the 'Arc' class. 

Don't do what I did though and forget to change the fill and color properties...I ended up drawing it off somewhere else and the fill just filled the entire circle. As Treebeard once wisely said "don't be hasty".





Wednesday 1 April 2020

Chapter 13 // Drill 5 - Principles & Practice Using C++

In this exercise I am using Visual Studio 2017 and the graphics files found here:
https://github.com/l-paz91/principles-practice/tree/master/Graphics%20Files

Chapter 13 // Drill 5

Add a 100-by-100 image. Have it move around from square to square when you click the "Next" button. Just put wait_for_button() in a loop with some code that picks a new square for your image.

Github: https://github.com/l-paz91/principles-practice/blob/master/Chapter%2013/Drills/Drill%205


The move function doesn't actually do what I wanted it to do in this exercise as if you go look at it's implementation in Graph.h you'll see that it actually adds the given co-ordinates to the existing ones. I realised this when I was wondering where the image was while furiously clicking next; the x and y values were around 8000 and 9000 by that point. So instead, I just made a new function within the Image struct that allows you to re-set the points for the image. Shape has a set_point() function however it's protected which means that derived classes can only access it from within the class.



 (Imagine the doge jumping around...)