Skip to content
Draft
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
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@
<artifactId>word-wrap</artifactId>
<version>0.1.13</version>
</dependency>

<!-- JUnit 5 for testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>

<profiles>
Expand Down
34 changes: 34 additions & 0 deletions src/examples/java/reference/SpriteGetOrigin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import org.openpatch.scratch.*;

public class SpriteGetOrigin extends Window {
public SpriteGetOrigin() {
super(800, 600);
var stage = new Stage();

var sprite = new Sprite("slime", "assets/slime.png");
sprite.setPosition(0, 0);

// Test getting default origin
System.out.println("Default origin: " + sprite.getOrigin());
System.out.println("Default originX: " + sprite.getOriginX());
System.out.println("Default originY: " + sprite.getOriginY());

// Test setting and getting predefined origin
sprite.setOrigin(Origin.TOP_LEFT);
System.out.println("After setOrigin(TOP_LEFT): " + sprite.getOrigin());

// Test setting and getting custom origin
sprite.setOrigin(50, -25);
System.out.println("After setOrigin(50, -25):");
System.out.println(" Origin mode: " + sprite.getOrigin());
System.out.println(" originX: " + sprite.getOriginX());
System.out.println(" originY: " + sprite.getOriginY());

stage.add(sprite);
this.setStage(stage);
}

public static void main(String[] args) {
new SpriteGetOrigin();
}
}
60 changes: 60 additions & 0 deletions src/examples/java/reference/SpriteSetOrigin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import org.openpatch.scratch.*;

public class SpriteSetOrigin extends Window {
public SpriteSetOrigin() {
super(800, 600);
var stage = new Stage();

// Create a sprite with centered origin (default)
var centerSprite = new Sprite("slime", "assets/slime.png");
centerSprite.setPosition(200, 0);
centerSprite.setOrigin(Origin.CENTER);
stage.add(centerSprite);

// Create a sprite with top-left origin
var topLeftSprite = new Sprite("slime", "assets/slime.png");
topLeftSprite.setPosition(400, 0);
topLeftSprite.setOrigin(Origin.TOP_LEFT);
stage.add(topLeftSprite);

// Create a sprite with bottom-right origin
var bottomRightSprite = new Sprite("slime", "assets/slime.png");
bottomRightSprite.setPosition(600, 0);
bottomRightSprite.setOrigin(Origin.BOTTOM_RIGHT);
stage.add(bottomRightSprite);

// Create a sprite with custom origin
var customSprite = new Sprite("slime", "assets/slime.png");
customSprite.setPosition(200, -150);
customSprite.setOrigin(-30, -30);
stage.add(customSprite);

// Create rotating sprites to test rotation with different origins
var rotatingCenter = new Sprite("slime", "assets/slime.png") {
@Override
public void run() {
this.turnRight(1);
}
};
rotatingCenter.setPosition(400, -150);
rotatingCenter.setOrigin(Origin.CENTER);
stage.add(rotatingCenter);

var rotatingTopLeft = new Sprite("slime", "assets/slime.png") {
@Override
public void run() {
this.turnRight(1);
}
};
rotatingTopLeft.setPosition(600, -150);
rotatingTopLeft.setOrigin(Origin.TOP_LEFT);
stage.add(rotatingTopLeft);

this.setStage(stage);
this.setDebug(true);
}

public static void main(String[] args) {
new SpriteSetOrigin();
}
}
28 changes: 28 additions & 0 deletions src/main/java/org/openpatch/scratch/Origin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package org.openpatch.scratch;

