Skip to content

Module 02 Solutions

Download Module 02 Solutions

The four required members for OCF.

Fixed.hpp
class Fixed {
private:
int _rawValue;
static const int _fractionalBits = 8;
public:
Fixed(); // Default constructor
Fixed(const Fixed& other); // Copy constructor
Fixed& operator=(const Fixed& other); // Assignment operator
~Fixed(); // Destructor
int getRawBits() const;
void setRawBits(int const raw);
};
Fixed.cpp
Fixed::Fixed() : _rawValue(0) {
std::cout << "Default constructor called" << std::endl;
}
Fixed::Fixed(const Fixed& other) : _rawValue(other._rawValue) {
std::cout << "Copy constructor called" << std::endl;
}
Fixed& Fixed::operator=(const Fixed& other) {
std::cout << "Copy assignment operator called" << std::endl;
if (this != &other)
_rawValue = other._rawValue;
return *this;
}
Fixed::~Fixed() {
std::cout << "Destructor called" << std::endl;
}

Converting between int, float, and fixed-point.

Fixed.cpp (conversions)
// From int: shift left by fractional bits
Fixed::Fixed(const int value)
: _rawValue(value << _fractionalBits) {}
// From float: multiply by 2^8 and round
Fixed::Fixed(const float value)
: _rawValue(roundf(value * (1 << _fractionalBits))) {}
// To int: shift right
int Fixed::toInt() const {
return _rawValue >> _fractionalBits;
}
// To float: divide by 2^8
float Fixed::toFloat() const {
return (float)_rawValue / (1 << _fractionalBits);
}
// Stream operator (non-member)
std::ostream& operator<<(std::ostream& os, const Fixed& fixed) {
os << fixed.toFloat();
return os;
}

Comparison, arithmetic, and increment operators.

Fixed.hpp (operators)
// Comparison
bool operator>(const Fixed& other) const;
bool operator<(const Fixed& other) const;
bool operator>=(const Fixed& other) const;
bool operator<=(const Fixed& other) const;
bool operator==(const Fixed& other) const;
bool operator!=(const Fixed& other) const;
// Arithmetic
Fixed operator+(const Fixed& other) const;
Fixed operator-(const Fixed& other) const;
Fixed operator*(const Fixed& other) const;
Fixed operator/(const Fixed& other) const;
// Pre-increment: ++a
Fixed& operator++();
// Post-increment: a++
Fixed operator++(int);
// Static min/max
static Fixed& min(Fixed& a, Fixed& b);
static const Fixed& min(const Fixed& a, const Fixed& b);
static Fixed& max(Fixed& a, Fixed& b);
static const Fixed& max(const Fixed& a, const Fixed& b);
Fixed.cpp (increment)
// Pre-increment: modify and return reference
Fixed& Fixed::operator++() {
_rawValue++;
return *this;
}
// Post-increment: save copy, modify, return old value
Fixed Fixed::operator++(int) {
Fixed temp(*this);
_rawValue++;
return temp;
}

Exercise 03: BSP (Binary Space Partitioning)

Section titled “Exercise 03: BSP (Binary Space Partitioning)”

Point-in-triangle test using cross products (area method).

Point.hpp
#ifndef POINT_HPP
#define POINT_HPP
#include "Fixed.hpp"
class Point {
private:
Fixed const _x;
Fixed const _y;
public:
Point(); // Default: (0, 0)
Point(const float x, const float y); // From floats
Point(const Point& other); // Copy constructor
Point& operator=(const Point& other); // Assignment (does nothing - const members)
~Point();
Fixed getX() const;
Fixed getY() const;
};
bool bsp(Point const a, Point const b, Point const c, Point const point);
#endif
Point.cpp
#include "Point.hpp"
Point::Point() : _x(0), _y(0) {}
Point::Point(const float x, const float y) : _x(x), _y(y) {}
Point::Point(const Point& other) : _x(other._x), _y(other._y) {}
// Assignment operator: const members cannot be reassigned
// This is a limitation - assignment does nothing useful
Point& Point::operator=(const Point& other) {
(void)other;
return *this;
}
Point::~Point() {}
Fixed Point::getX() const { return _x; }
Fixed Point::getY() const { return _y; }
bsp.cpp
#include "Point.hpp"
// Calculate signed area of triangle using cross product
// Positive = counterclockwise, Negative = clockwise, Zero = collinear
static Fixed crossProduct(Point const& p1, Point const& p2, Point const& p3) {
// (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x)
return (p2.getX() - p1.getX()) * (p3.getY() - p1.getY())
- (p2.getY() - p1.getY()) * (p3.getX() - p1.getX());
}
bool bsp(Point const a, Point const b, Point const c, Point const point) {
// Calculate cross products for each edge
Fixed d1 = crossProduct(a, b, point);
Fixed d2 = crossProduct(b, c, point);
Fixed d3 = crossProduct(c, a, point);
// Check if point is on any edge (cross product = 0)
// Subject says: "if the point is a vertex or on an edge, return False"
if (d1 == Fixed(0) || d2 == Fixed(0) || d3 == Fixed(0))
return false;
// Check if all cross products have the same sign
// (all positive OR all negative means inside)
bool allNegative = (d1 < Fixed(0)) && (d2 < Fixed(0)) && (d3 < Fixed(0));
bool allPositive = (d1 > Fixed(0)) && (d2 > Fixed(0)) && (d3 > Fixed(0));
return allNegative || allPositive;
}
main.cpp
#include <iostream>
#include "Point.hpp"
int main() {
// Triangle vertices
Point a(0.0f, 0.0f);
Point b(10.0f, 0.0f);
Point c(5.0f, 10.0f);
// Test points
Point inside(5.0f, 3.0f); // Inside
Point onEdge(5.0f, 0.0f); // On edge AB
Point onVertex(0.0f, 0.0f); // On vertex A
Point outside(15.0f, 5.0f); // Outside
std::cout << "Inside: " << (bsp(a, b, c, inside) ? "true" : "false") << std::endl; // true
std::cout << "On edge: " << (bsp(a, b, c, onEdge) ? "true" : "false") << std::endl; // false
std::cout << "On vertex: " << (bsp(a, b, c, onVertex) ? "true" : "false") << std::endl; // false
std::cout << "Outside: " << (bsp(a, b, c, outside) ? "true" : "false") << std::endl; // false
return 0;
}

Key Points:

  • Point class has const attributes - x and y cannot change after construction
  • Assignment operator is effectively useless (const members can’t be reassigned)
  • Cross product sign test: If point is on same side of all three edges, it’s inside
  • Edge/vertex detection: Cross product = 0 means collinear (on edge or vertex) → return false
  • The algorithm works regardless of triangle winding order (clockwise or counterclockwise)
  • Fixed-point arithmetic provides exact comparisons (no floating-point precision issues)