-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[1831] Add geometric primitives with tests
Bug: #1831 Signed-off-by: Pierre-Charles David <[email protected]>
- Loading branch information
Showing
7 changed files
with
715 additions
and
0 deletions.
There are no files selected for viewing
51 changes: 51 additions & 0 deletions
51
...yout/src/main/java/org/eclipse/sirius/components/diagrams/layout/experimental/Anchor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Obeo. | ||
* This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Obeo - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.sirius.components.diagrams.layout.experimental; | ||
|
||
import org.eclipse.sirius.components.diagrams.layoutdata.Position; | ||
import org.eclipse.sirius.components.diagrams.layoutdata.Size; | ||
|
||
/** | ||
* Possible values for anchoring a box inside a container. | ||
* | ||
* @author pcdavid | ||
*/ | ||
public enum Anchor { | ||
TOP_LEFT, | ||
TOP_CENTER, | ||
TOP_RIGHT, | ||
|
||
MIDDLE_LEFT, | ||
MIDDLE_CENTER, | ||
MIDDLE_RIGHT, | ||
|
||
BOTTOM_LEFT, | ||
BOTTOM_CENTER, | ||
BOTTOM_RIGHT; | ||
|
||
public Position apply(Rectangle container, Size boxSize) { | ||
return switch (this) { | ||
case TOP_LEFT -> container.topLeft(); | ||
case TOP_CENTER -> container.topCenter().translate(-boxSize.width() / 2.0, 0); | ||
case TOP_RIGHT -> container.topRight().translate(-boxSize.width(), 0.0); | ||
|
||
case MIDDLE_LEFT -> container.middleLeft().translate(0.0, -boxSize.height() / 2.0); | ||
case MIDDLE_CENTER -> container.middleCenter().translate(-boxSize.width() / 2.0, -boxSize.height() / 2.0); | ||
case MIDDLE_RIGHT -> container.middleRight().translate(-boxSize.width(), -boxSize.height() / 2.0); | ||
|
||
case BOTTOM_LEFT -> container.bottomLeft().translate(0.0, -boxSize.height()); | ||
case BOTTOM_CENTER -> container.bottomCenter().translate(-boxSize.width() / 2.0, -boxSize.height()); | ||
case BOTTOM_RIGHT -> container.bottomRight().translate(-boxSize.width(), -boxSize.height()); | ||
}; | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
...out/src/main/java/org/eclipse/sirius/components/diagrams/layout/experimental/Offsets.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Obeo. | ||
* This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Obeo - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.sirius.components.diagrams.layout.experimental; | ||
|
||
/** | ||
* Specifies the offsets in all 4 directions. Can be used to represent a margin, border, or padding. | ||
* | ||
* @author pcdavid | ||
*/ | ||
public record Offsets(double top, double bottom, double left, double right) { | ||
|
||
private static final Offsets EMPTY = Offsets.of(0.0); | ||
|
||
public Offsets(double value) { | ||
this(value, value, value, value); | ||
} | ||
|
||
public Offsets(double top, double bottom, double left, double right) { | ||
this.top = checkNonNegative(top, "top"); | ||
this.bottom = checkNonNegative(bottom, "bottom"); | ||
this.left = checkNonNegative(left, "left"); | ||
this.right = checkNonNegative(right, "right"); | ||
} | ||
|
||
public static Offsets of(double value) { | ||
return new Offsets(value); | ||
} | ||
|
||
public static Offsets empty() { | ||
return EMPTY; | ||
} | ||
|
||
public double width() { | ||
return this.left + this.right; | ||
} | ||
|
||
public double height() { | ||
return this.top + this.bottom; | ||
} | ||
|
||
public Offsets combine(Offsets other) { | ||
return new Offsets(this.top + other.top, this.bottom + other.bottom, this.left + other.left, this.right + other.right); | ||
} | ||
|
||
public Builder newOffsets() { | ||
return new Builder(); | ||
} | ||
|
||
private static double checkNonNegative(double value, String name) { | ||
if (value < 0) { | ||
throw new IllegalArgumentException(name + " can not be negative"); | ||
} | ||
return value; | ||
} | ||
|
||
public static class Builder { | ||
private double top; | ||
|
||
private double bottom; | ||
|
||
private double left; | ||
|
||
private double right; | ||
|
||
public Builder vertical(double value) { | ||
return this.top(value).bottom(value); | ||
} | ||
|
||
public Builder horizontal(double value) { | ||
return this.left(value).right(value); | ||
} | ||
|
||
public Builder top(double newTop) { | ||
this.top = checkNonNegative(newTop, "top"); | ||
return this; | ||
} | ||
|
||
public Builder bottom(double newBottom) { | ||
this.bottom = checkNonNegative(newBottom, "bottom"); | ||
return this; | ||
} | ||
|
||
public Builder left(double newLeft) { | ||
this.left = checkNonNegative(newLeft, "left"); | ||
return this; | ||
} | ||
|
||
public Builder right(double newRight) { | ||
this.right = checkNonNegative(newRight, "right"); | ||
return this; | ||
} | ||
|
||
public Offsets build() { | ||
return new Offsets(this.top, this.bottom, this.left, this.right); | ||
} | ||
|
||
} | ||
} |
148 changes: 148 additions & 0 deletions
148
...t/src/main/java/org/eclipse/sirius/components/diagrams/layout/experimental/Rectangle.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Obeo. | ||
* This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Obeo - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.sirius.components.diagrams.layout.experimental; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.Comparator; | ||
import java.util.List; | ||
|
||
import org.eclipse.sirius.components.diagrams.layoutdata.Position; | ||
import org.eclipse.sirius.components.diagrams.layoutdata.Size; | ||
|
||
/** | ||
* Represents a rectangular area. | ||
* | ||
* @author pcdavid | ||
*/ | ||
public record Rectangle(double x, double y, double width, double height) { | ||
|
||
public Rectangle(double x, double y, double width, double height) { | ||
if (width < 0) { | ||
throw new IllegalArgumentException("width can not be negative"); | ||
} | ||
if (height < 0) { | ||
throw new IllegalArgumentException("height can not be negative"); | ||
} | ||
this.x = x; | ||
this.y = y; | ||
this.width = width; | ||
this.height = height; | ||
} | ||
|
||
public Rectangle(Position position, Size size) { | ||
this(position.x(), position.y(), size.width(), size.height()); | ||
} | ||
|
||
public Rectangle translate(double dx, double dy) { | ||
return new Rectangle(this.x + dx, this.y + dy, this.width, this.height); | ||
} | ||
|
||
public Rectangle moveTo(Position newPosition) { | ||
return new Rectangle(newPosition.x(), newPosition.y(), this.width, this.height); | ||
} | ||
|
||
public Rectangle moveTo(double newX, double newY) { | ||
return new Rectangle(newX, newY, this.width, this.height); | ||
} | ||
|
||
public Rectangle resize(double newWidth, double newHeight) { | ||
return new Rectangle(this.x, this.y, newWidth, newHeight); | ||
} | ||
|
||
public Rectangle expand(Offsets offsets) { | ||
return new Rectangle(this.x - offsets.left(), this.y - offsets.top(), this.width + offsets.width(), this.height + offsets.height()); | ||
} | ||
|
||
public Rectangle shrink(Offsets offsets) { | ||
return new Rectangle(this.x + offsets.left(), this.y + offsets.top(), this.width - offsets.width(), this.height - offsets.height()); | ||
} | ||
|
||
public Rectangle union(Rectangle... others) { | ||
List<Rectangle> rectangles = new ArrayList<>(); | ||
rectangles.add(this); | ||
rectangles.addAll(Arrays.asList(others)); | ||
|
||
double minX = rectangles.stream().map(Rectangle::x).min(Comparator.naturalOrder()).orElse(0.0); | ||
double minY = rectangles.stream().map(Rectangle::y).min(Comparator.naturalOrder()).orElse(0.0); | ||
double maxX = rectangles.stream().map(r -> r.x + r.width).max(Comparator.naturalOrder()).orElse(0.0); | ||
double maxY = rectangles.stream().map(r -> r.y + r.height).max(Comparator.naturalOrder()).orElse(0.0); | ||
|
||
return new Rectangle(minX, minY, maxX - minX, maxY - minY); | ||
} | ||
|
||
public Rectangle intersection(Rectangle other) { | ||
double topleftX = Math.max(this.x, other.x); | ||
double topleftY = Math.max(this.y, other.y); | ||
double bottomrightX = Math.min(this.bottomRight().x(), other.bottomRight().x()); | ||
double bottomrightY = Math.min(this.bottomRight().y(), other.bottomRight().y()); | ||
if (topleftX > bottomrightX || topleftY > bottomrightY) { | ||
return new Rectangle(0, 0, 0, 0); | ||
} else { | ||
return new Rectangle(topleftX, topleftY, bottomrightX - topleftX, bottomrightY - topleftY); | ||
} | ||
} | ||
|
||
public boolean isEmpty() { | ||
return this.width == 0.0 || this.height == 0.0; | ||
} | ||
|
||
public boolean overlaps(Rectangle other) { | ||
return !this.intersection(other).isEmpty(); | ||
} | ||
|
||
public boolean includes(Rectangle other) { | ||
return this.intersection(other).equals(other); | ||
} | ||
|
||
public Size size() { | ||
return new Size(this.width, this.height); | ||
} | ||
|
||
public Position topLeft() { | ||
return new Position(this.x, this.y); | ||
} | ||
|
||
public Position topCenter() { | ||
return this.topLeft().midPoint(this.topRight()); | ||
} | ||
|
||
public Position topRight() { | ||
return new Position(this.x + this.width, this.y); | ||
} | ||
|
||
public Position middleLeft() { | ||
return this.topLeft().midPoint(this.bottomLeft()); | ||
} | ||
|
||
public Position middleCenter() { | ||
return this.topLeft().midPoint(this.bottomRight()); | ||
} | ||
|
||
public Position middleRight() { | ||
return this.topRight().midPoint(this.bottomRight()); | ||
} | ||
|
||
public Position bottomLeft() { | ||
return new Position(this.x, this.y + this.height); | ||
} | ||
|
||
public Position bottomCenter() { | ||
return this.bottomLeft().midPoint(this.bottomRight()); | ||
} | ||
|
||
public Position bottomRight() { | ||
return new Position(this.x + this.width, this.y + this.height); | ||
} | ||
|
||
} |
82 changes: 82 additions & 0 deletions
82
...src/test/java/org/eclipse/sirius/components/diagrams/layout/experimental/AnchorTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/******************************************************************************* | ||
* Copyright (c) 2023 Obeo. | ||
* This program and the accompanying materials | ||
* are made available under the terms of the Eclipse Public License v2.0 | ||
* which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-2.0/ | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* Obeo - initial API and implementation | ||
*******************************************************************************/ | ||
package org.eclipse.sirius.components.diagrams.layout.experimental; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import org.eclipse.sirius.components.diagrams.layoutdata.Position; | ||
import org.eclipse.sirius.components.diagrams.layoutdata.Size; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
/** | ||
* Tests for the box anchoring logic. | ||
* | ||
* @author pcdavid | ||
*/ | ||
public class AnchorTests { | ||
|
||
private Rectangle container; | ||
private Size boxSize; | ||
|
||
@BeforeEach | ||
public void setup() { | ||
this.container = new Rectangle(0, 0, 100, 100); | ||
this.boxSize = new Size(10, 10); | ||
} | ||
|
||
@Test | ||
public void testTopLeft() { | ||
assertThat(Anchor.TOP_LEFT.apply(this.container, this.boxSize)).isEqualTo(new Position(0, 0)); | ||
} | ||
|
||
@Test | ||
public void testTopCenter() { | ||
assertThat(Anchor.TOP_CENTER.apply(this.container, this.boxSize)).isEqualTo(new Position(45.0, 0)); | ||
} | ||
|
||
@Test | ||
public void testTopRight() { | ||
assertThat(Anchor.TOP_RIGHT.apply(this.container, this.boxSize)).isEqualTo(new Position(90.0, 0)); | ||
} | ||
|
||
@Test | ||
public void testMiddleLeft() { | ||
assertThat(Anchor.MIDDLE_LEFT.apply(this.container, this.boxSize)).isEqualTo(new Position(0.0, 45.0)); | ||
} | ||
|
||
@Test | ||
public void testMiddleCenter() { | ||
assertThat(Anchor.MIDDLE_CENTER.apply(this.container, this.boxSize)).isEqualTo(new Position(45.0, 45.0)); | ||
} | ||
|
||
@Test | ||
public void testMiddleRight() { | ||
assertThat(Anchor.MIDDLE_RIGHT.apply(this.container, this.boxSize)).isEqualTo(new Position(90.0, 45.0)); | ||
} | ||
|
||
@Test | ||
public void testBottomLeft() { | ||
assertThat(Anchor.BOTTOM_LEFT.apply(this.container, this.boxSize)).isEqualTo(new Position(0.0, 90.0)); | ||
} | ||
|
||
@Test | ||
public void testBottomCenter() { | ||
assertThat(Anchor.BOTTOM_CENTER.apply(this.container, this.boxSize)).isEqualTo(new Position(45.0, 90.0)); | ||
} | ||
|
||
@Test | ||
public void testBottomRight() { | ||
assertThat(Anchor.BOTTOM_RIGHT.apply(this.container, this.boxSize)).isEqualTo(new Position(90.0, 90.0)); | ||
} | ||
} |
Oops, something went wrong.