From 092325d5a94d4b0bedcb51a5415254a83ba03d91 Mon Sep 17 00:00:00 2001 From: Baaaaaz <131240480+Baaaaaz@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:40:32 +0000 Subject: [PATCH 1/6] Fix comments, counter --- .../rptools/maptool/client/ui/zone/renderer/HaloRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java index c6d4141058..00da3e4d24 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java @@ -125,7 +125,7 @@ public void renderHalos( || (halo.isOwnerOnly() && AppUtil.playerOwns(token)) || (!halo.isOwnerOnly() && !halo.isGMOnly())) { - timer.increment("HaloRenderer-renderHalos:tokensWithRenderableHalos"); + timer.increment("HaloRenderer-renderHalos:renderableHalos"); double haloScaleFactor; if (halo.isScaleWithToken()) { @@ -233,7 +233,7 @@ public void renderHalos( } timer.stop("HaloRenderer-renderHalos:orderedRendering"); - // finally, render any legacy halo in the innermost position + // finally, render any legacy halo in the outermost position if (token.getHaloColor() != null) { timer.start("HaloRenderer-renderHalos:renderSimpleHalo"); DrawableColorPaint dcp = new DrawableColorPaint(token.getHaloColor()); From 004f1c40985d83e64b7ce7b9bd67ec7c331e1977 Mon Sep 17 00:00:00 2001 From: Baaaaaz <131240480+Baaaaaz@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:46:43 +0000 Subject: [PATCH 2/6] Reinstate correct render order for halos (reversed) and haloparts (normal), update comments --- .../client/ui/zone/renderer/HaloRenderer.java | 79 +++++++++++++------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java index 00da3e4d24..f9a095bda9 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java @@ -30,12 +30,16 @@ import net.rptools.maptool.model.*; import net.rptools.maptool.model.drawing.DrawableColorPaint; import net.rptools.maptool.model.zones.GridChanged; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class HaloRenderer { private final RenderHelper renderHelper; private final Campaign campaign; private final Zone zone; + private static final Logger log = LogManager.getLogger(HaloRenderer.class); + private final Map haloUnitShapeMap = new HashMap<>(); // region These fields need to be recalculated whenever the grid changes. @@ -107,8 +111,9 @@ public void renderHalos( timer.start("HaloRenderer-renderHalos:prepareToRender"); // Loop through the token's halos and first determine whether they actually need to be rendered. // If so, get the halos associated halo parts and establish where concentrically they need to be - // rendered and store them for later as we will render these halos in reverse order. - var renderableHaloParts = new ArrayList(); + // rendered and store them for later as we will render these halos in reverse order and their + // respective haloparts in order. + var renderableHalos = new ArrayList>(); if (!tokenHalos.isEmpty()) { // loop through the halos attached to the token for (GUID id : tokenHalos) { @@ -140,6 +145,7 @@ public void renderHalos( // loop through the individual halo parts in each halo var haloParts = halo.getHaloParts(); + var renderableHaloParts = new ArrayList(); for (HaloPart hp : haloParts) { /* @@ -189,6 +195,7 @@ public void renderHalos( maxHaloPartWidth, (int) Math.ceil((width * hp.getScaleY() + hp.getOffset()) * haloScaleFactor)); } + renderableHalos.add(renderableHaloParts); // add to the relevant max width accumulators if (maxHaloPartWidth != 0) { @@ -205,31 +212,34 @@ public void renderHalos( } timer.stop("HaloRenderer-renderHalos:prepareToRender"); - // Render the halos in reverse order, but their respective halos in order. This is so - // any filled outer-concentric halos do not graffiti over inner-concentric halos. + // Render the halos in reverse order but their respective haloparts in order. This is so + // any filled outer-concentric halos do not graffiti over inner-concentric halos and also + // haloparts are rendered in the order in which they are written in the halo syntax. timer.start("HaloRenderer-renderHalos:orderedRendering"); - for (var renderableHalo : renderableHaloParts.reversed()) { - var halo = renderableHalo.halo(); + for (var renderableHaloParts : renderableHalos.reversed()) { + for (var renderableHaloPart : renderableHaloParts) { + var halo = renderableHaloPart.halo(); + + int offsetConcentricByInner; + if (halo.isInner() || renderableInnerHaloMaxWidth <= 0) { + offsetConcentricByInner = 0; + } else { + offsetConcentricByInner = 2 * (renderableInnerHaloMaxWidth + haloLineWidthPreference); + } - int offsetConcentricByInner; - if (halo.isInner() || renderableInnerHaloMaxWidth <= 0) { - offsetConcentricByInner = 0; - } else { - offsetConcentricByInner = 2 * (renderableInnerHaloMaxWidth + haloLineWidthPreference); + renderHaloPart( + g2d, + token, + position, + grid, + renderableHaloPart.part(), + renderableHaloPart.shapeType(), + renderableHaloPart.lineWidth(), + renderableHaloPart.facingAngle(), + renderableHaloPart.scaleFactor(), + renderableHaloPart.concentricOffset() + offsetConcentricByInner, + halo.isFlipWithToken()); } - - renderHaloPart( - g2d, - token, - position, - grid, - renderableHalo.part(), - renderableHalo.shapeType(), - renderableHalo.lineWidth(), - renderableHalo.facingAngle(), - renderableHalo.scaleFactor(), - renderableHalo.concentricOffset() + offsetConcentricByInner, - halo.isFlipWithToken()); } timer.stop("HaloRenderer-renderHalos:orderedRendering"); @@ -293,6 +303,10 @@ public void renderHalos( * @param position the token's position * @param grid the map's grid * @param haloPart the haloPart itself + * @param haloShapeType the halo shapre type + * @param lineWidth the halo line width + * @param haloFacingAngle the halo facing angle + * @param haloScaleFactor the halo scale factor * @param concentricOffset the distance to offset the next haloPart * @param flipWithToken {@code true} if the halo should be flipped as the token is flipped. */ @@ -447,6 +461,19 @@ private AffineTransform getHaloGeometricTransform( return at; } + /** + * Build and apply all the transforms we need for basic shapes + * + * @param scaleX + * @param scaleY + * @param flipH + * @param flipV + * @param rotate + * @param translateX + * @param translateY + * @param rotateBeforeScale + * @return + */ private AffineTransform buildTransform( double scaleX, double scaleY, @@ -456,14 +483,13 @@ private AffineTransform buildTransform( double translateX, double translateY, boolean rotateBeforeScale) { - // build and apply all the transforms we need for basic shapes // (*) note that transforms are applied in the reverse order to which they are added below AffineTransform at = new AffineTransform(); // (*) the transform below this comment will be applied last if (translateX != 0 || translateY != 0) { at.translate(translateX, translateY); } - // for isometric rotate before(*) scale , otherwise scale before (*) rotate + // for isometric rotate before (*) scale , otherwise scale before (*) rotate if (rotateBeforeScale) { if (scaleX != 0 || scaleY != 0) { at.scale(scaleX, scaleY); @@ -800,6 +826,7 @@ private Shape getHaloFootprintShape(Token token, Grid grid, double concentricAdj * @param grid the grid * @param haloPart the haloPart * @param concentricAdjustment offset from the centre + * @param haloFacingAngle the halo facing angle * @return the grid shape, or a circle for gridless */ private Shape getHaloGridShape( From 03c493926f18ba109b101830f66627e96439bb84 Mon Sep 17 00:00:00 2001 From: Baaaaaz <131240480+Baaaaaz@users.noreply.github.com> Date: Sun, 22 Mar 2026 09:04:56 +0000 Subject: [PATCH 3/6] More comments and remove logger --- .../client/ui/zone/renderer/HaloRenderer.java | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java index f9a095bda9..178b2ae0f2 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java @@ -30,16 +30,12 @@ import net.rptools.maptool.model.*; import net.rptools.maptool.model.drawing.DrawableColorPaint; import net.rptools.maptool.model.zones.GridChanged; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; public class HaloRenderer { private final RenderHelper renderHelper; private final Campaign campaign; private final Zone zone; - private static final Logger log = LogManager.getLogger(HaloRenderer.class); - private final Map haloUnitShapeMap = new HashMap<>(); // region These fields need to be recalculated whenever the grid changes. @@ -464,15 +460,15 @@ private AffineTransform getHaloGeometricTransform( /** * Build and apply all the transforms we need for basic shapes * - * @param scaleX - * @param scaleY - * @param flipH - * @param flipV - * @param rotate - * @param translateX - * @param translateY - * @param rotateBeforeScale - * @return + * @param scaleX scale in the x dimension + * @param scaleY scale in the y dimension + * @param flipH flip horizontally + * @param flipV flip vertically + * @param rotate rotate + * @param translateX translate in the x dimension + * @param translateY translate in the x dimension + * @param rotateBeforeScale controls sequencing of scale and rotation (for isometric) + * @return the combined transforms */ private AffineTransform buildTransform( double scaleX, From 9a952f52210c0cfaab7a2f29368c217712b34d98 Mon Sep 17 00:00:00 2001 From: Baaaaaz <131240480+Baaaaaz@users.noreply.github.com> Date: Mon, 23 Mar 2026 05:09:58 +0000 Subject: [PATCH 4/6] Another comment tweak (primarily to enable rerunning Build Verification, as hoping prior MacOS issue was transient!) --- .../rptools/maptool/client/ui/zone/renderer/HaloRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java index 178b2ae0f2..5815411f80 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java @@ -106,7 +106,7 @@ public void renderHalos( timer.start("HaloRenderer-renderHalos:prepareToRender"); // Loop through the token's halos and first determine whether they actually need to be rendered. - // If so, get the halos associated halo parts and establish where concentrically they need to be + // If so, get the halo's associated haloparts and establish where concentrically they need to be // rendered and store them for later as we will render these halos in reverse order and their // respective haloparts in order. var renderableHalos = new ArrayList>(); From 9fd14612a17ec648ff34850e07d86b7c286f2017 Mon Sep 17 00:00:00 2001 From: Baaaaaz <131240480+Baaaaaz@users.noreply.github.com> Date: Wed, 25 Mar 2026 21:11:25 +0000 Subject: [PATCH 5/6] Resolve flipH and flipV not flipping halos due to XOR precedence --- .../rptools/maptool/client/ui/zone/renderer/HaloRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java index 5815411f80..993742669f 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java @@ -425,8 +425,8 @@ private AffineTransform getHaloGeometricTransform( double translateX = position.transformedBounds().getBounds2D().getCenterX(); double translateY = position.transformedBounds().getBounds2D().getCenterY(); - boolean flipH = flipWithToken && position.token().isFlippedX() ^ haloPart.getFlipHorizontal(); - boolean flipV = flipWithToken && position.token().isFlippedY() ^ haloPart.getFlipVertical(); + boolean flipH = (flipWithToken && position.token().isFlippedX()) ^ haloPart.getFlipHorizontal(); + boolean flipV = (flipWithToken && position.token().isFlippedY()) ^ haloPart.getFlipVertical(); double rotate; boolean rotateBeforeScale = false; From 7f29a336311769c7f04bd687fd6cd5cfe4da7d93 Mon Sep 17 00:00:00 2001 From: Baaaaaz <131240480+Baaaaaz@users.noreply.github.com> Date: Fri, 3 Apr 2026 05:51:44 +0100 Subject: [PATCH 6/6] resolve feedback and correct some more typos --- .../client/ui/zone/renderer/HaloRenderer.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java index 993742669f..37d6f83132 100644 --- a/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java +++ b/src/main/java/net/rptools/maptool/client/ui/zone/renderer/HaloRenderer.java @@ -106,9 +106,10 @@ public void renderHalos( timer.start("HaloRenderer-renderHalos:prepareToRender"); // Loop through the token's halos and first determine whether they actually need to be rendered. - // If so, get the halo's associated haloparts and establish where concentrically they need to be + // If so, get the halo's associated halo parts and establish where concentrically they need to + // be // rendered and store them for later as we will render these halos in reverse order and their - // respective haloparts in order. + // respective halo parts in order. var renderableHalos = new ArrayList>(); if (!tokenHalos.isEmpty()) { // loop through the halos attached to the token @@ -208,9 +209,9 @@ public void renderHalos( } timer.stop("HaloRenderer-renderHalos:prepareToRender"); - // Render the halos in reverse order but their respective haloparts in order. This is so + // Render the halos in reverse order but their respective halo parts in order. This is so // any filled outer-concentric halos do not graffiti over inner-concentric halos and also - // haloparts are rendered in the order in which they are written in the halo syntax. + // halo parts are rendered in the order in which they are written in the halo syntax. timer.start("HaloRenderer-renderHalos:orderedRendering"); for (var renderableHaloParts : renderableHalos.reversed()) { for (var renderableHaloPart : renderableHaloParts) { @@ -299,7 +300,7 @@ public void renderHalos( * @param position the token's position * @param grid the map's grid * @param haloPart the haloPart itself - * @param haloShapeType the halo shapre type + * @param haloShapeType the halo shape type * @param lineWidth the halo line width * @param haloFacingAngle the halo facing angle * @param haloScaleFactor the halo scale factor @@ -528,11 +529,11 @@ private Shape getUnitPolygonShape(boolean isStar, Integer vertices) { var timer = CodeTimer.get(); timer.start("HaloRenderer-getUnitPolygonShape"); - timer.increment("HaloRenderer-getUnitPolygonShape:computeIfAbsent"); - double inRadius = 1 / 2d; + timer.increment("HaloRenderer-getUnitPolygonShape"); + double inradius = 1 / 2d; // a circumscribed circle passes through all polygon outer-vertices - double circumRadius = inRadius / Math.cos(Math.PI / vertices); - double radius = circumRadius; + double circumradius = inradius / Math.cos(Math.PI / vertices); + double radius = circumradius; double orientationAngle = 0d; // orientate regular polygons to have a flat top, otherwise a pointy top for stars @@ -574,7 +575,7 @@ private Shape getUnitPolygonShape(boolean isStar, Integer vertices) { } /** - * Get a scaled and positioned haloPart minishape, composed of a number of miniature shapes + * Get a scaled and positioned haloPart mini-shape, composed of a number of miniature shapes * rotated and spread equidistant around a circle. * * @return the polygon shape @@ -885,7 +886,7 @@ private Shape getHaloGridShape( * @return Area as a Shape */ private Shape getHaloOutlineShape(Token token) { - // no concentricAdjustment for HaloPart Topolopy shapes currently... + // no concentricAdjustment for HaloPart Outline shapes currently... var timer = CodeTimer.get(); timer.start("HaloRenderer-getHaloOutlineShape");