Skip to content

Commit

Permalink
Merge pull request #4964 from kwvanderlinde/bugfix/3315-facing-uninit…
Browse files Browse the repository at this point in the history
…ialized-static-state

Fix NPE on partially initialized square grids
  • Loading branch information
cwisniew authored Oct 2, 2024
2 parents fbb27b1 + 399c740 commit 8c88df5
Show file tree
Hide file tree
Showing 35 changed files with 3,330 additions and 302 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,7 @@ public Object childEvaluate(
gridConfig.has("type")
? gridConfig.getAsJsonPrimitive("type").getAsString()
: AppPreferences.getDefaultGridType();
final var grid =
GridFactory.createGrid(
gridType, AppPreferences.getFaceEdge(), AppPreferences.getFaceVertex());
final var grid = GridFactory.createGrid(gridType);

final var gridColor =
gridConfig.has("color")
Expand Down
22 changes: 10 additions & 12 deletions src/main/java/net/rptools/maptool/client/tool/DefaultTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.util.Map;
import java.util.Set;
import javax.swing.*;
import net.rptools.maptool.client.AppPreferences;
import net.rptools.maptool.client.AppState;
import net.rptools.maptool.client.AppUtil;
import net.rptools.maptool.client.MapTool;
Expand All @@ -32,7 +33,6 @@
import net.rptools.maptool.model.Token;
import net.rptools.maptool.model.ViewMovementKey;
import net.rptools.maptool.model.Zone;
import net.rptools.maptool.util.TokenUtil;

/** */
public abstract class DefaultTool extends Tool
Expand Down Expand Up @@ -379,17 +379,15 @@ public void mouseWheelMoved(MouseWheelEvent e) {
facing += e.getWheelRotation() > 0 ? 5 : -5;
}
} else {
int[] facingArray = getZone().getGrid().getFacingAngles();
int facingIndex = TokenUtil.getIndexNearestTo(facingArray, facing);

facingIndex += e.getWheelRotation() > 0 ? 1 : -1;
if (facingIndex < 0) {
facingIndex = facingArray.length - 1;
}
if (facingIndex == facingArray.length) {
facingIndex = 0;
}
facing = facingArray[facingIndex];
facing =
renderer
.getZone()
.getGrid()
.nextFacing(
facing,
AppPreferences.getFaceEdge(),
AppPreferences.getFaceVertex(),
e.getWheelRotation() <= 0);
}

token.setFacing(facing);
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/net/rptools/maptool/client/tool/FacingTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import net.rptools.maptool.language.I18N;
import net.rptools.maptool.model.GUID;
import net.rptools.maptool.model.Token;
import net.rptools.maptool.util.TokenUtil;

