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
42 changes: 42 additions & 0 deletions src/examples/java/reference/SpriteGetOrigin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package reference;

import org.openpatch.scratch.Origin;
import org.openpatch.scratch.Sprite;
import org.openpatch.scratch.Stage;
import org.openpatch.scratch.extensions.recorder.*;

public class SpriteGetOrigin {
public SpriteGetOrigin() {
Stage myStage = new Stage(600, 240);
Sprite mySprite = new Sprite("slime", "assets/slime.png");
myStage.add(mySprite);
mySprite.setPosition(-100, 0);

GifRecorder recorder =
new GifRecorder("examples/reference/" + this.getClass().getName() + ".gif");
recorder.start();

// Show origin getter methods
mySprite.say("getOrigin(): " + mySprite.getOrigin());
myStage.wait(1500);

// Set custom origin and show getters
mySprite.setOrigin(-25, 15);
mySprite.say(
"getOriginX(): " + mySprite.getOriginX() + ", getOriginY(): " + mySprite.getOriginY());
myStage.wait(1500);

// Set preset origin and show computed values
mySprite.setOrigin(Origin.TOP_LEFT);
mySprite.say(
"TOP_LEFT -> X:" + (int) mySprite.getOriginX() + ", Y:" + (int) mySprite.getOriginY());
myStage.wait(1500);

recorder.stop();
myStage.exit();
}

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

import org.openpatch.scratch.Origin;
import org.openpatch.scratch.Sprite;
import org.openpatch.scratch.Stage;
import org.openpatch.scratch.extensions.recorder.*;

public class SpriteSetOrigin {
public SpriteSetOrigin() {
Stage myStage = new Stage(600, 240);
Sprite mySprite = new Sprite("slime", "assets/slime.png");
myStage.add(mySprite);
mySprite.setPosition(0, 0);

GifRecorder recorder =
new GifRecorder("examples/reference/" + this.getClass().getName() + ".gif");
recorder.start();

// Show default center origin
mySprite.say("Origin: CENTER (default)");
myStage.wait(1000);

// Demonstrate TOP_LEFT origin
mySprite.setOrigin(Origin.TOP_LEFT);
mySprite.say("Origin: TOP_LEFT");
myStage.wait(1000);

// Demonstrate TOP_RIGHT origin
mySprite.setOrigin(Origin.TOP_RIGHT);
mySprite.say("Origin: TOP_RIGHT");
myStage.wait(1000);

// Demonstrate BOTTOM_LEFT origin
mySprite.setOrigin(Origin.BOTTOM_LEFT);
mySprite.say("Origin: BOTTOM_LEFT");
myStage.wait(1000);

// Demonstrate custom origin offset
mySprite.setOrigin(-30, 20);
mySprite.say("Origin: Custom (-30, 20)");
myStage.wait(1000);

// Back to center
mySprite.setOrigin(Origin.CENTER);
mySprite.say("Origin: CENTER");
myStage.wait(1000);

recorder.stop();
myStage.exit();
}

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

/**
* The Origin enum represents the different origin positions that a sprite can have.
* The origin determines the reference point for the sprite's position and rotation.
* By default, the origin is at the center of the sprite.
*/
public enum Origin {
/** Origin at the top-left corner of the sprite */
TOP_LEFT,
/** Origin at the top-center of the sprite */
TOP_CENTER,
/** Origin at the top-right corner of the sprite */
TOP_RIGHT,
/** Origin at the center-left of the sprite */
CENTER_LEFT,
/** Origin at the center of the sprite (default) */
CENTER,
/** Origin at the center-right of the sprite */
CENTER_RIGHT,
/** Origin at the bottom-left corner of the sprite */
BOTTOM_LEFT,
/** Origin at the bottom-center of the sprite */
BOTTOM_CENTER,
/** Origin at the bottom-right corner of the sprite */
BOTTOM_RIGHT,
/** Custom origin position specified by x and y offsets */
CUSTOM
}
159 changes: 148 additions & 11 deletions src/main/java/org/openpatch/scratch/Sprite.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ public interface WhenKeyPressedHandler {
private boolean hitboxDisabled = false;
private final Text text;
private boolean isUI;
private Origin origin = Origin.CENTER;
private double originX = 0;
private double originY = 0;

private WhenIReceiveHandler whenIReceiveHandler = (sprite, msg) -> {
};
Expand Down Expand Up @@ -253,6 +256,9 @@ public Sprite(Sprite s) {
this.whenKeyReleasedHandler = s.whenKeyReleasedHandler;
this.whenKeyPressedHandler = s.whenKeyPressedHandler;
this.isUI = s.isUI;
this.origin = s.origin;
this.originX = s.originX;
this.originY = s.originY;
}

/**
Expand Down Expand Up @@ -1024,6 +1030,98 @@ public void setRotationStyle(RotationStyle style) {
this.rotationStyle = style;
}

/**
* Sets the origin of the sprite to one of the predefined positions.
* The origin determines the reference point for the sprite's position and rotation.
* By default, the origin is at the center of the sprite (Origin.CENTER).
*
* @see Origin
* @param origin the origin position to set
*/
public void setOrigin(Origin origin) {
this.origin = origin;
if (origin != Origin.CUSTOM) {
this.originX = 0;
this.originY = 0;
}
}

/**
* Sets a custom origin offset for the sprite relative to its center.
* Positive x values move the origin to the right, negative to the left.
* Positive y values move the origin up, negative y values move it down.
* For example, setOrigin(-50, -50) offsets the origin 50 pixels left and 50 pixels down from center.
*
* @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 setting of the sprite.
*
* @return the current origin
*/
public Origin getOrigin() {
return this.origin;
}

/**
* Returns the x offset of the origin from the center of the sprite.
* For predefined origins (non-custom), this calculates the offset based on
* the sprite's current dimensions.
*
* @return the x offset of the origin in pixels
*/
public double getOriginX() {
if (this.origin == Origin.CUSTOM) {
return this.originX;
}
double halfWidth = this.getWidth() / 2.0;
switch (this.origin) {
case TOP_LEFT:
case CENTER_LEFT:
case BOTTOM_LEFT:
return -halfWidth;
case TOP_RIGHT:
case CENTER_RIGHT:
case BOTTOM_RIGHT:
return halfWidth;
default:
return 0;
}
}

/**
* Returns the y offset of the origin from the center of the sprite.
* For predefined origins (non-custom), this calculates the offset based on
* the sprite's current dimensions.
*
* @return the y offset of the origin in pixels
*/
public double getOriginY() {
if (this.origin == Origin.CUSTOM) {
return this.originY;
}
double halfHeight = this.getHeight() / 2.0;
switch (this.origin) {
case TOP_LEFT:
case TOP_CENTER:
case TOP_RIGHT:
return halfHeight;
case BOTTOM_LEFT:
case BOTTOM_CENTER:
case BOTTOM_RIGHT:
return -halfHeight;
default:
return 0;
}
}

/**
* Sets the position of the sprite
*
Expand Down Expand Up @@ -1497,6 +1595,10 @@ public Hitbox getHitbox() {
var spriteWidth = this.show ? costumeWidth : this.pen.getSize();
var spriteHeight = this.show ? costumeHeight : this.pen.getSize();

// Get origin offset - the image is drawn offset from position by -originX, -originY
var originOffsetX = -this.getOriginX();
var originOffsetY = this.getOriginY();

var rotation = this.direction - 90;
if (this.rotationStyle == RotationStyle.DONT
|| this.rotationStyle == RotationStyle.LEFT_RIGHT) {
Expand All @@ -1506,22 +1608,38 @@ public Hitbox getHitbox() {
if (this.hitbox != null) {
this.hitbox.translateAndRotateAndResize(
rotation,
this.x,
-this.y,
this.x - spriteWidth / 2.0f,
-this.y - spriteHeight / 2.0f,
this.x + originOffsetX,
-this.y + originOffsetY,
this.x + originOffsetX - spriteWidth / 2.0f,
-this.y + originOffsetY - 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);
this.x + originOffsetX - spriteWidth / 2.0f,
-this.y + originOffsetY - spriteHeight / 2.0f,
this.x + originOffsetX,
-this.y + originOffsetY,
rotation);
var cornerTopRight = Utils.rotateXY(
this.x + spriteWidth / 2.0f, -this.y - spriteHeight / 2.0f, this.x, -this.y, rotation);
this.x + originOffsetX + spriteWidth / 2.0f,
-this.y + originOffsetY - spriteHeight / 2.0f,
this.x + originOffsetX,
-this.y + originOffsetY,
rotation);
var cornerBottomLeft = Utils.rotateXY(
this.x - spriteWidth / 2.0f, -this.y + spriteHeight / 2.0f, this.x, -this.y, rotation);
this.x + originOffsetX - spriteWidth / 2.0f,
-this.y + originOffsetY + spriteHeight / 2.0f,
this.x + originOffsetX,
-this.y + originOffsetY,
rotation);
var cornerBottomRight = Utils.rotateXY(
this.x + spriteWidth / 2.0f, -this.y + spriteHeight / 2.0f, this.x, -this.y, rotation);
this.x + originOffsetX + spriteWidth / 2.0f,
-this.y + originOffsetY + spriteHeight / 2.0f,
this.x + originOffsetX,
-this.y + originOffsetY,
rotation);

double[] xPoints = new double[4];
double[] yPoints = new double[4];
Expand Down Expand Up @@ -2275,7 +2393,16 @@ 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.getOriginX(),
this.getOriginY());
}
}

Expand All @@ -2293,7 +2420,15 @@ 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.getOriginX(),
this.getOriginY());
}
}

Expand All @@ -2303,7 +2438,9 @@ private Stamp getStamp() {
this.direction,
this.x,
this.y,
this.rotationStyle);
this.rotationStyle,
this.getOriginX(),
this.getOriginY());

return stamp;
}
Expand Down
Loading