Merge branch 'main' of github.com:tbarnes98/CS3210 into main

This commit is contained in:
2022-04-05 10:38:23 -05:00
9 changed files with 882 additions and 0 deletions

21
Lab5/drawbase.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef DRAWBASE_H
#define DRAWBASE_H
// forward reference
class GraphicsContext;
class DrawingBase
{
public:
// prevent warnings
virtual ~DrawingBase(){}
virtual void paint(GraphicsContext* gc){}
virtual void keyDown(GraphicsContext* gc, unsigned int keycode){}
virtual void keyUp(GraphicsContext* gc, unsigned int keycode){}
virtual void mouseButtonDown(GraphicsContext* gc,
unsigned int button, int x, int y){}
virtual void mouseButtonUp(GraphicsContext* gc,
unsigned int button, int x, int y){}
virtual void mouseMove(GraphicsContext* gc, int x, int y){}
};
#endif

26
Lab5/gcontext.cpp Normal file
View File

@@ -0,0 +1,26 @@
/* This is an abstract base class representing a generic graphics
* context. Most implementation specifics will need to be provided by
* a concrete implementation. See header file for specifics. */
#define _USE_MATH_DEFINES // for M_PI
#include <cmath> // for trig functions
#include "gcontext.h"
/*
* Destructor - does nothing
*/
GraphicsContext::~GraphicsContext()
{
// nothing to do
// here to insure subclasses handle destruction properly
}
//does nothing
void GraphicsContext::drawLine(int x0, int y0, int x1, int y1){}
void GraphicsContext::drawCircle(int x0, int y0, unsigned int radius){}
void GraphicsContext::endLoop()
{
run = false;
}

141
Lab5/gcontext.h Normal file
View File

@@ -0,0 +1,141 @@
#ifndef GCONTEXT_H
#define GCONTEXT_H
/**
* This class is intended to be the abstract base class
* for a graphical context for various platforms. Any
* concrete subclass will need to implement the pure virtual
* methods to support setting pixels, getting pixel color,
* setting the drawing mode, and running an event loop to
* capture mouse and keyboard events directed to the graphics
* context (or window). Specific expectations for the various
* methods are documented below.
*
* */
// forward reference - needed because runLoop needs a target for events
class DrawingBase;
class GraphicsContext
{
public:
/*********************************************************
* Some constants and enums
*********************************************************/
// This enumerated type is an argument to setMode and allows
// us to support two different drawing modes. MODE_NORMAL is
// also call copy-mode and the affect pixel(s) are set to the
// color requested. XOR mode will XOR the new color with the
// existing color so that the change is reversible.
enum drawMode {MODE_NORMAL, MODE_XOR};
// Some colors - for fun
static const unsigned int BLACK = 0x000000;
static const unsigned int BLUE = 0x0000FF;
static const unsigned int GREEN = 0x00FF00;
static const unsigned int RED = 0xFF0000;
static const unsigned int CYAN = 0x00FFFF;
static const unsigned int MAGENTA = 0xFF00FF;
static const unsigned int YELLOW = 0xFFFF00;
static const unsigned int GRAY = 0x808080;
static const unsigned int WHITE = 0xFFFFFF;
/*********************************************************
* Construction / Destruction
*********************************************************/
// Implementations of this class should include a constructor
// that creates the drawing canvas (window), sets a background
// color (which may be configurable), sets a default drawing
// color (which may be configurable), and start with normal
// (copy) drawing mode.
// need a virtual destructor to ensure subclasses will have
// their destructors called properly. Must be virtual.
virtual ~GraphicsContext();
/*********************************************************
* Drawing operations
*********************************************************/
// Allows the drawing mode to be changed between normal (copy)
// and xor. The implementing context should default to normal.
virtual void setMode(drawMode newMode) = 0;
// Set the current color. Implementations should default to white.
// color is 24-bit RGB value
virtual void setColor(unsigned int color) = 0;
// Set pixel to the current color
virtual void setPixel(int x, int y) = 0;
// Get 24-bit RGB pixel color at specified location
// unsigned int will likely be 32-bit on 32-bit systems, and
// possible 64-bit on some 64-bit systems. In either case,
// it is large enough to hold a 16-bit color.
virtual unsigned int getPixel(int x, int y) = 0;
// This should reset entire context to the current background
virtual void clear()=0;
// These are the naive implementations that use setPixel,
// but are overridable should a context have a better-
// performing version available.
/* will need to be provided by the concrete
* implementation.
*
* Parameters:
* x0, y0 - origin of line
* x1, y1 - end of line
*
* Returns: void
*/
virtual void drawLine(int x0, int y0, int x1, int y1);
/* will need to be provided by the concrete
* implementation.
*
* Parameters:
* x0, y0 - origin/center of circle
* radius - radius of circle
*
* Returns: void
*/
virtual void drawCircle(int x0, int y0, unsigned int radius);
/*********************************************************
* Event loop operations
*********************************************************/
// Run Event loop. This routine will receive events from
// the implementation and pass them along to the drawing. It
// will return when the window is closed or other implementation-
// specific sequence.
virtual void runLoop(DrawingBase* drawing) = 0;
// This method will end the current loop if one is running
// a default version is supplied
virtual void endLoop();
/*********************************************************
* Utility operations
*********************************************************/
// returns the width of the window
virtual int getWindowWidth() = 0;
// returns the height of the window
virtual int getWindowHeight() = 0;
protected:
// this flag is used to control whether the event loop
// continues to run.
bool run;
};
#endif