/**
* The Origin enum represents the different origin positions that a sprite can have.
* The origin determines the reference point for positioning and rotation.
*/
public enum Origin {
/** Origin at the top-left corner */
TOP_LEFT,
/** Origin at the top-center */
TOP_CENTER,
/** Origin at the top-right corner */
TOP_RIGHT,
/** Origin at the center-left */
CENTER_LEFT,
/** Origin at the center (default) */
CENTER,
/** Origin at the center-right */
CENTER_RIGHT,
/** Origin at the bottom-left corner */
BOTTOM_LEFT,
/** Origin at the bottom-center */
BOTTOM_CENTER,
/** Origin at the bottom-right corner */
BOTTOM_RIGHT,
/** Custom origin position */
CUSTOM
}
160 changes: 149 additions & 11 deletions src/main/java/org/openpatch/scratch/Sprite.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ public interface WhenKeyPressedHandler {
private double x = 0;
private double y = 0;
private double direction = 90;
private Origin origin = Origin.CENTER;
private double originX = 0;
private double originY = 0;
private Stage stage;
private final AbstractMap<String, Timer> timer;
private final Pen pen;
Expand Down Expand Up @@ -235,6 +238,9 @@ public Sprite(Sprite s) {
this.x = s.x;
this.y = s.y;
this.direction = s.direction;
this.origin = s.origin;
this.originX = s.originX;
this.originY = s.originY;
this.stage = s.stage;
this.timer = new ConcurrentHashMap<>();
this.timer.put("default", new Timer());
Expand Down Expand Up @@ -1173,6 +1179,64 @@ public double getDirection() {
return this.direction;
}

/**
* Sets the origin of the sprite to a predefined position.
* The origin determines the reference point for positioning and rotation.
*
* @param origin the predefined origin position
*/
public void setOrigin(Origin origin) {
this.origin = origin;
// Calculate offset based on the predefined position
// These offsets are relative to the center (0, 0) in costume space
if (origin != Origin.CUSTOM) {
// Reset custom offsets when using predefined position
this.originX = 0;
this.originY = 0;
}
}

/**
* Sets the origin of the sprite to a custom position.
* The coordinates are relative to the center of the costume.
* Positive x moves origin to the right, positive y moves origin down.
*
* @param x the x offset from center in pixels
* @param y the y offset from center in pixels
*/
public void setOrigin(double x, double y) {
this.origin = Origin.CUSTOM;
this.originX = x;
this.originY = y;
}

/**
* Returns the current origin mode of the sprite.
*
* @return the origin mode
*/
public Origin getOrigin() {
return this.origin;
}

/**
* Returns the x offset of the origin from the center.
*
* @return the x offset in pixels
*/
public double getOriginX() {
return this.originX;
}

/**
* Returns the y offset of the origin from the center.
*
* @return the y offset in pixels
*/
public double getOriginY() {
return this.originY;
}

/**
* Returns the pen of the sprite.
*
Expand Down Expand Up @@ -1503,25 +1567,77 @@ public Hitbox getHitbox() {
rotation = 0;
}

// Calculate origin offset based on origin mode
double offsetX = 0;
double offsetY = 0;

switch (this.origin) {
case TOP_LEFT:
offsetX = spriteWidth / 2.0;
offsetY = -spriteHeight / 2.0;
break;
case TOP_CENTER:
offsetX = 0;
offsetY = -spriteHeight / 2.0;
break;
case TOP_RIGHT:
offsetX = -spriteWidth / 2.0;
offsetY = -spriteHeight / 2.0;
break;
case CENTER_LEFT:
offsetX = spriteWidth / 2.0;
offsetY = 0;
break;
case CENTER:
offsetX = 0;
offsetY = 0;
break;
case CENTER_RIGHT:
offsetX = -spriteWidth / 2.0;
offsetY = 0;
break;
case BOTTOM_LEFT:
offsetX = spriteWidth / 2.0;
offsetY = spriteHeight / 2.0;
break;
case BOTTOM_CENTER:
offsetX = 0;
offsetY = spriteHeight / 2.0;
break;
case BOTTOM_RIGHT:
offsetX = -spriteWidth / 2.0;
offsetY = spriteHeight / 2.0;
break;
case CUSTOM:
// For custom origin, apply the offset from center
offsetX = -this.originX;
offsetY = this.originY;
break;
}

// Apply origin offset to the hitbox base position
double hitboxCenterX = this.x + offsetX;
double hitboxCenterY = this.y + offsetY;

if (this.hitbox != null) {
this.hitbox.translateAndRotateAndResize(
rotation,
this.x,
-this.y,
this.x - spriteWidth / 2.0f,
-this.y - spriteHeight / 2.0f,
hitboxCenterX,
-hitboxCenterY,
hitboxCenterX - spriteWidth / 2.0f,
-hitboxCenterY - spriteHeight / 2.0f,
this.size);
return this.hitbox;
}

var cornerTopLeft = Utils.rotateXY(
this.x - spriteWidth / 2.0f, -this.y - spriteHeight / 2.0f, this.x, -this.y, rotation);
hitboxCenterX - spriteWidth / 2.0f, -hitboxCenterY - spriteHeight / 2.0f, hitboxCenterX, -hitboxCenterY, rotation);
var cornerTopRight = Utils.rotateXY(
this.x + spriteWidth / 2.0f, -this.y - spriteHeight / 2.0f, this.x, -this.y, rotation);
hitboxCenterX + spriteWidth / 2.0f, -hitboxCenterY - spriteHeight / 2.0f, hitboxCenterX, -hitboxCenterY, rotation);
var cornerBottomLeft = Utils.rotateXY(
this.x - spriteWidth / 2.0f, -this.y + spriteHeight / 2.0f, this.x, -this.y, rotation);
hitboxCenterX - spriteWidth / 2.0f, -hitboxCenterY + spriteHeight / 2.0f, hitboxCenterX, -hitboxCenterY, rotation);
var cornerBottomRight = Utils.rotateXY(
this.x + spriteWidth / 2.0f, -this.y + spriteHeight / 2.0f, this.x, -this.y, rotation);
hitboxCenterX + spriteWidth / 2.0f, -hitboxCenterY + spriteHeight / 2.0f, hitboxCenterX, -hitboxCenterY, rotation);

double[] xPoints = new double[4];
double[] yPoints = new double[4];
Expand Down Expand Up @@ -2275,7 +2391,17 @@ protected void draw(PGraphics buffer) {
var shader = this.getCurrentShader();
this.costumes
.get(this.currentCostume)
.draw(buffer, this.size, this.direction, this.x, this.y, this.rotationStyle, shader);
.draw(
buffer,
this.size,
this.direction,
this.x,
this.y,
this.rotationStyle,
shader,
this.origin,
this.originX,
this.originY);
}
}

Expand All @@ -2293,7 +2419,16 @@ protected void drawDebug(PGraphics buffer) {
if (this.costumes.size() > 0 && this.show) {
this.costumes
.get(this.currentCostume)
.drawDebug(buffer, this.size, this.direction, this.x, this.y, this.rotationStyle);
.drawDebug(
buffer,
this.size,
this.direction,
this.x,
this.y,
this.rotationStyle,
this.origin,
this.originX,
this.originY);
}
}

Expand All @@ -2303,7 +2438,10 @@ private Stamp getStamp() {
this.direction,
this.x,
this.y,
this.rotationStyle);
this.rotationStyle,
this.origin,
this.originX,
this.originY);

return stamp;
}
Expand Down
Loading