Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parent relationship to GraphicsObject #35

Merged
merged 3 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/edu/macalester/graphics/CanvasWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class CanvasWindow {

private final Canvas canvas;
private final JFrame windowFrame;
private final GraphicsGroup content = new GraphicsGroup();
private final GraphicsGroup content;
private Set<JComponent> embeddedComponents = Set.of();
private final Rectangle background;

Expand All @@ -101,7 +101,12 @@ public class CanvasWindow {
* @param windowHeight The height of the window's content area
*/
public CanvasWindow(String title, int windowWidth, int windowHeight) {
content.setCanvas(this); // propagates to descendants
content = new GraphicsGroup() {
@Override
public CanvasWindow getCanvas() {
return CanvasWindow.this;
}
};

// We use a Rectangle for the background because canvas.setBackground() triggers spurious
// repaints, whereas this approach puts background color changes into the same paint cycle
Expand Down
2 changes: 1 addition & 1 deletion src/edu/macalester/graphics/FrameRateReporter.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class FrameRateReporter {
private int framesSinceLastReport = 0;
private long timeOfLastReport = System.currentTimeMillis();

public void tick() {
public synchronized void tick() {
if (!enabled) {
return;
}
Expand Down
16 changes: 4 additions & 12 deletions src/edu/macalester/graphics/GraphicsGroup.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* The group defines its own coordinate system, so the positions of objects added to it are relative
* to the whole group's position.
* <p>
* Calling {@link setPosition(Point)} on a GraphicsGroup sets where the group’s local (0,0) shows up
* Calling {@link #setPosition(Point)} on a GraphicsGroup sets where the group’s local (0,0) shows up
* within its parent. This means that a group’s position is not necessarily the upper left, the
* center, or any other fixed relationship with the shapes inside the group. Instead, you determine
* how the group’s graphics relate to the whole group’s position when you set the position of each
Expand Down Expand Up @@ -61,7 +61,7 @@ public GraphicsGroup() {
public void add(GraphicsObject gObject) {
gObject.addObserver(this);
children.add(gObject);
gObject.setCanvas(getCanvas());
gObject.setParent(this);
changed();
}

Expand All @@ -85,7 +85,7 @@ public void add(GraphicsObject gObject, double x, double y) {
*/
public void remove(GraphicsObject gObject) {
gObject.removeObserver(this);
gObject.setCanvas(null);
gObject.setParent(null);
if (!children.removeIf(child -> child == gObject)) {
throw new NoSuchElementException("The object to remove is not part of this graphics group. Either it is already removed, or it was never originally added.");
}
Expand All @@ -100,7 +100,7 @@ public void removeAll() {
while (it.hasNext()) {
GraphicsObject obj = it.next();
obj.removeObserver(this);
obj.setCanvas(null);
obj.setParent(null);
it.remove();
}
changed();
Expand Down Expand Up @@ -190,14 +190,6 @@ void forEachDescendant(Point origin, BiConsumer<GraphicsObject,Point> callback)
}
}

@Override
void setCanvas(CanvasWindow canvas) {
super.setCanvas(canvas);
for (GraphicsObject child : children) {
child.setCanvas(canvas);
}
}

@Override
protected void changed() {
boundsNeedUpdate();
Expand Down
23 changes: 16 additions & 7 deletions src/edu/macalester/graphics/GraphicsObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
public abstract class GraphicsObject {
private final List<GraphicsObserver> observers = new ArrayList<>();
private CanvasWindow canvas;
private GraphicsGroup parent;

private Point position = Point.ORIGIN;
private double rotation = 0;
Expand Down Expand Up @@ -458,18 +458,27 @@ void forEachDescendant(Point origin, BiConsumer<GraphicsObject,Point> callback)
callback.accept(this, origin.add(getPosition()));
}

/**
* Returns the group that contains this graphics object, or null if it does not belong to a group.
*/
public GraphicsGroup getParent() {
return parent;
}

/**
* Returns the window that this Object is inside, or null if it does not belong to a window.
*/
public final CanvasWindow getCanvas() {
return canvas;
public CanvasWindow getCanvas() {
return (parent == null) ? null : parent.getCanvas();
}

void setCanvas(CanvasWindow canvas) {
if (canvas != this.canvas && canvas != null && this.canvas != null) {
throw new IllegalStateException("Trying to add graphics object to two different windows");
void setParent(GraphicsGroup parent) {
if (parent != this.parent && parent != null && this.parent != null) {
throw new IllegalStateException(
"Cannot add " + this.getClass().getSimpleName() + " to group,"
+ " because it already belongs to a different group");
}
this.canvas = canvas;
this.parent = parent;
}

/**
Expand Down
23 changes: 23 additions & 0 deletions test/edu/macalester/graphics/CanvasWindowTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,29 @@ void getElementAt() throws IOException {
assertEquals(null, canvas.getElementAt(new Point(41, 51)));
}

@Test
void getCanvas() {
GraphicsGroup g1 = new GraphicsGroup();
GraphicsGroup g2 = new GraphicsGroup();
GraphicsGroup g3 = new GraphicsGroup();
canvas = new CanvasWindow("getCanvas", 10, 10);

assertEquals(null, g1.getCanvas()); // Never added

g1.add(g2);
canvas.add(g1);
assertEquals(canvas, g1.getCanvas()); // Added directly
assertEquals(canvas, g2.getCanvas()); // Added indirectly via existing relationship

g2.add(g3);
assertEquals(canvas, g3.getCanvas()); // Added indirectly after parent already in canvas

g1.remove(g2);
assertEquals(canvas, g1.getCanvas());
assertEquals(null, g2.getCanvas());
assertEquals(null, g3.getCanvas());
}

@Test
void embeddedComponentHandling() throws IOException {
canvas = new CanvasWindow("embeddedComponentHandling", 320, 60);
Expand Down
20 changes: 20 additions & 0 deletions test/edu/macalester/graphics/GraphicsGroupTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ public GraphicsObject getGraphicsObject() {
return group;
}

@Test
void parentRelationships() {
group = new GraphicsGroup();
assertEquals(null, group.getParent());
assertEquals(null, circle.getParent());

group.add(circle);
assertEquals(group, circle.getParent());
assertEquals(null, group.getParent());

GraphicsGroup group2 = new GraphicsGroup();
assertThrows(IllegalStateException.class, () -> group2.add(circle));

group.remove(circle);
assertEquals(null, circle.getParent());

group2.add(circle);
assertEquals(group2, circle.getParent());
}

@RenderingTest
void simple() {
group = new GraphicsGroup();
Expand Down
Loading