Skip to content

Commit deee9a0

Browse files
Copilotmikebarkmin
andcommitted
Add origin support for Sprite class
Co-authored-by: mikebarkmin <2592379+mikebarkmin@users.noreply.github.com>
1 parent 99a3b7e commit deee9a0

6 files changed

Lines changed: 348 additions & 15 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package reference;
2+
3+
import org.openpatch.scratch.Origin;
4+
import org.openpatch.scratch.Sprite;
5+
import org.openpatch.scratch.Stage;
6+
import org.openpatch.scratch.extensions.recorder.*;
7+
8+
public class SpriteGetOrigin {
9+
public SpriteGetOrigin() {
10+
Stage myStage = new Stage(600, 240);
11+
Sprite mySprite = new Sprite("slime", "assets/slime.png");
12+
myStage.add(mySprite);
13+
mySprite.setPosition(-100, 0);
14+
15+
GifRecorder recorder =
16+
new GifRecorder("examples/reference/" + this.getClass().getName() + ".gif");
17+
recorder.start();
18+
19+
// Show origin getter methods
20+
mySprite.say("getOrigin(): " + mySprite.getOrigin());
21+
myStage.wait(1500);
22+
23+
// Set custom origin and show getters
24+
mySprite.setOrigin(-25, 15);
25+
mySprite.say(
26+
"getOriginX(): " + mySprite.getOriginX() + ", getOriginY(): " + mySprite.getOriginY());
27+
myStage.wait(1500);
28+
29+
// Set preset origin and show computed values
30+
mySprite.setOrigin(Origin.TOP_LEFT);
31+
mySprite.say(
32+
"TOP_LEFT -> X:" + (int) mySprite.getOriginX() + ", Y:" + (int) mySprite.getOriginY());
33+
myStage.wait(1500);
34+
35+
recorder.stop();
36+
myStage.exit();
37+
}
38+
39+
public static void main(String[] args) {
40+
new SpriteGetOrigin();
41+
}
42+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package reference;
2+
3+
import org.openpatch.scratch.Origin;
4+
import org.openpatch.scratch.Sprite;
5+
import org.openpatch.scratch.Stage;
6+
import org.openpatch.scratch.extensions.recorder.*;
7+
8+
public class SpriteSetOrigin {
9+
public SpriteSetOrigin() {
10+
Stage myStage = new Stage(600, 240);
11+
Sprite mySprite = new Sprite("slime", "assets/slime.png");
12+
myStage.add(mySprite);
13+
mySprite.setPosition(0, 0);
14+
15+
GifRecorder recorder =
16+
new GifRecorder("examples/reference/" + this.getClass().getName() + ".gif");
17+
recorder.start();
18+
19+
// Show default center origin
20+
mySprite.say("Origin: CENTER (default)");
21+
myStage.wait(1000);
22+
23+
// Demonstrate TOP_LEFT origin
24+
mySprite.setOrigin(Origin.TOP_LEFT);
25+
mySprite.say("Origin: TOP_LEFT");
26+
myStage.wait(1000);
27+
28+
// Demonstrate TOP_RIGHT origin
29+
mySprite.setOrigin(Origin.TOP_RIGHT);
30+
mySprite.say("Origin: TOP_RIGHT");
31+
myStage.wait(1000);
32+
33+
// Demonstrate BOTTOM_LEFT origin
34+
mySprite.setOrigin(Origin.BOTTOM_LEFT);
35+
mySprite.say("Origin: BOTTOM_LEFT");
36+
myStage.wait(1000);
37+
38+
// Demonstrate custom origin offset
39+
mySprite.setOrigin(-30, 20);
40+
mySprite.say("Origin: Custom (-30, 20)");
41+
myStage.wait(1000);
42+
43+
// Back to center
44+
mySprite.setOrigin(Origin.CENTER);
45+
mySprite.say("Origin: CENTER");
46+
myStage.wait(1000);
47+
48+
recorder.stop();
49+
myStage.exit();
50+
}
51+
52+
public static void main(String[] args) {
53+
new SpriteSetOrigin();
54+
}
55+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.openpatch.scratch;
2+
3+
/**
4+
* The Origin enum represents the different origin positions that a sprite can have.
5+
* The origin determines the reference point for the sprite's position and rotation.
6+
* By default, the origin is at the center of the sprite.
7+
*/
8+
public enum Origin {
9+
/** Origin at the top-left corner of the sprite */
10+
TOP_LEFT,
11+
/** Origin at the top-center of the sprite */
12+
TOP_CENTER,
13+
/** Origin at the top-right corner of the sprite */
14+
TOP_RIGHT,
15+
/** Origin at the center-left of the sprite */
16+
CENTER_LEFT,
17+
/** Origin at the center of the sprite (default) */
18+
CENTER,
19+
/** Origin at the center-right of the sprite */
20+
CENTER_RIGHT,
21+
/** Origin at the bottom-left corner of the sprite */
22+
BOTTOM_LEFT,
23+
/** Origin at the bottom-center of the sprite */
24+
BOTTOM_CENTER,
25+
/** Origin at the bottom-right corner of the sprite */
26+
BOTTOM_RIGHT,
27+
/** Custom origin position specified by x and y offsets */
28+
CUSTOM
29+
}

src/main/java/org/openpatch/scratch/Sprite.java

Lines changed: 148 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,9 @@ public interface WhenKeyPressedHandler {
163163
private boolean hitboxDisabled = false;
164164
private final Text text;
165165
private boolean isUI;
166+
private Origin origin = Origin.CENTER;
167+
private double originX = 0;
168+
private double originY = 0;
166169

167170
private WhenIReceiveHandler whenIReceiveHandler = (sprite, msg) -> {
168171
};
@@ -253,6 +256,9 @@ public Sprite(Sprite s) {
253256
this.whenKeyReleasedHandler = s.whenKeyReleasedHandler;
254257
this.whenKeyPressedHandler = s.whenKeyPressedHandler;
255258
this.isUI = s.isUI;
259+
this.origin = s.origin;
260+
this.originX = s.originX;
261+
this.originY = s.originY;
256262
}
257263

258264
/**
@@ -1024,6 +1030,98 @@ public void setRotationStyle(RotationStyle style) {
10241030
this.rotationStyle = style;
10251031
}
10261032

1033+
/**
1034+
* Sets the origin of the sprite to one of the predefined positions.
1035+
* The origin determines the reference point for the sprite's position and rotation.
1036+
* By default, the origin is at the center of the sprite (Origin.CENTER).
1037+
*
1038+
* @see Origin
1039+
* @param origin the origin position to set
1040+
*/
1041+
public void setOrigin(Origin origin) {
1042+
this.origin = origin;
1043+
if (origin != Origin.CUSTOM) {
1044+
this.originX = 0;
1045+
this.originY = 0;
1046+
}
1047+
}
1048+
1049+
/**
1050+
* Sets a custom origin offset for the sprite relative to its center.
1051+
* Positive x values move the origin to the right, negative to the left.
1052+
* Positive y values move the origin up, negative y values move it down.
1053+
* For example, setOrigin(-50, -50) offsets the origin 50 pixels left and 50 pixels down from center.
1054+
*
1055+
* @param x the x offset from center in pixels
1056+
* @param y the y offset from center in pixels
1057+
*/
1058+
public void setOrigin(double x, double y) {
1059+
this.origin = Origin.CUSTOM;
1060+
this.originX = x;
1061+
this.originY = y;
1062+
}
1063+
1064+
/**
1065+
* Returns the current origin setting of the sprite.
1066+
*
1067+
* @return the current origin
1068+
*/
1069+
public Origin getOrigin() {
1070+
return this.origin;
1071+
}
1072+
1073+
/**
1074+
* Returns the x offset of the origin from the center of the sprite.
1075+
* For predefined origins (non-custom), this calculates the offset based on
1076+
* the sprite's current dimensions.
1077+
*
1078+
* @return the x offset of the origin in pixels
1079+
*/
1080+
public double getOriginX() {
1081+
if (this.origin == Origin.CUSTOM) {
1082+
return this.originX;
1083+
}
1084+
double halfWidth = this.getWidth() / 2.0;
1085+
switch (this.origin) {
1086+
case TOP_LEFT:
1087+
case CENTER_LEFT:
1088+
case BOTTOM_LEFT:
1089+
return -halfWidth;
1090+
case TOP_RIGHT:
1091+
case CENTER_RIGHT:
1092+
case BOTTOM_RIGHT:
1093+
return halfWidth;
1094+
default:
1095+
return 0;
1096+
}
1097+
}
1098+
1099+
/**
1100+
* Returns the y offset of the origin from the center of the sprite.
1101+
* For predefined origins (non-custom), this calculates the offset based on
1102+
* the sprite's current dimensions.
1103+
*
1104+
* @return the y offset of the origin in pixels
1105+
*/
1106+
public double getOriginY() {
1107+
if (this.origin == Origin.CUSTOM) {
1108+
return this.originY;
1109+
}
1110+
double halfHeight = this.getHeight() / 2.0;
1111+
switch (this.origin) {
1112+
case TOP_LEFT:
1113+
case TOP_CENTER:
1114+
case TOP_RIGHT:
1115+
return halfHeight;
1116+
case BOTTOM_LEFT:
1117+
case BOTTOM_CENTER:
1118+
case BOTTOM_RIGHT:
1119+
return -halfHeight;
1120+
default:
1121+
return 0;
1122+
}
1123+
}
1124+
10271125
/**
10281126
* Sets the position of the sprite
10291127
*
@@ -1497,6 +1595,10 @@ public Hitbox getHitbox() {
14971595
var spriteWidth = this.show ? costumeWidth : this.pen.getSize();
14981596
var spriteHeight = this.show ? costumeHeight : this.pen.getSize();
14991597

1598+
// Get origin offset - the image is drawn offset from position by -originX, -originY
1599+
var originOffsetX = -this.getOriginX();
1600+
var originOffsetY = this.getOriginY();
1601+
15001602
var rotation = this.direction - 90;
15011603
if (this.rotationStyle == RotationStyle.DONT
15021604
|| this.rotationStyle == RotationStyle.LEFT_RIGHT) {
@@ -1506,22 +1608,38 @@ public Hitbox getHitbox() {
15061608
if (this.hitbox != null) {
15071609
this.hitbox.translateAndRotateAndResize(
15081610
rotation,
1509-
this.x,
1510-
-this.y,
1511-
this.x - spriteWidth / 2.0f,
1512-
-this.y - spriteHeight / 2.0f,
1611+
this.x + originOffsetX,
1612+
-this.y + originOffsetY,
1613+
this.x + originOffsetX - spriteWidth / 2.0f,
1614+
-this.y + originOffsetY - spriteHeight / 2.0f,
15131615
this.size);
15141616
return this.hitbox;
15151617
}
15161618

15171619
var cornerTopLeft = Utils.rotateXY(
1518-
this.x - spriteWidth / 2.0f, -this.y - spriteHeight / 2.0f, this.x, -this.y, rotation);
1620+
this.x + originOffsetX - spriteWidth / 2.0f,
1621+
-this.y + originOffsetY - spriteHeight / 2.0f,
1622+
this.x + originOffsetX,
1623+
-this.y + originOffsetY,
1624+
rotation);
15191625
var cornerTopRight = Utils.rotateXY(
1520-
this.x + spriteWidth / 2.0f, -this.y - spriteHeight / 2.0f, this.x, -this.y, rotation);
1626+
this.x + originOffsetX + spriteWidth / 2.0f,
1627+
-this.y + originOffsetY - spriteHeight / 2.0f,
1628+
this.x + originOffsetX,
1629+
-this.y + originOffsetY,
1630+
rotation);
15211631
var cornerBottomLeft = Utils.rotateXY(
1522-
this.x - spriteWidth / 2.0f, -this.y + spriteHeight / 2.0f, this.x, -this.y, rotation);
1632+
this.x + originOffsetX - spriteWidth / 2.0f,
1633+
-this.y + originOffsetY + spriteHeight / 2.0f,
1634+
this.x + originOffsetX,
1635+
-this.y + originOffsetY,
1636+
rotation);
15231637
var cornerBottomRight = Utils.rotateXY(
1524-
this.x + spriteWidth / 2.0f, -this.y + spriteHeight / 2.0f, this.x, -this.y, rotation);
1638+
this.x + originOffsetX + spriteWidth / 2.0f,
1639+
-this.y + originOffsetY + spriteHeight / 2.0f,
1640+
this.x + originOffsetX,
1641+
-this.y + originOffsetY,
1642+
rotation);
15251643

15261644
double[] xPoints = new double[4];
15271645
double[] yPoints = new double[4];
@@ -2275,7 +2393,16 @@ protected void draw(PGraphics buffer) {
22752393
var shader = this.getCurrentShader();
22762394
this.costumes
22772395
.get(this.currentCostume)
2278-
.draw(buffer, this.size, this.direction, this.x, this.y, this.rotationStyle, shader);
2396+
.draw(
2397+
buffer,
2398+
this.size,
2399+
this.direction,
2400+
this.x,
2401+
this.y,
2402+
this.rotationStyle,
2403+
shader,
2404+
this.getOriginX(),
2405+
this.getOriginY());
22792406
}
22802407
}
22812408

@@ -2293,7 +2420,15 @@ protected void drawDebug(PGraphics buffer) {
22932420
if (this.costumes.size() > 0 && this.show) {
22942421
this.costumes
22952422
.get(this.currentCostume)
2296-
.drawDebug(buffer, this.size, this.direction, this.x, this.y, this.rotationStyle);
2423+
.drawDebug(
2424+
buffer,
2425+
this.size,
2426+
this.direction,
2427+
this.x,
2428+
this.y,
2429+
this.rotationStyle,
2430+
this.getOriginX(),
2431+
this.getOriginY());
22972432
}
22982433
}
22992434

@@ -2303,7 +2438,9 @@ private Stamp getStamp() {
23032438
this.direction,
23042439
this.x,
23052440
this.y,
2306-
this.rotationStyle);
2441+
this.rotationStyle,
2442+
this.getOriginX(),
2443+
this.getOriginY());
23072444

23082445
return stamp;
23092446
}

0 commit comments

Comments
 (0)