Skip to content

Commit

Permalink
Merge pull request #4955 from kwvanderlinde/bugfix/4929-improper-faci…
Browse files Browse the repository at this point in the history
…ng-set

Do not modify token facing when calculating light and vision
  • Loading branch information
cwisniew authored Oct 2, 2024
2 parents 59bac50 + 3eb4a99 commit fe10790
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 74 deletions.
70 changes: 32 additions & 38 deletions src/main/java/net/rptools/maptool/model/Grid.java
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,12 @@ public void setSize(int size) {
}
int visionDistance = zone.getTokenVisionInPixels();
double visionRange = (range == 0) ? visionDistance : range * getSize() / zone.getUnitsPerCell();
/* Token facing as an angle. 0° points to the right and clockwise is positive. */
int tokenFacingAngle = token.getFacingInDegrees() + 90;
Rectangle footprint = token.getFootprint(this).getBounds(this);

if (scaleWithToken) {
double footprintWidth = token.getFootprint(this).getBounds(this).getWidth() / 2;
double footprintWidth = footprint.getWidth() / 2;

// Test for gridless maps
var cellShape = getCellShape();
Expand All @@ -423,85 +426,76 @@ public void setSize(int size) {
} else {
// For grids, this will be the same, but for Hex's we'll use the smaller side depending on
// which Hex type you choose
double footprintHeight = token.getFootprint(this).getBounds(this).getHeight() / 2;
double footprintHeight = footprint.getHeight() / 2;
visionRange += Math.min(footprintWidth, footprintHeight);
}
}

Area visibleArea = new Area();
Area visibleArea;
switch (shape) {
case CIRCLE:
case CIRCLE -> {
visibleArea =
GraphicsUtil.createLineSegmentEllipse(
-visionRange, -visionRange, visionRange, visionRange, CIRCLE_SEGMENTS);
break;
case GRID:
}
case GRID -> {
visibleArea = getGridArea(token, range, scaleWithToken, visionRange);
break;
case SQUARE:
}
case SQUARE -> {
visibleArea =
new Area(
new Rectangle2D.Double(
-visionRange, -visionRange, visionRange * 2, visionRange * 2));
break;
case BEAM:
}
case BEAM -> {
// Make at least 1 pixel on each side, so it's at least visible at 100% zoom.
var pixelWidth = Math.max(2, width * getSize() / zone.getUnitsPerCell());
Shape lineShape = new Rectangle2D.Double(0, -pixelWidth / 2, visionRange, pixelWidth);
Shape visibleShape = new GeneralPath(lineShape);

visibleArea =
new Area(
AffineTransform.getRotateInstance(
Math.toRadians(offsetAngle) - Math.toRadians(token.getFacing()))
.createTransformedShape(visibleShape));
break;
case CONE:
AffineTransform.getRotateInstance(Math.toRadians(offsetAngle + tokenFacingAngle))
.createTransformedShape(lineShape));
}
case CONE -> {
Arc2D cone =
new Arc2D.Double(
-visionRange,
-visionRange,
visionRange * 2,
visionRange * 2,
360.0 - (arcAngle / 2.0) + (offsetAngle * 1.0),
(offsetAngle - tokenFacingAngle) - arcAngle / 2.,
arcAngle,
Arc2D.PIE);

// Flatten the cone to remove 'curves'
GeneralPath path = new GeneralPath();
path.append(cone.getPathIterator(null, 1), false);
Area tempvisibleArea = new Area(path);

// Rotate
tempvisibleArea =
tempvisibleArea.createTransformedArea(
AffineTransform.getRotateInstance(-Math.toRadians(token.getFacing())));

Rectangle footprint = token.getFootprint(this).getBounds(this);
footprint.x = -footprint.width / 2;
footprint.y = -footprint.height / 2;

visibleArea.add(new Area(footprint));
visibleArea.add(tempvisibleArea);
break;
case HEX:
footprint = token.getFootprint(this).getBounds(this);
visibleArea = new Area(path);

var footprintPart = new Rectangle(footprint);
footprintPart.x = -footprintPart.width / 2;
footprintPart.y = -footprintPart.height / 2;
visibleArea.add(new Area(footprintPart));
}
case HEX -> {
double x = footprint.getCenterX();
double y = footprint.getCenterY();

double footprintWidth = token.getFootprint(this).getBounds(this).getWidth();
double footprintHeight = token.getFootprint(this).getBounds(this).getHeight();
double footprintWidth = footprint.getWidth();
double footprintHeight = footprint.getHeight();
double adjustment = Math.min(footprintWidth, footprintHeight);
x -= adjustment / 2;
y -= adjustment / 2;

visibleArea = createHex(x, y, visionRange, 0);
break;
default:
}
default -> {
log.error("Unhandled shape {}; treating as a circle", shape);
visibleArea =
GraphicsUtil.createLineSegmentEllipse(
-visionRange, -visionRange, visionRange * 2, visionRange * 2, CIRCLE_SEGMENTS);
break;
}
}

return visibleArea;
Expand Down
56 changes: 21 additions & 35 deletions src/main/java/net/rptools/maptool/model/IsometricGrid.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.swing.Action;
import javax.swing.KeyStroke;
import net.rptools.maptool.client.AppPreferences;
Expand Down Expand Up @@ -267,7 +268,7 @@ public void uninstallMovementKeys(Map<KeyStroke, Action> actionMap) {
}