27
Lab5/main.cpp Normal file
View File

@@ -0,0 +1,27 @@
/**
* @file main.cpp
* @author Trevor Barnes (barnestr@msoe.edu)
* @brief
* @version 1.0
* @date 2022-04-12
*
* @copyright Copyright (c) 2022
*
*/
#include "x11context.h"
#include <unistd.h>
#include <iostream>
using namespace std;
int main(void)
{
GraphicsContext* gc = new X11Context(800,600,GraphicsContext::BLACK);
sleep(10);
delete gc;
return 0;
}

25
Lab5/makefile Normal file
View File

@@ -0,0 +1,25 @@
# lab5 Makefile
CC = g++
CFLAGS = -c -MMD -g
LFLAGS = -lX11
# Change w/ every new project
SOURCES = main.cpp gcontext.cpp x11context.cpp
OBJECTS = $(SOURCES:.cpp=.o)
# Change w/ every new project
EXECUTABLE = Lab4
all: $(EXECUTABLE) $(SOURCES)
$(EXECUTABLE): $(OBJECTS)
$(CC) -o $@ $(OBJECTS) $(LFLAGS)
-include *.d
%.o:%.cpp
$(CC) $(CFLAGS) $<
clean:
rm -f $(EXECUTABLE)
rm -f $(OBJECTS)
rm -f *.d

205
Lab5/matrix.cpp Normal file
View File

