So, after many years of OOP, I got a pretty simple homework assignment from one of my university courses to implement a simple object-oriented structure.
The requested design:
Implement an objected oriented solution for creating the following shapes:
Ellipse, Circle, Square, Rectangle, Triangle, Parallelogram.
Each shape created must have the following parameters : unique id, color.
And following functions: color change, move, area, circumference, is inside, copy.
No validity tests are needed (not in the classes and not from user input).
My design:
Overall a pretty simple approach, shape_class / non-circular are abstract, and rectangle/square are combined into one class since they contain exactly the same parameters and no validity tests are needed (no reason to split them into two).
Shape class - implements a static id (unique id), and an init function dealing with the color name.
public abstract class shape_class {
static int STATIC_ID;
int id;
String color_name;
public shape_class(String color_name_input) {
this.id = STATIC_ID;
shape_class.STATIC_ID+=1;
if (Arrays.asList(toycad_globals.ALLOWED_COLORS).contains(color_name_input))
{
this.color_name = color_name_input;
}
}
public void change_color(String color_name_input) {
if (Arrays.asList(toycad_globals.ALLOWED_COLORS).contains(color_name_input)) {
this.color_name = color_name_input;
}
}
public abstract shape_class return_copy();
public abstract void move(double x, double y);
public abstract double area();
public abstract double circumference();
public abstract boolean is_inside(double x, double y);
}
** Non-circular** - Receives an array of points (Which define the object) and implements almost all required functions.
public abstract class non_circullar extends shape_class {
List<line> line_list = new ArrayList<line>();
List<point> point_list = new ArrayList<point>();
non_circullar(String color_name, point...input_point_list) {
super(color_name);
this.point_list = Arrays.asList(input_point_list);
for (int current_index =0; current_index< (input_point_list.length); current_index++) {
point current_first_point = input_point_list[current_index];
point current_second_point = input_point_list[(current_index+1)%input_point_list.length];
this.line_list.add(new line(current_first_point, current_second_point));
}
}
public point[] get_point_list_copy() {
int index = 0;
point [] new_array = new point[this.point_list.size()];
for (point current_point:this.point_list) {
new_array[index] = current_point.return_copy();
index+=1;
}
return new_array;
}
public double circumference() {
double sum = 0;
for (line current_line :this.line_list) {
sum += current_line.get_length();
}
return sum;
}
public void move(double x, double y) {
for (point current_point :this.point_list) {
current_point.move(x, y);
}
}
public boolean is_inside(double x, double y) {
int i;
int j;
boolean result = false;
for (i = 0, j = this.point_list.size() - 1; i < this.point_list.size(); j = i++) {
if ((this.point_list.get(i).y > y) != (this.point_list.get(j).y > y) &&
(x < (this.point_list.get(j).x - this.point_list.get(i).x) * (y - this.point_list.get(i).y) /
(this.point_list.get(j).y-this.point_list.get(i).y) + this.point_list.get(i).x))
{
result = !result;
}
}
return result;
}
int get_top_left_line_index() {
int top_left_line_index = 0;
int index = 0;
point best_point = this.line_list.get(0).get_average_point();
point current_point;
for (line current_line :this.line_list) {
current_point = current_line.get_average_point();
if (current_point.x < best_point.x) {
best_point = current_point;
top_left_line_index = index;
} else if (current_point.x == best_point.x && current_point.y > best_point.y) {
best_point = current_point;
top_left_line_index = index;
}
index +=1;
}
return top_left_line_index;
}
}
The problem:
For this assignment 40 points were reduced for design issues:
1) Circle is an ellipse and thus needs to inherit from it (Even though they share no parameters).
2) Rectangle / Square are two different entities even though in this implementation they are exactly the same (no validity tests).
I would be happy to get some inputs from the community regarding this design, are the design issues 'legit' or not, and what could have been done better?
Edit 1:
An ellipse is expressed by : two points and d (For a point to be on the ellipse the distance between it and the two points must be equal to d).
A circle is expressed by : center and radius.
I find it very hard to understand how they can share common params.
I suggest you follow this scheme:
You need to categorize shapes by the number of the edges first and then by the common characteristics. Then you have to recognize the following facts:
circle is just a special type of ellipse
square is just a special type of rectangle
both rectangle and parallelogram have 4 edges
unlikeparallelogram, rectangle have all the angles of 90°.
This is a simplified scheme according to your needs:
Ellipse, Circle, Square, Rectangle, Triangle, Parallelogram
Edit: Note that there exists the following hierarchy as well. Both rectangle and parallelogram have the opposite edges of the same length. Finally, it depends on the preferred interpretation and on what suits your situation better (thanks to #Federico klez Culloca):
Quadrilateral <- Parallelogram <- Rectangle <- Square
Make it scalable: In case of more complex shapes of elementary geometry included, I'd put probably place polygon below shape and then differentiate the descendants by the convexity and non-convexity first.
The design you have used is not idea (IMHO).
First, rename non-circular into Polygon (Also, us uppercase for the first letter).
Based on the implementation, a Circle is a specific Ellipse so I would have used inheritance here
Shape < -- Circular < -- Ellipse < -- Circle
< -- Polygon < -- Triangle < -- Equilateral
< -- ... //don't know the english names of those triangles
< -- Quadrilateral < -- Square
< -- Rectangle
< -- ...
< -- Hexagon
< -- ...
Each subclass of Polygon are abstract, those are used for the validation of the number of corners.
In general, I would have linked Square and Rectangle based on the geometry rule (same width and heigth) ( Square extends Rectangle) but based on your implementation using Point and Line, this is not required.
But using two classes would still allows some validation in the future ( every Line for a Square need to have the same length, ...).
This shows that a design depends mostly on the requirement, not only on the subject.
About Ellipse and Circle. An Ellipse is form of two points, if those point are the same, this is a Circle, this can be a link ;)
Related
I'm working on a collision system for my game, however I can't get it to work, if I add more than one wall (which is the object I'm rendering) the collision system doesn't work and I can get through the block.
However if I leave only one wall the collision works correctly, or if at the end of the loop I add a break;
the collision works but only on the first wall of the map, the others don't get the collision.
Would anyone know how to solve this? I've been trying to solve it for 2 days and I couldn't.
public boolean checkCollisionWall(int xnext, int ynext){
int[] xpoints1 = {xnext+3,xnext+3,xnext+4,xnext+3,xnext+3,xnext+4,xnext+10,xnext+11,xnext+11,xnext+10,xnext+11,xnext+11};
int[] ypoints1 = {ynext+0,ynext+8,ynext+9,ynext+11,ynext+12,ynext+15,ynext+15,ynext+12,ynext+11,ynext+9,ynext+8,ynext+0};
int npoints1 = 12;
Polygon player = new Polygon(xpoints1,ypoints1,npoints1);
Area area = new Area(player);
for(int i = 0; i < Game.walls.size(); i++){
Wall atual = Game.walls.get(i);
int[] xpoints2 = {atual.getX(),atual.getX(),atual.getX()+16,atual.getX()+16};
int[] ypoints2 = {atual.getY(),atual.getY()+16,atual.getY()+16,atual.getY()};
int npoints2 = 4;
Polygon Wall = new Polygon(xpoints2,ypoints2,npoints2);
area.intersect(new Area(Wall));
if(area.isEmpty()){
return true;
}
}
return false;
}
I'm pretty sure the problem is this call:
area.intersect(new Area(Wall));
Here's the JavaDoc for that method:
public void intersect(Area rhs)
Sets the shape of this Area to the intersection of its current shape
and the shape of the specified Area. The resulting shape of this Area
will include only areas that were contained in both this Area and also
in the specified Area.
So area, which represents the shape of your player, is going to be modified by each test with a wall, which is why it's only working with one wall.
You could fix the issue by simply making the player Area the argument of the call, as in:
Area wallArea = new Area(Wall);
wallArea.intersect(area);
if(wallArea.isEmpty()){
return true;
}
By the way, this logic is reversed isn't it. Don't you want to check that the resulting area is not empty, i.e. there was an overlap between the player and the wall?
The other option, if each Wall is actually a rectangle, would be to use the this Area method instead:
public boolean intersects(double x,
double y,
double w,
double h)
Tests if the interior of the Shape intersects the interior of a
specified rectangular area. The rectangular area is considered to
intersect the Shape if any point is contained in both the interior of
the Shape and the specified rectangular area.
Something like this:
if(area.intersects(atual.getX(), atual.getY(), 16, 16)) {
return true;
}
As this avoids the creation of an Area object for each wall, and the intersection test is going to be much simpler, and faster.
I'm creating a game, in which when I render many blocks. The fps goes seriously down and everything lags. I know why it is lagging, because of many objects being rendered at once, but I can't figure out how to create and implement a frustum culling or any type of culling class to my game.
NOTE: I'm using VBOs.
I just can't find on the net; please help.
Here is some of my code:
//Render Game this were I render my game
public void Render_GAME() {
Update();
if (isGameRunning == true) {
Update();
world.render();
p1.Update();
}
}
Flat Class: where I render block
package game.terrain.biomes;
import core.camera.*;
import core.graphics.*;
import core.math.*;
import game.blocks.*;
import game.Player;
public class Flat{
//Global Variables:
private int width;
private int height;
private int depth;
private SpaceStone[][][] blocks;
public Flat(int width, int height, int depth)
{
this.width = width;
this.height = height;
this.depth = depth;
blocks = new SpaceStone[width][height][depth];
createBlocks();
}
//Create Blocks
private void createBlocks()
{
SpaceStone.createBlock();
for(int x = 0; x < width; x += 5)
{
for(int y = 0; y < height; y += 5)
{
for(int z = 0; z < depth; z += 5)
{
blocks[x][y][z] = new SpaceStone(new Vector3f(x, y, z), new Vector3f(0, 0, 0), new Vector3f(2.5f, 2.5f, 5f));
}
}
}
}
//Render Blocks
private void renderBlocks()
{
Shader.BLOCK.Enable();
SpaceStone.blocktex.bindTexture();
SpaceStone.block.Bind();
Shader.BLOCK.setUniform1i("tex", 1);
Matrix4f viewMatrix = Player.getViewMatrix(Player.getCamera());
Shader.BLOCK.setUniformMat4f("pr_matrix", Player.getPerspective());
for(int i = 0; i < width; i += 5)
{
for(int j = 0; j < height; j += 5)
{
for(int k = 0; k < depth; k += 5)
{
Matrix4f transform = new Transformation().getTransform(blocks[i][j][k], viewMatrix);
Shader.BLOCK.setUniformMat4f("vw_matrix", transform);
SpaceStone.block.Draw();
}
}
}
Shader.BLOCK.Disable();
SpaceStone.blocktex.unbindTexture();
SpaceStone.block.Unbind();
}
//Render Flat Biome
public void renderFlatBiome()
{
//Render Blocks
renderBlocks();
}
}
If you want more information, such as classes or the whole project please comment and notify me.
OpenGL performs the frustum culling, it sounds like you need to do some processing every frame to decide which blocks to pass to OpenGL to render (or maybe not every frame, new frustum culling calculations would be required every time the geometry or the camera changes). You need to construct a representation of the frustum and test yourself which geometry to be rendered. The viewing frustum could be considered a volume and so you are looking for which cubes are contained within that volume.
First thing is to see which geometry is behind the near clipping plane and further than the far clipping plane. This can be done by simply calculating the distance to the near and far clipping planes and ensuring they are on the correct side of the plane.
The next thing to do is check which geometry is too far left or right to fit in the frustum, and this is slightly more complicated due to the nature and different projections. Orthographic projection is a lot easier to calculate since the frustum for an orthographic projection is essentially cuboid itself. Perspective is trapezoidal in shape (depending of the field of view), but the principle is the same. Construct two planes which represent the left and right clipping planes of the frustum, and cull geometry which is the 'wrong' side in relation to your camera position.
You are simply relieving GL of geometry to draw which GL will determine not to draw anyway. Depending on the scene, the size of the geometry, the way its stored in the buffers and other aspects, the overhead of binding/unbinding and the vertex processing could easily outweigh any performance hit from culling client side.
I haven't written java for years so can't provide source, however I have outlined the simplest form (and not necessarily optimized) of doing this client side (in relation to OpenGL). By spatial grouping geometry data this some form of hierarchy (bounding volume, KD-tree, AABB etc), you can reduce the amount tests required for the culling.
In a lot of cases, the most basic form of hierarchal grouping is Axis Aligned Bounding Box (AABB), which (if none of your cubes ever have a rotation) is what you already have since you are using cubes. Discrete geometry is usually grouped in some form using cuboid volumes denoted by bounding boxes or 'slabs' (two parallel planes which define a volume between them).
I'm developing a racing game like http://harmmade.com/vectorracer/ and I have implemented the A* algorithm to use for the AI players. The algorithm is working fine for 1-tile movements, but I don't want the AI players to only move 1 tile at a time (by using only their adjacent points), I need them to be able to accelerate and decelerate when they are closing in on a turn. Their next positions should depend on their previous one, just like Vector Racer.
public boolean createRoute() {
// The list where the points will be added in reverse order (from finish_point)
ArrayList<Track_Point> path = new ArrayList<>();
// The list where the unchecked points will be stored
ArrayList<Track_Point> open = new ArrayList<>();
// The list where the checked points will be stored
ArrayList<Track_Point> closed = new ArrayList<>();
// The starting point is always added as the first point to be checked
open.add(starting_point);
Track_Point current;
while (true) {
current = null;
// If all points from the open list have been removed (be being checked), it means that there isn't a possible path from the starting to the finish point
if (open.isEmpty()) {
System.out.println("no route available");
return false;
}
// Selects the point with the lowest F value from the open list
for (Track_Point temp : open) {
temp.show();
if (current == null || temp.getF() < current.getF()) {
current = temp;
}
}
// If the current point has reached the finish point, break the loop to construct the path
if (current.equals(finish_point)) {
break;
}
// Removes the current point (with the lowest F value) from the open list
open.remove(current);
// Adds the current point (with the lowest F value) to the closed list
closed.add(current);
ArrayList<Track_Point> possible_points = createNextPossibleTrackPoints(current);
//Sets the parent of the possible points
for (Track_Point tp : possible_points) {
if (!tp.equals(current)) {
tp.setParent(current);
}
}
for (Track_Point possible_point : possible_points) {
double nextG = current.getG() + current.distance(possible_point);
if (nextG < possible_point.getG()) {
open.remove(possible_point);
closed.remove(possible_point);
}
if (!open.contains(possible_point) && !closed.contains(possible_point)) {
possible_point.setParent(current);
open.add(possible_point);
}
}
}
//Track_Point current = finish_point;
while (current.getParent() != null) {
path.add(current);
current = current.getParent();
}
// optimalRacingLine is the list where all the points will be held in the correct order
optimalRacingLine.add(starting_point);
for (int k = path.size() - 1; k >= 0; k--) {
optimalRacingLine.add(path.get(k));
}
return true;
}
createPossiblePoints(Point current) so far returns a list of the current point's adjacents.
Each point's H value is calculated in their constructor, as I'm passing the finish point there and it calculates the distance between them.
Each point's G value is calculated when I set a parent to it, the G value is the distance from the new point to their parent + the parent's G value.
How do I modify this code to allow acceleration/deceleration?
The code of Track_Point:
package model;
import javafx.geometry.Point2D;
public class Track_Point extends Point2D {
private Track_Point parent, velocity;
private double f, g, h;
public Track_Point(double x, double y) {
super(x, y);
}
public Track_Point(double x, double y, Track_Point f) { // f is the finish point
super(x, y);
h = distance(f);
}
public void setParent(Track_Point tp) {
parent = tp;
g = distance(tp) + tp.getG();
f = g + h;
velocity = new Track_Point(getX() - parent.getX(), getY() - parent.getY());
}
public Track_Point getParent() {
return parent;
}
public double getG() {
return g;
}
public double getH() {
return h;
}
public double getF() {
return f;
}
public Track_Point getVelocity() {
return velocity;
}
#Override
public String toString() {
return "( " + (int) getX() + " , " + (int) getY() + " )";
}
public void show() {
System.out.println(toString());
}
}
Added some screenshots of my failed attempt and the working simple A* version
http://tinypic.com/r/zlakg2/8 - working version
http://tinypic.com/r/2e3u07o/8 - modified version (uses velocity as a parameter in the createNextPossiblePoints method)
Firstly, don't use an integers for the x/y position. There should be no such thing as '1 tile' in a racing game. Your game world and output can be completely different. For example, consider using doubles to store x and y. Ssh, don't worry, your JFrame doesn't need to know.
Heuristics
You are using A* to run your AI? Consider these additional heuristics:
Prefer high velocities; cost = max velocity - current velocity
Stay near inside edge of turn (imagine the turn as the outside edge of a circle); cost = distance_from(focus of turn)
Avoid walls; cost = isMovable(x, y) ? 0 : infinite/high
EDIT Prefer shortest path to avoid taking unnecessary moves as your second image does (Breadth First search not Djikstra); cost = steps from first node
The way A* works is as follows:
Use Djikstra (distance from origin) + Greedy (distance to target)
Insert your heuristics here
Add them all together and choose the lowest number
There's no such thing as f, g, or h; it's just mathematical nonsense you don't need to know.
Velocity
velocity = Math.abs(position1 - position2); so... position1 + velocity = position2.
You'll need to add the following variables:
int xVelocity
int yVelocity
Each moment, x += xVelocity; y += yVelocity.
The next position will be xf = x + xVelocity; yf = y + yVelocity. Then, you draw a ring around that position as follows:
+yVelocity
\|/
-xVelocity -0- +xVelocity
/|\
-yVelocity
So the center retains the same velocity, any adjacent side changes one velocity, and any diagonal side changes both velocities.
As for using A* the solution space of a turn is small enough that you can brute force it; don't add TrackPoints to the open list if you bump into a wall and prefer the highest velocity.
Really, that's all there is to it; simple stuff, but it can be tedious and difficult the first few times you need to do it.
EDIT: Just played vector racer and it's actually a lot simpler than I expected. I thought you were making a full blown 2d racing game. What I've told you is still very applicable, but you'll need to make a few adjustments, particularly to the way you handle rotation. You'll definitely want to look up the racing line. I don't have the time at the moment to go over the mathematics of the racing line, but this should help you calculate it.
EDIT2: Updated Velocity section. I'll do some calculations to figure out a faster heuristic, but what is present is enough to check 3-10 moves ahead without major performance issues.
I am working on a platformer game that will use tile maps, which I don't know if is a good idea!
I've made a neat tile map editor with tools for setting a spawn point etc. but now that I want to be able to test play the game after editing map and for future use I need of course integrate physics which I've done with Box2D which comes with LibGDX!
I am creating a method to create a collision map from tile map which has data if tile is collideable or not!
So I came up with this great idea:
loop through the map and if we find a colliding tile loop through its neighbor tiles and see if they're colliding too, and do this til noncolliding tile is found when we set width and height for the colliding rectangle
after we got bunch of rectangle I sort them in order from biggest square to smallest so we get biggest pieces and I add the rectangles to final list and check against the final rect if any of them overlaps with current body so I don't have overlapping bodys
But you know, code tells more than 1000 words, right?
public void createBody() {
List<Rectangle> allRects = new ArrayList<Rectangle>();
for(int x = 0; x < info.getWidth(); x++) {
for(int y = 0; y < info.getHeight(); y++) {
if(tiles[x][y].getInfo().isColliding()) {
int width = 1;
int height = 1;
//loop through neighbors horizontally
for(int i = 0; i < info.getWidth() - x; i++) {
if(!tiles[x + i][y].getInfo().isColliding()) {
//if tile is not clipped, we set width to i which is current x offset
width = i;
break;
}
}
//only if width is bigger than zero can the rect have any tiels..
if(width > 0) {
boolean breakingBad = false;
//loop through neighbors horizontally
for(int j = 0; j < info.getHeight() - y; j++) {
//loop though neigbors vertizally
for(int i = 0; i < width; i++) {
//check if tile is not colliding
if(!tiles[x + i][y + j].getInfo().isColliding()) {
//and if so, we set height to j which is current y offset
height = j;
//breaking bad aka leaving both loops
breakingBad = true;
break;
}
}
if(breakingBad) {
break;
}
}
}
if(width * height > 0)
allRects.add(new Rectangle(x, y, width, height));
}
}
}
Collections.sort(allRects, new Comparator<Rectangle>() {
#Override
public int compare(Rectangle o1, Rectangle o2) {
Integer o1Square = o1.width * o1.height;
Integer o2Square = o2.width * o2.height;
return o2Square.compareTo(o1Square);
}
});
List<Rectangle> finalRects = new ArrayList<Rectangle>();
mainloop:
for(Rectangle rect: allRects) {
for(Rectangle finalRect: finalRects) {
if(finalRect.contains(rect)) {
continue mainloop;
}
}
finalRects.add(rect);
}
for(Rectangle rect: finalRects) {
PolygonShape polyShape = new PolygonShape();
polyShape.setAsBox((float)rect.getWidth() / 2, (float)rect.getHeight() / 2, Vector2.tmp.set((float)rect.getCenterX(), (float)rect.getCenterY()), 0f);
mapBody.createFixture(polyShape, 1);
polyShape.dispose();
}
}
however this sill seems pretty inefficient because for some reasons its still creating smaller fixtures than it could be possible, for example in upper right corner
also its creating single fixtures in the corners of the center rectangle and I can't figure out why!
Is the whole idea all inefficient, and should I use other method or manually create collision maps or what could be the best idea?
Originally each tile was its own fixture which caused weird bugs on their edges as expected
First off, a custom tile mapping tool is a great idea on the surface, but you're reinventing the wheel.
libGDX has built-in support for TMX maps.
http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/maps/tiled/TmxMapLoader.html
Instead of using your homebrew editor, you can use a full featured editor such as this Tiled - http://www.mapeditor.org/
So once you have a better system in place for your maps, I would look at this from an object oriented perspective. Since you want to use box2d physics, each collidableTile HAS A body. So all you need to do is assign a physics body to each collidableTile, and set the size according to your standard tile size.
Don't forget that there is a difference between the box2d world and your game screen, where box2d is measured in metric units, and your screen is measured in pixels. So you need to do some math to set positions and size properly. If you want a set of tiles to share a body, you may want to pass in the body as a parameter when you construct each collidableTile, and then adjust the size of the body based on how many adjacent tiles you can find. More complex shapes for the physics body may be more complex.
You can also save resources by setting those tiles to 'sleep', where box2d does a reduced simulation on those bodies until it detects a collision. If you're only using box2d for collision detection on terrain, you may want to consider other options, like using shape libraries to detect intersections, and then setting the box2d physics on your player characters body to stop downward acceleration while there is contact, or something.
I'm working on a program right now that allows you to create shapes (square, rectangles and circles) you also have an option to create a compound shape by selecting already created shapes...I'm using two observers for the shapes. One for squares/rectangles and one for large circles and these observers list the reference points and sizes of the shapes. When a compound shape is created it should list the components in the square/rectangle frame if there are squares/rectangles in the compound shape.
I'm supposed to create the compound shape using composite pattern. So basically compound shapes and my circles, squares and rectangles need to be handled the same way
I have an array of objects called shapes, and compound shape is an object with an array of shapes in it.
My question is how can I check through the shapes array for an object of type compound shape and then check through the compound shapes array for an instance of rectangle or square?
Sorry for not including code but my program is somewhat large with a lot of classes
heres the methods I use to check for an instance of square or rectangle...these two methods are in two different classes. The shapes display in the right observer window when theyre just the simple shapes but the compound shapes dont display. If for example I had a shape list with 3 shapes...shape 1 is a large circle, shape 2 is a compound shape and shape 3 is a rectangle. And lets say the compound shape has 2 squares. Right now this would display the large circle and the rectangle but it wont display the compound shape components. I thought that once I got to the compoundShape, instanceof would look pick out an instanceof square or rectangle from the compundshape array
heres the compound shapes toString method
public String toString(){
String output="";
output += "Compound Shape: /n";
for (int i = 0; i< numShapes; i++){
output += shapes[i].toString();
}
return output;
}
heres the do method I use to look for an instance of square or rectangle
do {//squares rectangles
currentShape = shapes.getShape();
if (shapes.squareRectangleFinder())
outputString1 += currentShape.toString();
}while (shapes.next());
and heres the square finder method
public boolean squareRectangleFinder() {
if ((shapes[currentShape] instanceof Square)||(shapes[currentShape] instanceof Rectangle)){
return true;
}
return false;
}
I guess this is what "composite pattern" should do. Accordint to Wikipedia:
clients should ignore the difference between compositions of objects and individual objects
In my understanding it should be done like this (getters/setters, add/remove operations omitted):
interface Shape {
public int getLeftmostCoordinate();
}
class Rectangle implements Shape {
private int top;
private int left;
private int width;
private int height;
public int getLeftmostCoordinate() {
return left;
}
}
class Circle implements Shape {
private int x;
private int y;
private int r;
public int getLeftmostCoordinate() {
return x - r;
}
}
class CompoundShape implements Shape {
private Shape[] shapes;
public int getLeftmostCoordinate() {
int left = shapes[0].getLeftmostCoordinate();
for (int i=1; i<shapes.length; i++) {
int candidate = shapes[i].getLeftmostCoordinate();
if (candidate < left) {
left = candidate;
}
}
return left;
}
}