Project: Step 1
Circular boundaries

Disclaimer: the automatically generated English translation is provided only for convenience and it may contain wording flaws. The original French document must be taken as reference!

Goal: Implementation of a base class representing circular boundaries.

Required concepts: object classes, constructors/destructors, operator overloading.

These concepts are explained in lessons 16 to 19. The exercises in series 19 contain material similar to what you need to program.

Useful files : partie1.zip

Instructions for writing the REPONSES file: see the forum.


Setup

The instructions below assume that you have created the directory cpp/projet. Adapt the commands to the location of your project.

Copy the provided archive partie1.zip to your cpp/projet directory and unzip it using the command unzip partie1.zip (type man unzip in a terminal for more details on this command). If you are working on your own machines:

In QtCreator, configure the project by carefully following the instructions given here: Open the file CMakeLists.txt in the directory src/ and follow the instructions below by placing the folder build at the same level as the folders src or res. Once the project is configured, you should be able to compile and run it. The execution should produce the following output in the “Application output” window:
    ===============================================================================
    All tests passed (60 assertions in 9 test cases)
      
The meaning of this execution will be explained below.
WARNING: the target enabling the compilation of your own contributions is commented out in the provided material. Uncomment it in the CMakeLists.txt file (remove the `#` at the beginning of lines 160 and 161) when you are ready to test the class CircularBoundary that you will be asked to code..

The archive provided contains:

To increase the modularity of your project, prototyping methods and declaring attributes should be placed in a file with the extension .hpp while their definitions should be placed in a file with the extension .cpp.

Here are some useful tips about compilation:
  1. How to avoid multiple inclusions:

    The classes you will be programming in the project will combine in relatively complex ways, and you will sometimes need to include many existing .hpp files at the beginning of a new .hpp file. If files have been included redundantly in these different .hpp files, the compiler will respond with an error message. To avoid this situation, you must place the following directive at the beginning of each myfile.hpp file:
    #pragma once
    
  2. File naming convention:

    Files will have the same names as the classes they contain: for example, Lab.[hpp][cpp] files will contain code related to the Lab class.

    This is, of course, only a convention.

    The lab environment has the particularity of being case-insensitive: Vec2d.hpp will therefore be interpreted as vec2d.hpp (starting with a lowercase “v”). Including the file vec2d.hpp will therefore be interpreted as correct by the compiler, even if your file is called Vec2d.hpp. This is not the case in a classic Unix environment. So be careful to be consistent, as your project must be able to run anywhere.
  3. Compilation in a terminal (Linux, MacOS):
    The provided material can be integrated into QtCreator, where you can compile and run it. On Linux and MacOS, you can also use it perfectly well from the command line, as shown here.

Modules to be programmed

This part of the project consists of a single module (component).

The aim here is to put your initial knowledge of object-oriented programming into practice by programming a utility class representing circular boundaries in a two-dimensional space.

The idea is that the entities in the program (bacteria, nutrient sources, etc.) will evolve in a two-dimensional space.

These entities will encounter each other, and it will be necessary to be able to test this fact in a simple way: for example, if a bacterium encounters a nutrient source, it will be able to consume it. It is therefore important to be able to test that the encounter takes place.

Testing the collision/encounter of two objects can be very complex depending on their actual shapes. In this project, we will use a simplification that consists of approximating the shape of any entity using a circular outline. Collision tests will then be performed simply in relation to these outlines, as illustrated in the image below:

item The collision test between a bacterium and a nutrient source
is simplified by approximating each of these entities
using a circular outline (red circles).

The CircularBoundary class that you are asked to program is used to represent the circular boundaries used for collision testing.

You will be asked to create a class and provide it with several methods. To test these methods, some material is provided. It is described in the section on “Test programs”. We encourage you to read this material as soon as you code your first methods and to try to use it to test your code as you progress.
An object of type CircularBoundary is characterized by:

You will give the CircularBoundary class the following members:

[Question Q1.2] Which overload do you choose for the operators you were asked to code (internal or external) and how do you justify this choice? Think about it and answer this question in your REPONSES file.

[Question Q1.3] Which method arguments, among those you were asked to code above, do you think it would be wise to pass by constant reference? Answer this question in your REPONSES file.

[Question Q1.4] Which of the methods you were asked to code do you think should be declared as const? Answer this question in your REPONSES file.