@@ -0,0 +1,205 @@
/**
* @file matrix.cpp
* @author Trevor Barnes (barnestr@msoe.edu)
* @brief Contains the main functionality for matrices of double values
* @version 1.0
* @date 2022-03-29
*
* @copyright Copyright (c) 2022
*
*/
#include <stdexcept>
#include <iomanip>
#include "matrix.h"
using namespace std;
// constructor
Matrix::Matrix(unsigned int rows, unsigned int cols){
// Ensure size is valid
if(rows < 1 || cols < 1){
throw(out_of_range("rows and cols must be greater than 0"));
}
// Assign given row and col size to matrix
this->rows = rows;
this->cols = cols;
// Create the row pointer
this->the_matrix = new Row*[rows];
for(int i = 0; i < rows; i++){
// Assign the rows of the matrix to new memory
this->the_matrix[i] = new Row(cols);
}
}
// Copy constructor
Matrix::Matrix(const Matrix& from){
// New matrix gets row amount from "from" matrix
this->rows = from.rows;
// New matrix gets col amount from "from" matrix
this->cols = from.cols;
// Create the row pointer with new row amount
this->the_matrix = new Row*[rows];
for(int i = 0; i < rows; i++){
// Set the pointer to each copied row
this->the_matrix[i] = new Row(from[i]);
}
}
// Destructor
Matrix::~Matrix(){
// Iterates through the pointer array and deletes each row's array
for(int i = 0; i < rows; i++) {
delete this->the_matrix[i];
}
// Deletes the matrix itself
delete[] the_matrix;
}
// Assignment operator
Matrix& Matrix::operator=(const Matrix& rhs){
if(&rhs != this){
if(rows > 0 && cols > 0){
// Iterates through the pointer array and deletes each row's array
for(int i = 0; i < rows; i++) {
delete this->the_matrix[i];
}
// Deletes the matrix itself
delete[] the_matrix;
}
this->rows = rhs.rows;
this->cols = rhs.cols;
this->the_matrix = new Row*[rows];
for(int i = 0; i < rows; i++){
// Set the pointer to each copied row
the_matrix[i] = new Row(rhs[i]);
}
}
return *this;
}
// Named Constructor
Matrix Matrix::identity(unsigned int size){
// Ensure size is valid
if(size < 1){
throw(out_of_range("rows and cols must be greater than 0"));
}
// Create square matrix
Matrix result(size, size);
for(int i = 0; i < size; i++){
// Fill each diagonal value with 1
result[i][i] = 1;
}
return result;
}
// Matrix addition
Matrix Matrix::operator+(const Matrix& rhs) const{
if(rows != rhs.rows || cols != rhs.cols){
throw(out_of_range("Matrices must be the same size"));
}
Matrix result(this->rows, this->cols);
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
result[i][j] = (*the_matrix[i])[j] + rhs[i][j];
}
}
return result;
}
// Matrix multiplication
Matrix Matrix::operator*(const Matrix& rhs) const{
if(cols != rhs.rows){
throw(out_of_range("1st matrix rows must equal 2nd matrix cols"));
}
Matrix result(this->rows, rhs.cols);
for(int i = 0; i < this->rows; i++){
for(int j = 0; j < rhs.cols; j++){
result[i][j] = 0.0;
for(int k = 0; k < rhs.rows; k++){
result[i][j] += (*the_matrix[i])[k]*rhs[k][j];
}
}
}
return result;
}
// Scalar multiplication
Matrix Matrix::operator*(const double scale) const{
Matrix result(this->rows, this->cols);
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
result[i][j] = (*the_matrix[i])[j]*scale;
}
}
return result;
}
// Transpose of a Matrix
Matrix Matrix::operator~() const{
Matrix result(this->cols, this->rows);
for(int i = 0; i < rows; i++){
for(int j = 0; j < cols; j++){
result[j][i] = (*the_matrix[i])[j];
}
}
return result;
}
// Clear Matrix
void Matrix::clear(){
for(int i = 0; i < rows; i++) {
the_matrix[i]->clear();
}
}
// Access Operators - non-const
Row& Matrix::operator[](unsigned int row){
// Ensure the row is in range
if(row > rows){
throw(out_of_range("Row is out of range"));
}
return *(the_matrix[row]);
}
// Access Operators - const
const Row& Matrix::operator[](unsigned int row) const{
// Ensure the row is in range
if(row > rows){
throw(out_of_range("Row is out of range"));
}
return *(the_matrix[row]);
}
// print to output stream
void Matrix::out(std::ostream& os) const{
os << setprecision(4);
os << "[";
for(int i = 0; i < rows; i++) {
if(i != 0){
cout << " ";
}
os << "[ ";
for(int j = 0; j < cols; j++) {
os << (*the_matrix[i])[j];
if(j != cols-1){
os << ", ";
}
}
os << " ]";
if(i != rows-1){
os << endl;
}
}
os << "]" << endl;
}
// global insertion operator
std::ostream& operator<<(std::ostream& os, const Matrix& rhs){
rhs.out(os);
return os;
}
// global scalar multiplication
Matrix operator*(const double scale, const Matrix& rhs){
return rhs*scale;
}