/** */
public class FacingTool extends DefaultTool {
Expand Down Expand Up @@ -101,8 +100,11 @@ public void mouseMoved(MouseEvent e) {
int degrees = (int) Math.toDegrees(angle);

if (!SwingUtil.isControlDown(e)) {
int[] facingAngles = renderer.getZone().getGrid().getFacingAngles();
degrees = facingAngles[TokenUtil.getIndexNearestTo(facingAngles, degrees)];
degrees =
renderer
.getZone()
.getGrid()
.nearestFacing(degrees, AppPreferences.getFaceEdge(), AppPreferences.getFaceVertex());
}
Area visibleArea = null;
Set<GUID> remoteSelected = new HashSet<GUID>();
Expand Down
22 changes: 9 additions & 13 deletions src/main/java/net/rptools/maptool/client/tool/PointerTool.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@
import net.rptools.maptool.util.GraphicsUtil;
import net.rptools.maptool.util.ImageManager;
import net.rptools.maptool.util.StringUtil;
import net.rptools.maptool.util.TokenUtil;
import org.apache.commons.lang.StringUtils;

/**
Expand Down Expand Up @@ -1100,18 +1099,15 @@ private void handleKeyRotate(int direction, boolean freeRotate) {
if (freeRotate) {
facing += direction * 5;
} else {
int[] facingArray = renderer.getZone().getGrid().getFacingAngles();
int facingIndex = TokenUtil.getIndexNearestTo(facingArray, facing);

facingIndex += direction;

if (facingIndex < 0) {
facingIndex = facingArray.length - 1;
}
if (facingIndex == facingArray.length) {
facingIndex = 0;
}
facing = facingArray[facingIndex];
facing =
renderer
.getZone()
.getGrid()
.nextFacing(
facing,
AppPreferences.getFaceEdge(),
AppPreferences.getFaceVertex(),
direction < 0);
}
MapTool.serverCommand().updateTokenProperty(token, Token.Update.setFacing, facing);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -559,24 +559,16 @@ public int getZoneDistancePerCell() {
private Grid createZoneGrid() {
Grid grid = null;
if (getHexHorizontalRadio().isSelected()) {
grid =
GridFactory.createGrid(
GridFactory.HEX_HORI, AppPreferences.getFaceEdge(), AppPreferences.getFaceVertex());
grid = GridFactory.createGrid(GridFactory.HEX_HORI);
}
if (getHexVerticalRadio().isSelected()) {
grid =
GridFactory.createGrid(
GridFactory.HEX_VERT, AppPreferences.getFaceEdge(), AppPreferences.getFaceVertex());
grid = GridFactory.createGrid(GridFactory.HEX_VERT);
}
if (getSquareRadio().isSelected()) {
grid =
GridFactory.createGrid(
GridFactory.SQUARE, AppPreferences.getFaceEdge(), AppPreferences.getFaceVertex());
grid = GridFactory.createGrid(GridFactory.SQUARE);
}
if (getIsometricRadio().isSelected()) {
grid =
GridFactory.createGrid(
GridFactory.ISOMETRIC, AppPreferences.getFaceEdge(), AppPreferences.getFaceVertex());
grid = GridFactory.createGrid(GridFactory.ISOMETRIC);
}
if (getNoGridRadio().isSelected()) {
grid = GridFactory.createGrid(GridFactory.NONE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import java.nio.file.Path;
import java.text.ParseException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
Expand All @@ -55,7 +54,6 @@
import net.rptools.maptool.client.walker.WalkerMetric;
import net.rptools.maptool.events.MapToolEventBus;
import net.rptools.maptool.language.I18N;
import net.rptools.maptool.model.Grid;
import net.rptools.maptool.model.GridFactory;
import net.rptools.maptool.model.Token;
import net.rptools.maptool.model.Zone;
Expand Down Expand Up @@ -824,12 +822,10 @@ public PreferencesDialog() {
facingFaceEdges.addActionListener(
e -> {
AppPreferences.setFaceEdge(facingFaceEdges.isSelected());
updateFacings();
});
facingFaceVertices.addActionListener(
e -> {
AppPreferences.setFaceVertex(facingFaceVertices.isSelected());
updateFacings();
});

toolTipInlineRolls.addActionListener(
Expand Down Expand Up @@ -1526,24 +1522,6 @@ public void setVisible(boolean b) {
super.setVisible(b);
}

/**
* Used by the ActionListeners of the facing checkboxes to update the facings for all of the
* current zones. Redundant to go through all zones because all zones using the same grid type
* share facings but it doesn't hurt anything and avoids having to track what grid types are being
* used.
*/
private void updateFacings() {
// List<Zone> zlist = MapTool.getServer().getCampaign().getZones(); // generated NPE
// http://forums.rptools.net/viewtopic.php?f=3&t=17334
List<Zone> zlist = MapTool.getCampaign().getZones();
boolean faceEdges = AppPreferences.getFaceEdge();
boolean faceVertices = AppPreferences.getFaceVertex();
for (Zone z : zlist) {
Grid g = z.getGrid();
g.setFacings(faceEdges, faceVertices);
}
}

/**
* Initializes and sets the initial state of various user preferences in the application. This
* method is called during the initialization process.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ public enum Images {
GRID_BORDER_HEX,
GRID_BORDER_ISOMETRIC,
GRID_BORDER_SQUARE,
GRID_BORDER_SQUARE_RED,
HEROLABS_PORTRAIT,
HEROLABS_TOKEN,
LIGHT_SOURCE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ public class RessourceManager {
put(Images.GRID_BORDER_HEX, IMAGE_DIR + "hexBorder.png");
put(Images.GRID_BORDER_ISOMETRIC, IMAGE_DIR + "isoBorder.png");
put(Images.GRID_BORDER_SQUARE, IMAGE_DIR + "whiteBorder.png");
put(Images.GRID_BORDER_SQUARE_RED, IMAGE_DIR + "grid-square-red.png");
put(Images.HEROLABS_PORTRAIT, IMAGE_DIR + "powered_by_hero_lab_small.png");
put(Images.HEROLABS_TOKEN, IMAGE_DIR + "hero-lab-token.png");
put(Images.LIGHT_SOURCE, IMAGE_DIR + "lightbulb.png");
Expand Down
63 changes: 51 additions & 12 deletions src/main/java/net/rptools/maptool/model/Grid.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public abstract class Grid implements Cloneable {

private static final Dimension NO_DIM = new Dimension();
private static final DirectionCalculator calculator = new DirectionCalculator();
private static Map<Integer, Area> gridShapeCache = new ConcurrentHashMap<>();
private static final Map<Integer, Area> gridShapeCache = new ConcurrentHashMap<>();

protected transient Map<KeyStroke, Action> movementKeys = null;
private transient Zone zone;
Expand Down Expand Up @@ -112,21 +112,60 @@ public void drawCoordinatesOverlay(Graphics2D g, ZoneRenderer renderer) {
}

/**
* Set the facing options for tokens/objects on a grid. Each grid type can providing facings to
* the edges, the vertices, both, or neither.
* Get the next standard facing in the given direction.
*
* <p>If both are false, tokens on that grid will not be able to rotate with the mouse and
* keyboard controls for setting facing.
*
* @param faceEdges - Tokens can face edges.
* @param faceVertices - Tokens can face vertices.
* @param facing The current facing.
* @param faceEdges Whether to snap facing to edges.
* @param faceVertices
* @param clockwise
* @return
*/
public void setFacings(boolean faceEdges, boolean faceVertices) {
// Handle it in the individual grid types
public final int nextFacing(
int facing, boolean faceEdges, boolean faceVertices, boolean clockwise) {
// Work in range (0, 360] as it is easier for implementations.
// Will convert back to (-180,180] at the end.
facing = Math.floorMod(facing - 1, 360) + 1;

int nextFacing = snapFacingInternal(facing, faceEdges, faceVertices, clockwise ? -1 : 1);

return normalizeFacing(nextFacing);
}

public int[] getFacingAngles() {
return null;
public final int nearestFacing(int facing, boolean faceEdges, boolean faceVertices) {
// Work in range (0, 360] as it is easier for implementations.
// Will convert back to (-180,180] at the end.
facing = Math.floorMod(facing - 1, 360) + 1;

int nearestFacing = snapFacingInternal(facing, faceEdges, faceVertices, 0);

return normalizeFacing(nearestFacing);
}

/**
* Snaps a facing to the nearest edges or vertex, then optionally jumps to an adjacent one.
*
* @param facing The original facing. Must be set in the range 0 < facing <= 360.
* @param faceEdges If {@code true}, allow snapping the facing to the nearest edge.
* @param faceVertices If {@code true}, allow snapping the facing to the nearest vertex.
* @param addedSteps The number of edges or vertices to advance after snapping (depends on values
* of {@code faceEdges} and {@code faceVertices}.
* @return The snapped facing. Can be any integer.
*/
protected abstract int snapFacingInternal(
int facing, boolean faceEdges, boolean faceVertices, int addedSteps);

/**
* Return an equivalent facing in the range (-180, 180].
*
* @param facing
* @return
*/
private int normalizeFacing(int facing) {
facing = Math.floorMod(facing, 360);
if (facing > 180) {
facing -= 360;
}
return facing;
}

/**
Expand Down
12 changes: 4 additions & 8 deletions src/main/java/net/rptools/maptool/model/GridFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,17 @@ public class GridFactory {
public static final String NONE = "None";

public static Grid createGrid(String type) {
return createGrid(type, true, false);
}

public static Grid createGrid(String type, boolean faceEdges, boolean faceVertices) {
if (isHexVertical(type)) {
return new HexGridVertical(faceEdges, faceVertices);
return new HexGridVertical();
}
if (isHexHorizontal(type)) {
return new HexGridHorizontal(faceEdges, faceVertices);
return new HexGridHorizontal();
}
if (isSquare(type)) {
return new SquareGrid(faceEdges, faceVertices);
return new SquareGrid();
}
if (isIsometric(type)) {
return new IsometricGrid(faceEdges, faceVertices);
return new IsometricGrid();
}
if (isNone(type)) {
return new GridlessGrid();
Expand Down
19 changes: 15 additions & 4 deletions src/main/java/net/rptools/maptool/model/GridlessGrid.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,8 @@ public boolean isCoordinatesSupported() {
return false;
}
};
// @formatter:on

private static final int[] FACING_ANGLES = new int[] {-135, -90, -45, 0, 45, 90, 135, 180};
// @formatter:on

@Override
public List<TokenFootprint> getFootprints() {
Expand All @@ -74,8 +73,20 @@ public List<TokenFootprint> getFootprints() {
}

@Override
public int[] getFacingAngles() {
return FACING_ANGLES;
protected int snapFacingInternal(
int facing, boolean faceEdges, boolean faceVertices, int addedSteps) {
// Work in range (0, 360], it's easier. Will convert back to (-180,180] at the end.
facing = Math.floorMod(facing - 1, 360) + 1;

/* The number of degrees between each standard facing. */
int step = 45;
/* The position of the first standard facing CCW from zero. */
int base = 0;
/* A modification applied to facing to get the nearest answer, not a modulo/int div answer. */
int diff = (step - 1) / 2;

int stepsFromBase = Math.floorDiv(facing + diff - base, step) + addedSteps;
return stepsFromBase * step + base;
}

@Override
Expand Down
7 changes: 2 additions & 5 deletions src/main/java/net/rptools/maptool/model/HexGrid.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ public boolean isCoordinatesSupported() {
return false;
}
};
protected static BufferedImage pathHighlight = RessourceManager.getImage(Images.GRID_BORDER_HEX);
protected static final BufferedImage pathHighlight =
RessourceManager.getImage(Images.GRID_BORDER_HEX);

@Override
public Point2D.Double getCenterOffset() {
Expand Down Expand Up @@ -105,10 +106,6 @@ public Point2D.Double getCenterOffset() {
*/
protected transient double edgeLength;

public HexGrid() {
super();
}

@Override
public boolean isHex() {
return true;
Expand Down
Loading

0 comments on commit 8c88df5

Please sign in to comment.