Module 02 Solutions
Exercise 00: Orthodox Canonical Form
Section titled “Exercise 00: Orthodox Canonical Form”The four required members for OCF.
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::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;}Exercise 01: Conversions
Section titled “Exercise 01: Conversions”Converting between int, float, and fixed-point.
// From int: shift left by fractional bitsFixed::Fixed(const int value) : _rawValue(value << _fractionalBits) {}
// From float: multiply by 2^8 and roundFixed::Fixed(const float value) : _rawValue(roundf(value * (1 << _fractionalBits))) {}
// To int: shift rightint Fixed::toInt() const { return _rawValue >> _fractionalBits;}
// To float: divide by 2^8float 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;}Exercise 02: Operator Overloading
Section titled “Exercise 02: Operator Overloading”Comparison, arithmetic, and increment operators.
// Comparisonbool 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;
// ArithmeticFixed 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: ++aFixed& operator++();// Post-increment: a++Fixed operator++(int);
// Static min/maxstatic 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);// Pre-increment: modify and return referenceFixed& Fixed::operator++() { _rawValue++; return *this;}
// Post-increment: save copy, modify, return old valueFixed 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
Section titled “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);
#endifPoint.cpp
Section titled “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 usefulPoint& 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
Section titled “bsp.cpp”#include "Point.hpp"
// Calculate signed area of triangle using cross product// Positive = counterclockwise, Negative = clockwise, Zero = collinearstatic 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 (test)
Section titled “main.cpp (test)”#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
constattributes - 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)