103
Lab5/matrix.h Normal file
View File

@@ -0,0 +1,103 @@
#ifndef matrix_h
#define matrix_h
#include <iostream>
#include "row.h"
class Matrix
{
public:
// No default (no argument) constructor. It doesn't really make
// sense to have one as we cannot rely on a size. This may trip
// us up later, but it will lead to a better implementation.
// matrix();
// Constructor - create Matrix and clear cells. If rows or
// cols is < 1, throw an exception
Matrix(unsigned int rows, unsigned int cols);
// Copy constructor - make a new Matrix just like rhs
Matrix(const Matrix& from);
// Destructor. Free allocated memory
~Matrix();
// Assignment operator - make this just like rhs. Must function
// correctly even if rhs is a different size than this.
Matrix& operator=(const Matrix& rhs);
// Named Constructor - produce a square identity matrix of the
// requested size. Since we do not know how the object produced will
// be used, we pretty much have to return by value. A size of 0
// would not make sense and should throw an exception.
static Matrix identity(unsigned int size);
// Matrix addition - lhs and rhs must be same size otherwise
// an exception shall be thrown
Matrix operator+(const Matrix& rhs) const;
// Matrix multiplication - lhs and rhs must be compatible
// otherwise an exception shall be thrown
Matrix operator*(const Matrix& rhs) const;
// Scalar multiplication. Note, this function will support
// someMatrixObject * 5.0, but not 5.0 * someMatrixObject.
Matrix operator*(const double scale) const;
// Transpose of a Matrix - should always work, hence no exception
Matrix operator~() const;
// Clear Matrix to all members 0.0
void clear();
// Access Operators - throw an exception if index out of range
Row& operator[](unsigned int row);
// const version of above - throws an exception if indices are out of
// range
const Row& operator[](unsigned int row) const;
// I/O - for convenience - this is intended to be called by the global
// << operator declared below.
void out(std::ostream& os) const;
private:
// An array of Row pointers size "rows" that each point to a double array
// of size "cols"
Row** the_matrix;
unsigned int rows;
unsigned int cols;
/** routines **/
// add any "helper" routine here, such as routines to support
// matrix inversion
};
/** Some Related Global Functions **/
// Overloaded global << with std::ostream as lhs, Matrix as rhs. This method
// should generate output compatible with an ostream which is commonly used
// with console (cout) and files. Something like:
// [[ r0c0, r0c1, r0c2 ]
// [ r1c0, r1c1, r1c2 ]
// [ r0c0, r0c1, r0c2 ]]
// would be appropriate.
//
// Since this is a global function, it does not have access to the private
// data of a Matrix object. So, it will need to use the public interface of
// Matrix to do its job. The method Matrix::out was added to Matrix
// specifically for this purpose. The other option would have been to make
// it a "friend"
std::ostream& operator<<(std::ostream& os, const Matrix& rhs);
// We would normally have a corresponding >> operator, but
// will defer that exercise that until a later assignment.
// Scalar multiplication with a global function. Note, this function will
// support 5.0 * someMatrixObject, but not someMatrixObject * 5.0
Matrix operator*(const double scale, const Matrix& rhs);
#endif
// Based on lab by Dr. Darrin Rothe ((c) 2015 Dr. Darrin Rothe)

286
Lab5/x11context.cpp Normal file
View File