The tests to be used to validate your implementation of the CircularBoundary class are described in the section “Test programs” and in particular in the section “Test 1”.

Test programs

To test this part of the project, you have:

  1. the circularBoundaryTest target defined in the partie1/src/CMakeLists.txt file, which calls several files;

  2. This target allows you to run the tests/UnitTests/CircularBoundaryTest.cpp test program. This program calls the various functions you coded in the CircularBoundary class to check that they are correct.

Take a quick look at the CMakeLists.txt file to see how this target is defined:

  add_executable(circularBoundaryTest ${TEST_DIR}/UnitTests/CircularBoundaryTest.cpp ${CB_SOURCES} ${CACHE_SOURCES})
  configure_sfml_executable(circularBoundaryTest ....)
allowing all CircularBoundaryTest tests to be run.

To understand how these tests work, open the file src/tests/UnitTests/CircularBoundaryTest.cpp and examine it.

This test program uses a specific framework called Catch, which allows you to perform what is known in programming jargon as unit tests. This involves writing test scenarios for different coded features.

Below, we explain how these tests work.

How unit tests (non-graphical) work

The test program provided in CircularBoundaryTest.cpp defines a scenario (“test case”) for testing collision detection features using the methods you were asked to code in the CircularBoundary class.

The scenario contains a number of messages (labeled with the keywords GIVEN, THEN, or AND_THEN) that describe the conditions under which the various tests are performed and their results. These messages will only be displayed if a test fails.

If all the tests in a scenario run correctly, you will simply see a message similar to this:

===============================================================================
All tests passed 

The tests consist of verifying a number of assertions expressed using features such as CHECK and CHECK_FALSE.

Let's examine, for example, the first test in the scenario (lines 35 to 50).

This test begins by constructing two identical circular contours (b1 and b2 at position (1,1) and radius 2), which are supposed to collide if your code is correct (this is what is displayed using THEN). To test that your code works correctly for this case, you need to perform four CHECK :

  1. that b1 collides with b2;
  2. that b2 collides with b1;
  3. that b1 & b2 returns true;
  4. and that b2 & b1 returns true.

As you can see, a CHECK succeeds if the condition passed to it as an argument returns true (similarly, a CHECK_FALSE succeeds if the condition passed to it as an argument returns false).

Suppose your isColliding method is poorly coded. Running the test program CircularBoundaryTest.cpp will display:

-------------------------------------------------------------------------------
Scenario: Collision/IsInside with CircularBoundary
     Given: Two identical bodies
      Then: they collide
-------------------------------------------------------------------------------
src/Tests/UnitTests/CircularBoundaryTest.cpp:26
...............................................................................

src/Tests/UnitTests/CircularBoundaryTest.cpp:37: FAILED:
  CHECK( b1.isColliding(b2) )
with expansion:
  false

src/Tests/UnitTests/CircularBoundaryTest.cpp:38: FAILED:
  CHECK( b2.isColliding(b1) )
with expansion:
  false

The test therefore displays the name associated with the scenario, the messages associated with GIVEN and THEN to indicate what we have as input (“Two identical bodies”) and what is supposed to be true in this case (“they collide”), followed by the list of failed assertions and their lines.

Running the CircularBoundaryTest target should produce the following output:

CircularBoundary: position = (1, 1), radius = 2
===============================================================================
All tests passed (42 assertions in 1 test case)
  

Note: in QtCreator, this output is visible in the “Application output” window.

This indicates that 42 assertions (CHECK or CHECK_FALSE ) were successfully run as part of a single scenario (“1 test case”).

Note that if the tested features are not present in your code, running the test will cause compilation errors.

For example, suppose that isColliding (taking a CircularBoundary as an argument) is not coded in your code, you will then get an error message such as

error: no matching function for call to 'Body::isColliding(Body&)'
             CHECK(b1.isColliding(b2));
If you want to test the methods as you code them, which is recommended, simply comment out the CHECK or CHECK_FALSE that call methods that have not yet been coded.

Test 1: Features of CircularBoundary

Tests are numbered globally throughout the project. This is the first test in the project. Each test is a graded item.

Running the target CircularBoundaryTest should produce the following output:

CircularBoundary: position = (1, 1), radius = 2
===============================================================================
All tests passed (42 assertions in 1 test case)

The message CircularBoundary: position = (1, 1), radius = 2 should also be displayed to show that line 44 is executing correctly (i.e., your overload of the << does what is expected of it).


Back to main document