@Override
public Area getShapedArea(
public @Nonnull Area getShapedArea(
ShapeType shape,
Token token,
double range,
Expand All @@ -281,37 +282,32 @@ public Area getShapedArea(
int visionDistance = getZone().getTokenVisionInPixels();
double visionRange =
(range == 0) ? visionDistance : range * getSize() / getZone().getUnitsPerCell();
int tokenFacingAngle = token.getFacingInDegrees() + 90;
Rectangle footprint = token.getFootprint(this).getBounds(this);

if (scaleWithToken) {
Rectangle footprint = token.getFootprint(this).getBounds(this);
visionRange += footprint.getHeight() / 2;
System.out.println(token.getName() + " footprint.getWidth() " + footprint.getWidth());
System.out.println(token.getName() + " footprint.getHeight() " + footprint.getHeight());
}
// System.out.println("this.getDefaultFootprint() " + this.getDefaultFootprint());
// System.out.println("token.getWidth() " + token.getWidth());

Area visibleArea = new Area();
switch (shape) {
case CIRCLE:
case CIRCLE -> {
visionRange = (float) Math.sin(Math.toRadians(45)) * visionRange;
// visibleArea = new Area(new Ellipse2D.Double(-visionRange * 2, -visionRange, visionRange *
// 4, visionRange * 2));
visibleArea =
GraphicsUtil.createLineSegmentEllipse(
-visionRange * 2, -visionRange, visionRange * 2, visionRange, CIRCLE_SEGMENTS);
break;
case SQUARE:
int x[] = {0, (int) visionRange * 2, 0, (int) -visionRange * 2};
int y[] = {(int) -visionRange, 0, (int) visionRange, 0};
}
case SQUARE -> {
int[] x = {0, (int) visionRange * 2, 0, (int) -visionRange * 2};
int[] y = {(int) -visionRange, 0, (int) visionRange, 0};
visibleArea = new Area(new Polygon(x, y, 4));
break;
case BEAM:
}
case BEAM -> {
var pixelWidth = Math.max(2, width * getSize() / getZone().getUnitsPerCell());
Shape visibleShape = new Rectangle2D.Double(0, -pixelWidth / 2, visionRange, pixelWidth);

// new angle, corrected for isometric view
double theta = Math.toRadians(offsetAngle) + Math.toRadians(token.getFacing());
double theta = Math.toRadians(offsetAngle - tokenFacingAngle);
Point2D angleVector = new Point2D.Double(Math.cos(theta), Math.sin(theta));
AffineTransform at = new AffineTransform();
at.rotate(Math.PI / 4);
Expand All @@ -324,35 +320,25 @@ public Area getShapedArea(
new Area(
AffineTransform.getRotateInstance(theta + Math.toRadians(45))
.createTransformedShape(visibleShape));

break;
case CONE:
}
case CONE -> {
// Rotate the vision range by 45 degrees for isometric view
visionRange = (float) Math.sin(Math.toRadians(45)) * visionRange;
// Get the cone, use degreesFromIso to convert the facing from isometric to plan

// Area tempvisibleArea = new Area(new Arc2D.Double(-visionRange * 2, -visionRange,
// visionRange * 4, visionRange * 2, token.getFacing() - (arcAngle / 2.0) + (offsetAngle *
// 1.0), arcAngle,
// Arc2D.PIE));
Arc2D cone =
new Arc2D.Double(
-visionRange * 2,
-visionRange,
visionRange * 4,
visionRange * 2,
token.getFacing() - (arcAngle / 2.0) + (offsetAngle * 1.0),
(offsetAngle - tokenFacingAngle) - (arcAngle / 2.0),
arcAngle,
Arc2D.PIE);
GeneralPath path = new GeneralPath();
path.append(cone.getPathIterator(null, 1), false); // Flatten the cone to remove 'curves'
Area tempvisibleArea = new Area(path);

// Get the cell footprint
Rectangle footprint =
token.getFootprint(getZone().getGrid()).getBounds(getZone().getGrid());
footprint.x = -footprint.width / 2;
footprint.y = -footprint.height / 2;
// convert the cell footprint to an area
Area cellShape = createCellShape(footprint.height);
// convert the area to isometric view
Expand All @@ -362,14 +348,14 @@ public Area getShapedArea(
// join cell footprint and cone to create viewable area
visibleArea.add(cellShape);
visibleArea.add(tempvisibleArea);
break;
default:
// visibleArea = new Area(new Ellipse2D.Double(-visionRange, -visionRange, visionRange * 2,
// visionRange * 2));
}
default -> {
log.error("Unhandled shape {}; treating as a circle", shape);
visionRange = (float) Math.sin(Math.toRadians(45)) * visionRange;
visibleArea =
GraphicsUtil.createLineSegmentEllipse(
-visionRange, -visionRange, visionRange, visionRange, CIRCLE_SEGMENTS);
break;
-visionRange * 2, -visionRange, visionRange * 2, visionRange, CIRCLE_SEGMENTS);
}
}
return visibleArea;
}
Expand Down
4 changes: 3 additions & 1 deletion src/main/java/net/rptools/maptool/model/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,9 @@ public int getFacing() {

/**
* This returns the rotation of the facing of the token from the default facing of down or -90.
* Positive for CW and negative for CCW.
*
* <p>Positive for CW and negative for CCW. The range is currently from -270° (inclusive) to +90°
* (exclusive), but callers should not rely on this.
*
* @return angle in degrees
*/
Expand Down

0 comments on commit fe10790

Please sign in to comment.