@@ -0,0 +1,286 @@
/* Provides a simple drawing context for X11/XWindows
* You must have the X11 dev libraries installed. If missing,
* 'sudo apt-get install libx11-dev' should help.
*/
#include <X11/Xlib.h> // Every Xlib program must include this
#include <X11/Xutil.h> // needed for XGetPixel
#include <X11/XKBlib.h> // needed for keyboard setup
#include "x11context.h"
#include "drawbase.h"
#include <iostream>
#include <unistd.h>
using namespace std;
/**
* The only constructor provided. Allows size of window and background
* color be specified.
* */
X11Context::X11Context(unsigned int sizex=400,unsigned int sizey=400,
unsigned int bg_color=GraphicsContext::BLACK)
{
// Open the display
display = XOpenDisplay(NULL);
// Holding a key in gives repeated key_press commands but only
// one key_release
int supported;
XkbSetDetectableAutoRepeat(display,true,&supported);
// Create a window - we will assume the color map is in RGB mode.
window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0,
sizex, sizey, 0, 0 , bg_color);
// Sign up for MapNotify events
XSelectInput(display, window, StructureNotifyMask);
// Put the window on the screen
XMapWindow(display, window);
// Create a "Graphics Context"
graphics_context = XCreateGC(display, window, 0, NULL);
// Default color to white
XSetForeground(display, graphics_context, GraphicsContext::WHITE);
// Wait for MapNotify event
for(;;)
{
XEvent e;
XNextEvent(display, &e);
if (e.type == MapNotify)
break;
}
// We also want exposure, mouse, and keyboard events
XSelectInput(display, window, ExposureMask|
ButtonPressMask|
ButtonReleaseMask|
KeyPressMask|
KeyReleaseMask|
PointerMotionMask);
// We need this to get the WM_DELETE_WINDOW message from the
// window manager in case user click the X icon
Atom atomKill = XInternAtom(display, "WM_DELETE_WINDOW", False);
XSetWMProtocols(display, window, &atomKill, 1);
return;
}
// Destructor - shut down window and connection to server
X11Context::~X11Context()
{
XFreeGC(display, graphics_context);
XDestroyWindow(display,window);
XCloseDisplay(display);
}
// Set the drawing mode - argument is enumerated
void X11Context::setMode(drawMode newMode)
{
if (newMode == GraphicsContext::MODE_NORMAL)
{
XSetFunction(display,graphics_context,GXcopy);
}
else
{
XSetFunction(display,graphics_context,GXxor);
}
}
// Set drawing color - assume colormap is 24 bit RGB
void X11Context::setColor(unsigned int color)
{
// Go ahead and set color here - better performance than setting
// on every setPixel
XSetForeground(display, graphics_context, color);
}
// Set a pixel in the current color
void X11Context::setPixel(int x, int y)
{
XDrawPoint(display, window, graphics_context, x, y);
XFlush(display);
}
unsigned int X11Context::getPixel(int x, int y)
{
XImage *image;
image = XGetImage (display, window, x, y, 1, 1, AllPlanes, XYPixmap);
XColor color;
color.pixel = XGetPixel (image, 0, 0);
XFree (image);
XQueryColor (display, DefaultColormap(display, DefaultScreen (display)),
&color);
// I now have RGB values, but, they are 16 bits each, I only want 8-bits
// each since I want a 24-bit RGB color value
unsigned int pixcolor = color.red & 0xff00;
pixcolor |= (color.green >> 8);
pixcolor <<= 8;
pixcolor |= (color.blue >> 8);
return pixcolor;
}
void X11Context::clear()
{
XClearWindow(display, window);
XFlush(display);
}
// Run event loop
void X11Context::runLoop(DrawingBase* drawing)
{
run = true;
while(run)
{
XEvent e;
XNextEvent(display, &e);
// Exposure event - lets not worry about region
if (e.type == Expose)
drawing->paint(this);
// Key Down
else if (e.type == KeyPress)
drawing->keyDown(this,XLookupKeysym((XKeyEvent*)&e,
(((e.xkey.state&0x01)&&!(e.xkey.state&0x02))||
(!(e.xkey.state&0x01)&&(e.xkey.state&0x02)))?1:0));
// Key Up
else if (e.type == KeyRelease){
drawing->keyUp(this,XLookupKeysym((XKeyEvent*)&e,
(((e.xkey.state&0x01)&&!(e.xkey.state&0x02))||
(!(e.xkey.state&0x01)&&(e.xkey.state&0x02)))?1:0));
}
// Mouse Button Down
else if (e.type == ButtonPress)
drawing->mouseButtonDown(this,
e.xbutton.button,
e.xbutton.x,
e.xbutton.y);
// Mouse Button Up
else if (e.type == ButtonRelease)
drawing->mouseButtonUp(this,
e.xbutton.button,
e.xbutton.x,
e.xbutton.y);
// Mouse Move
else if (e.type == MotionNotify)
drawing->mouseMove(this,
e.xmotion.x,
e.xmotion.y);
// This will respond to the WM_DELETE_WINDOW from the
// window manager.
else if (e.type == ClientMessage)
break;
}
}
int X11Context::getWindowWidth()
{
XWindowAttributes window_attributes;
XGetWindowAttributes(display,window, &window_attributes);
return window_attributes.width;
}
int X11Context::getWindowHeight()
{
XWindowAttributes window_attributes;
XGetWindowAttributes(display,window, &window_attributes);
return window_attributes.height;
}
// void X11Context::drawLine(int x1, int y1, int x2, int y2)
// {
// XDrawLine(display, window, graphics_context, x1, y1, x2, y2);
// XFlush(display);
// }
// Bresenham Implementation
void X11Context::drawLine(int x1, int y1, int x2, int y2){
bool steep;
if(abs(y2-y1) < abs(x2-x1)){
// Steep slope
steep = true;
if(x1 > x2){
// Swap to change drawing direction
int temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
} else {
// Shallow slope
steep = false;
if(y1 > y2){
// Swap to change drawing direction
int temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
}
// Initilize algorithm values
int dx = x2-x1;
int dy = y2-y1;
int D = 2*dy-dx;
// Will either be +1 or -1
int c = 1;
if(steep){
// When |dx| > |dy|
if(dy<0){
c = -1;
dy = -dy;
}
int D = 2*dy-dx;
int y = y1;
for(int i = x1; i < x2; i++){
setPixel(i,y);
if(D > 0){
y += c;
D += (2*(dy-dx));
}else{
D += 2*dy;
}
}
} else if(!steep){
// When |dx| < |dy|
if(dx<0){
c = -1;
dx = -dx;
}
int D = 2*dx-dy;
int x = x1;
for(int j = y1; j < y2; j++){
setPixel(x,j);
if(D > 0){
x += c;
D += (2*(dx-dy));
} else {
D += 2*dx;
}
}
}
}
void X11Context::drawCircle(int x, int y, unsigned int radius)
{
XDrawArc(display, window, graphics_context, x-radius,
y-radius, radius*2, radius*2, 0, 360*64);
XFlush(display);
}

48
Lab5/x11context.h Normal file
View File

@@ -0,0 +1,48 @@
#ifndef X11_CONTEXT
#define X11_CONTEXT
/**
* This class is a sample implementation of the GraphicsContext class
* for the X11 / XWindows system.
* */
#include <X11/Xlib.h> // Every Xlib program must include this
#include "gcontext.h" // base class
class X11Context : public GraphicsContext
{
public:
// Default Constructor
X11Context(unsigned int sizex,unsigned int sizey,unsigned int bg_color);
// Destructor
virtual ~X11Context();
// Drawing Operations
void setMode(drawMode newMode);
void setColor(unsigned int color);
void setPixel(int x, int y);
unsigned int getPixel(int x, int y);
void clear();
void drawLine(int x1, int y1, int x2, int y2);
void drawCircle(int x, int y, unsigned int radius);
// Event looop functions
void runLoop(DrawingBase* drawing);
// we will use endLoop provided by base class
// Utility functions
int getWindowWidth();
int getWindowHeight();
private:
// X11 stuff - specific to this context
Display* display;
Window window;
GC graphics_context;
};
#endif