0% found this document useful (0 votes)
8 views10 pages

FinalProject Checkpoint6

Uploaded by

Bo Stevens
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views10 pages

FinalProject Checkpoint6

Uploaded by

Bo Stevens
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

CS-2810 Final Project

- Checkpoint 6

Checkpoint Description
Alright! Time for our FINAL checkpoint! With the vast majority of our planned features now implemented, we’re going to sweep through our
program and bring in a bunch of extra functionality.

Consider this your victory lap

Our super cool editor, with new Buttons and functionality

Objectives
1. Objective 1
2. Objective 2
3. Objective 3

Objective 1

Objective checklist:
• Make our program save images to somewhere besides the desktop
• Lock the size of our window

1.0 Saving to Locations Besides Desktop


Right now, our program can save and load files in and out of the application, but it can only save files to the desktop. This is because we’ve hard-
coded our saving destination
public boolean keyDown(int keycode) {
if(_controlPressed && keycode == Keys.S) {
try {ImageInputOutput.Instance.saveImage("YOURDESKTOPLOCATION\\test.bmp");}
catch (IOException e) {e.printStackTrace();}
}

Coming up with a REALLY good solution here might take a little bit of time and effort, so let’s just do things the easy way instead and make our
program save files out to the same folder we opened them up from.
Add the following method to ImageInputOutput.java
private String scrapeFolderLocation(String filePath) {
return null;
}

This method will take the full file path of any image we load up
C:Users\\SomeUser\\Desktop\\SomeFile.bmp

And chop off the file name at the end, just giving us the folder location
C:Users\\SomeUser\\Desktop\\SomeFile.bmp
Doing this is a simple matter of simple String traversal
private String scrapeFolderLocation(String filePath) {
StringBuilder builder = new StringBuilder(filePath);
for(int i = filePath.length() - 1; i >= 0; i--) {
if(filePath.charAt(i) != '\\') continue;
return builder.substring(0,i);
}
return null;
}
Method loops until it hits that first \\, then grabs everything that came before it

Now perform the following –


• Add a public String called ImageFolderLocation to ImageInputOutput.java
• Inside of loadImage, call scrapeFolderLocation(), and save it’s result into ImageFolderLocation
• Go to InputManager.java
• Inside keyDown, add a check to see if ImageFolderLocation is null
o If so, return before trying to save
o Change your call to saveImage() to now use accept
ImageInputOutput.Instance.ImageFolderLocation + \\output.bmp

Saving and loading to the Documents folder for example, should now be possible

1.2 Locking the Window Size


Right now, our program is a little dangerous, because anyone could resize the window, and potentially mess up our program by making things
stretch, or causing our collision detection to go wonky. Let’s fix this by locking the size of the window.

Hop into DesktopLauncher.java, and we can do this by doing the following


config.setWindowedMode(584, 480);
config.setResizable(false);

And done!

1.3 Fixing onClickUp


Unfortunately, I can’t show this very well on this document, but your program may suffer the following problem when clicking on our little button in
the bottom-left.

Clicking down on a button, moving the mouse off the button, then letting go of the click

If performing the steps above produces the result you see above, then this is caused because we destroyed a small piece of functionality of our
program when we switched towards our opt-in collision detection system. You can fix this by making the following change inside of mouseMoved in
InputManager.java
public boolean mouseMoved(int screenX, int screenY) {
IHoverable collision = CollisionManager.Instance.getHovered(
new Vector2(screenX, ImageEditor.Instance.ScreenSize.y - screenY)
);
if(_currentlyHovered != null && _currentlyHovered != collision) _currentlyHovered.onHoverExit();
if(collision != null) collision.onHovered();
if(collision != _currentlyHovered) _currentlyClicked = null;
This sets _currentlyClicked to null whenever we unhover something, taking care of this edge -case

1.4 Cleaning up create()

Right now, our entry point is a bit of a mess. We’ll be adding to it in the next Objective as well, so let’s take a little bit of time and clean it up.
Your code might look just a little different, but this is what my create() method looks like right now
public void create () {
Instance = this;
new ImageInputOutput();
InputManager inputManager = new InputManager();
Gdx.input.setInputProcessor(inputManager);
batch = new SpriteBatch();
ScreenSize = new Vector2(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Vector2 editWindowSize = new Vector2(500, ScreenSize.y - 50);
_editWindow = new EditWindow(editWindowSize, new Vector2(ScreenSize.x - editWindowSize.x, 0));
Button button = new Button(new Vector2(50, 50), Vector2.Zero, Color.GOLD);
CollisionManager collisionManager = new CollisionManager();
}
No spacing, organization, or labelling

I won’t go into excess detail here, but this is how I cleaned up my code
public void create () {
Instance = this;
initializeUtilityClasses();
createGraphicalElements();
}
private void initializeUtilityClasses() {
new CollisionManager();
new ImageInputOutput();
InputManager inputManager = new InputManager();
Gdx.input.setInputProcessor(inputManager);
}
private void createGraphicalElements() {
ScreenSize = new Vector2(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Vector2 editWindowSize = new Vector2(500, ScreenSize.y - 50);
Button button = new Button(new Vector2(50, 50), Vector2.Zero, Color.GOLD);
batch = new SpriteBatch();
_editWindow = new EditWindow(editWindowSize, new Vector2(ScreenSize.x - editWindowSize.x, 0));
}

Objective 2

Checklist:
• Let’s actually DO SOMETHING with our Button class
o Create ColorButton.java
o Create a bunch of color buttons along the left-side of the screen
o Add Outlines to Buttons
• Let’s also create some Menu Buttons
o Create Save Button
o Create Exit Button
o Create Black & White Button
2.0 Color Buttons
Right now, there are two parts of our program that I find a little disappointing still.
ONE: We have this awesome Button class that we built forever ago, and it’s not really used for anything
TWO: We can only ever doodle in a single color

Let’s fix both of these in one swoop!


We’ll start by creating a new class, ColorButton.java that will inherit from Button
public class ColorButton extends Button {
public ColorButton(Vector2 scale, Vector2 position, Color recColor) {
super(scale, position, recColor);
}
}

Now, a Color Button will act VERY similarly to a Button, except, it’ll have some extra functionality when you click on it. It’ll set the color that we’re
doodling with to the color of the Button itself.
We’ll start by just overriding onClickUp() inside ColorButton
public void onClickUp(Vector2 clickPosition) {
super.onClickUp(clickPosition);
System.out.println("I've been clicked");
}
Doing this will allow our Color Button to be clicked like normal, but with extra features added on top!
We can test this out, by switching our Button in ImageEditor out for a ColorButton instead.
Button originalButton = new Button(new Vector2(50, 50), Vector2.Zero, Color.GOLD);
ColorButton button = new ColorButton(new Vector2(50, 50), Vector2.Zero, Color.GOLD);

Neat!

We can now add a new method to our EditWindow called setDrawColor(), and call it from ColorButton.onClickDown()
public void setDrawColor(Color newColor) {
DoodleMap.setColor(newColor);
}
Setting the color of our DoodleMap to a new color

We’ll be accessing it in just a moment, so hop into Button and set _startColor to protected
public class Button extends Rec2D implements IClickable, IHoverable {
protected Color _startColor;
private Color _hoveredColor;

And now, just call the setDrawColor method from inside onClickDown
public void onClickDown(Vector2 clickPosition) {
super.onClickDown(clickPosition);
EditWindow.Instance.setDrawColor(_startColor);
System.out.println("I've been clicked");
}
Using that new method to set the color of our DoodleMap to the color of the Color Button

And now, we can change colors super easily!

We can even add more Color Buttons for MORE colors!


new ColorButton(new Vector2(42, 42), Vector2.Zero, Color.GOLD);
new ColorButton(new Vector2(42, 42), new Vector2(42, 0), Color.ORANGE);
Creating an orange Button so that I can switch back and forth between

Perform the following –


• Fill up the left-side of the screen at least half way with Color Buttons for the user to pick between

Yeah I’m uhh….not an artist by trade


2.1 Outlines
Right now, our Buttons are distinct because they’re all different colors, but if you stare at them for a while, they might begin to look a little strange.
This might be because our Buttons have no borders separating them from each other. Let’s fix this.

Start by creating a class called Outline.java


public Texture OutlineTex;
public Outline(Vector2 recSize, Color color, int thickness) {
Pixmap map = new Pixmap((int) recSize.x, (int) recSize.y, Format.RGBA8888);
map.setColor(color);
OutlineTex = new Texture(map);
}
Each Outline will have a specified size, color, and thickness

We can now give every Rec2D a thin Outline.


public Outline Outline;
protected Color _recColor;
public Rec2D(Vector2 scale, Vector2 position, Color recColor) {
Scale = scale;
Position = position;
_recColor = recColor;
generateTexture();
Outline = new Outline(scale, Color.BLACK, 1);
ImageEditor.Instance.Rectangles.add(this);
}

In Outline, we can fill the top row of pixels with our selected Color by doing the following-
//Top
for(int x = 0; x < map.getWidth(); x++) {
for(int y = 0; y < thickness; y++) {
map.drawPixel(x, y);
}
}
Draw thickness many horizontal lines at the top of our Pixmap

We can verify that this is working by adding an extra loop to draw all the Outlines in our Rectangles inside our render() method
batch.draw(_editWindow.DoodleTexture, _editWindow.Position.x,
_editWindow.Position.y, _editWindow.Scale.x, _editWindow.Scale.y);
for(int i = 0; i < Rectangles.size; i++) {
rec = Rectangles.get(i);
batch.draw(rec.Outline.OutlineTex, rec.Position.x, rec.Position.y, rec.Scale.x, rec.Scale.y);
}

Running your code should now show black outlines along the top of all our Color Buttons

Each Button with a black line at the top

Now, perform the following –


• Add outlines along the rest of the sides
o HINT: All you have to do is change the structure of each of the loops
o HINT 2: You can make these as copy-paste looking as you want. Really, don’t think
too hard about this one. A rectangle ONLY has 4 sides. It’ll never have 3, and it’ll
never have 5. Simple, unscalable code is fine when our problem never scales
o Add an outline along the bottom
▪ This one should be the easiest
o Add outlines along the left and right

Once complete, you should see the following –

The outlines on the black and white buttons blend in a little, but that’s fine
2.2 Save Button
Right now, saving our program can be done by pressing CTRL + S. This is awesome for what I’ll call “power users” who know how to use program
shortcuts, but consider that not everyone might know this shortcut even exists, let alone feel comfortable using it regularly.

So let’s add some Buttons along the top of our program that let us expose existing functionality to our users.
Create a new class called SaveButton.java that extends Button and overrides onClickUp()
public class SaveButton extends Button {
public SaveButton(Vector2 scale, Vector2 position, Color recColor) {
super(scale, position, recColor);
}
public void onClickUp(Vector2 clickPosition) {
super.onClickUp(clickPosition );
System.out.println("Clicked on the save button");
}
}

We can now create a SaveButton in ImageEditor.createGraphicalElements()


new SaveButton(new Vector2(100,50), new Vector2(0, ScreenSize.y - 50), Color.GRAY);
Adding the Button to the top-left of the screen

And get the following result-

Our save button should be easily clickable now

We can even add the ability for our Buttons to have some Text to make it more obvious what their functions are supposed to be!
public class Button extends Rec2D implements IClickable, IHoverable {
public String ButtonText;

Then have SaveButton give ButtonText a value


public class SaveButton extends Button {
public SaveButton(Vector2 scale, Vector2 position, Color recColor) {
super(scale, position, recColor);
ButtonText = "Save";
}

We can then configure our program to render text out to the screen as desired
Hop into ImageEditor and add a BitmapFont to the class
private BitmapFont _font;
private void initializeUtilityClasses() {
new CollisionManager();
new ImageInputOutput();
InputManager inputManager = new InputManager();
Gdx.input.setInputProcessor(inputManager);
_font = new BitmapFont();
_font.getData().setScale(2f);
}
Creating the font and scaling up a little so our text isn’t super small

We can now hop over to render() and add some code to render out any text (AFTER rendering everything else)
for(int i = 0; i < Rectangles.size; i++) {
rec = Rectangles.get(i);
if(rec instanceof Button) {
button = (Button) rec;
if(button.ButtonText == null) continue;
_font.draw(batch, button.ButtonText, button.Position.x, button.Position.y + button.Scale.y * 0.75f,
button.Scale.x, Align.center, false);
}
instanceof checks to see if a rectangle is also a Button. We can then safely cast our rectangle INTO a Button so we can access ButtonText.

Running our code now, we should see some descriptive text above our Button!
Hurray!

Whoah whoah whoah, slow down! What even IS all this new stuff you’re throwing in?
Well…I promised this would be a victory lap, so I’ll spare you a lengthy explanation just this once. If you find it a satisfy ing answer, it’s all
just MAGIC!
If you want a real answer though, I’ll just refer you to this article on text rendering in LibGDX
https://libgdxinfo.wordpress.com/basic-label/

Our text is a bit blurry though. Unfortunately, this can be a bit of a pain to fix properly, so we’ll just pretend to fix it instead, by making the text (and
the Button it’s inside of) SMALLER!
Start by removing our call to setScale()
private void initializeUtilityClasses() {
_font = new BitmapFont();
_font.getData().setScale(2f);
}
So long!

And now changing the scale of things a little


ScreenSize = new Vector2(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
Vector2 editWindowSize = new Vector2(500, ScreenSize.y - 25);
new SaveButton(new Vector2(75,25), new Vector2(0, ScreenSize.y - 25), Color.GRAY);
new SaveButton(new Vector2(100,50), new Vector2(0, ScreenSize.y - 50), Color.GRAY);
Making the size of our Save Button smaller, and our Edit Window a little larger

Finally, the color of our Save Button, and our Edit Window (when it doesn’t have any image imported yet) are a little too similar. Let’s hop into
EditWindow, and make it use another color beside Gray.
public EditWindow(Vector2 scale, Vector2 position) {
super(scale, position, Color.SLATE);
You might find this color a little ugly. Feel free to experiment

And now we should have some text that is smaller, but less blurry!

A tradeoff, certainly, but again, easier than actually solving the problem

Lastly, let’s make this Button perform its intended function. This is perhaps the most surprising part, as, due to the structure of our program, this is
INCREDIBLY easy to do! Add the following to onClickUp inside SaveButton
public void onClickUp(Vector2 clickPosition) {
super.onClickUp(clickPosition);
if(ImageInputOutput.Instance.ImageFolderLocation == null) return;
try {ImageInputOutput.Instance.saveImage(ImageInputOutput.Instance.ImageFolderLocation + "\\output.bmp");}
catch (IOException e) {e.printStackTrace();}
}
No way it’s actually that easy, right?
Turns out it IS that easy!

2.3 Exit Button


While not strictly necessary, let’s make another Button that lets us exit our program, just so we can keep filling out this top row. Create another
class ExitButton.java that extends Button and overrides onClickUp
public class ExitButton extends Button{
public ExitButton(Vector2 scale, Vector2 position, Color recColor) {
super(scale, position, recColor);
ButtonText = "Exit";
}
public void onClickUp(Vector2 clickPosition) {
super.onClickUp(clickPosition);
Gdx.app.exit();
System.exit(-1);
}
}
Both exit() methods in conjunction, will close out our program

Now go ahead and create an ExitButton, and place it next to your Save Button
It should now render inside our program, and, when clicked on, close the application

Showing this off is a little difficult on my end, but I trust you can figure out how to test this one

2.4 Clear Doodle Button


Right now, if we make a mistake while doodling, we have to close our entire program and open it back up again. Let’s add some quality-of-life to our
application, and let users clear out their current doodle.

We’ll actually start by making some quick changes to EditWindow. We’ll add a proper variable to track the current drawing color, and get rid of our
setDrawColor method.
public class EditWindow extends Rec2D implements IClickable{
public Color DrawColor;
public void setDrawColor(Color newColor) {
DoodleMap.setColor(newColor);
}
This’ll pay off in just a moment

Didn’t you just have us create this method? What’s the deal?
Yes, I did! I did this because I wanted to show off the design process for building out new features. When writing code, we want t o initially
try to lock down every piece of data we can find. If a variable MUST exist, then it should default to private , and If a method MUST be run,
we should limit its use and functionality as much as possible. This stops future programmers from doing dumb things with our code. At
first, our code was fine with a locked -down setColor method, but we’ll soon be wanting to both SET and GET this data.

Quickly fix up the now broken code inside ColorButton


public void onClickDown(Vector2 clickPosition) {
super.onClickDown(clickPosition);
EditWindow.Instance.DrawColor = _startColor;
EditWindow.Instance.DoodleMap.setColor(_startColor);
}
There are some slightly cleaner ways of doing this, but I’m not particularly worried by this point

And add the following to our EditWindow’s constructor


public EditWindow(Vector2 scale, Vector2 position) {
super(scale, position, Color.SLATE);
DoodleMap = new Pixmap((int) scale.x,(int) scale.y, Format.RGBA8888);
DrawColor = Color.ORANGE;
DoodleMap.setColor(DrawColor);
DoodleMap.setColor(Color.ORANGE );
This just makes sure DrawColor ALWAYS has a value and is NEVER null

Now, as before, let’s add a new Button to our program, and call it ClearDoodleButton
public class ClearDoodleButton extends Button{
public ClearDoodleButton(Vector2 scale, Vector2 position, Color recColor) {
super(scale, position, recColor);
ButtonText = "Clear";
}
public void onClickUp(Vector2 clickPosition) {
super.onClickUp(clickPosition);
EditWindow edit = EditWindow.Instance;
edit.DoodleMap = new Pixmap((int) edit.Scale.x, (int) edit.Scale.y, Format.RGBA8888);
edit.DoodleMap.setColor(edit.DrawColor);
edit.DoodleTexture = new Texture(edit.DoodleMap);
}
}
Recreating our Pixmap and setting its Color to the Color the previous one had

Go ahead now and add your Clear Button to your program, and you should be able to test out its functionality!

Woops! Meant to draw a 6

Objective 3

Checklist:
• Clean up our program structurally
• Make Saving a multi-threaded activity
3.0 Creating Packages
By this point, if we take a look at our code files in our Package Explorer, you’ll notice that things have become a bit cluttered and difficult to navigate

How quickly can you find Outline.Java? Do your eyes ever skim past a file on accident while reading?

So far, organization hasn’t been a problem, but the larger our program becomes, the more difficult it will become to parse through any particular
piece of it. To solve this, we can break out our codebase into a number of packages

In Java, packages act like folders for our code. They let us organize into logical categories, and also let us have two or more class files with the SAME
name in a single project. We can create a new package in Eclipse like this
Right-click -> New -> Package

We’ve been creating a LOT of Buttons recently, so let’s create a new package called buttons

You can now drag-and-drop all of our Button classes into this package.
Selecting Update references will make any classes that have references to our Buttons insert an import statement at the top

Let’s also create a Utility package, and move all of our Managers and such into that package

Much neater!

3.1 Implementing Multithreading


I had a plan here, but, y’know what, I think you guys’ve done plenty. I don’t mind calling it here.
Make sure your program is still just as functional as before, and-

You completed the LAST checkpoint!!!

I mean this simply and honestly


GREAT work!

You can find a checklist in the final-submission assignment to verify that your program is working as intended

Remember to upload your changes to Github

You might also like