Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Token class updates to accommodate new layout features #5104

Open
wants to merge 31 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bb42cc4
Token class:
bubblobill Dec 11, 2024
debcb28
Merge remote-tracking branch 'origin/TokenLayout' into TokenLayout
bubblobill Dec 11, 2024
89c1411
Updated TokenDto to handle refactored property names and new property…
bubblobill Dec 11, 2024
3da8efc
Updated Token again due to mysterious update overwriting it
bubblobill Dec 11, 2024
bd46d06
Many things coming together
bubblobill Dec 11, 2024
a721eb7
Merge remote-tracking branch 'origin/TokenLayout' into TokenLayout
bubblobill Dec 11, 2024
1406973
Forgot I hadn't finished hexes
bubblobill Dec 11, 2024
aedb060
Changed all comments to block comments to make it easier to find comm…
bubblobill Dec 11, 2024
817739e
Corrected squares in createGridShape.
bubblobill Dec 11, 2024
c7a852d
Added image rotation property to layoutProps functions
bubblobill Dec 11, 2024
cb3b1c2
Sanity saving string concatenation
bubblobill Dec 11, 2024
fffb520
Spotless indent
bubblobill Dec 11, 2024
1741bb7
Code cleanup
bubblobill Dec 11, 2024
950b5a4
Code cleanup
bubblobill Dec 13, 2024
0786ea0
Refactoring
bubblobill Dec 13, 2024
789928e
Refactoring
bubblobill Dec 13, 2024
0cb23f9
Refactoring
bubblobill Dec 13, 2024
408ef52
Refactoring
bubblobill Dec 13, 2024
9e46740
Refactoring
bubblobill Dec 13, 2024
401ddc0
Facing-arrow painting moved to FacingArrowRenderer
bubblobill Dec 13, 2024
2017845
Code cleanup
bubblobill Dec 13, 2024
2b198d6
Code cleanup
bubblobill Dec 14, 2024
36146cd
Spotless exception removed
bubblobill Dec 14, 2024
d4e5ed9
Merge branch 'develop' into TokenLayout
bubblobill Dec 15, 2024
e419c9e
Merge branch 'develop' into TokenLayout
bubblobill Dec 16, 2024
cd56de7
Fixed getScaledTokenImage returning incorrect scaling for free-size i…
bubblobill Dec 16, 2024
0b9af49
cleanup and spotless
bubblobill Dec 16, 2024
0535e1d
cleanup and spotless
bubblobill Dec 16, 2024
536ae41
Merge branch 'develop' into TokenLayout
bubblobill Dec 16, 2024
4f6d4f4
Merge branch 'RPTools:develop' into TokenLayout
bubblobill Dec 29, 2024
55ef9fd
Merge branch 'RPTools:develop' into TokenLayout
bubblobill Jan 28, 2025
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
150 changes: 150 additions & 0 deletions src/main/java/net/rptools/lib/MathUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
* This software Copyright by the RPTools.net development team, and
* licensed under the Affero GPL Version 3 or, at your option, any later
* version.
*
* MapTool Source Code is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* You should have received a copy of the GNU Affero General Public
* License * along with this source Code. If not, please visit
* <http://www.gnu.org/licenses/> and specifically the Affero license
* text at <http://www.gnu.org/licenses/agpl.html>.
*/
package net.rptools.lib;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/* Utility class for useful mathematical methods */
public class MathUtil {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do without most of this class as the methods are either unused or can be simplified. I'll leave specific comments below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I originally created it to house mathematical methods that I found lurking in other classes that I could never find when I wanted them. Then I gave up trying to find existing methods and just started adding methods as they became useful. Some of which I stopped using as my code developed, as you discovered.

private static final Logger log = LogManager.getLogger(MathUtil.class);

/**
* Faster version of absolute for integers
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any proof of this claim? Math.abs() calls are always inlined with platform-specific instructions, so there's no branching concerns with it.

Anyways, this method isn't used and can be removed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes there is evidence, probably buried in StackExchange. Although it is probably easier to just use FastMath, and as you noticed, my need for it disappeared.

*
* @param val
* @return absolute value of val
*/
public static int abs(int val) {
return (val >> 31 ^ val) - (val >> 31);
}

/**
* Faster version of absolute
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guarantee this is slower than Math.abs(). Plus this method isn't used.

*
* @param val
* @return absolute value of val
*/
public static <T extends Number> T abs(T val) {
return (T) (val.floatValue() < 0 ? -1 * val.doubleValue() : val);
}

/**
* Returns a truncated double with the specified number of decimal places
*
* @param value to be truncated
* @param decimalPlaces number of decimal places to use
* @return truncated double value
*/
public static double doublePrecision(double value, int decimalPlaces) {
double d = Double.parseDouble(String.format("%." + decimalPlaces + "f", value));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for the UI usage, but outside of that will be a pretty slow implementation. Can use this instead:

double divisor = Math.pow(10, -decimalPlaces);
double d = divisor * Math.round(value / divisor);

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where were you when I was researching for a fast and reliable Math.trunc?

log.debug("value: " + value + ", decimalPlaces: " + decimalPlaces + " -> " + d);
return d;
}

public static boolean isDouble(Object o) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the other proposed simplifications, isDouble(), isFloat() and isNumber() are not needed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spoilsport

return o.getClass().isAssignableFrom(Double.class);
}

public static boolean isFloat(Object o) {
return o.getClass().isAssignableFrom(Float.class);
}

public static boolean isInt(Object o) {
return o.getClass().isAssignableFrom(Integer.class);
}

public static boolean isNumber(Object o) {
return o.getClass().isAssignableFrom(Number.class);
}

/**
* Checks that a value lies within a specified tolerance. Useful for checking if a value is "close
* enough"
*
* @param checkValue to be checked
* @param referenceValue to be checked against
* @param tolerance variance allowed
* @return true if the value is within ± tolerance
*/
public static boolean inTolerance(double checkValue, double referenceValue, double tolerance) {
return checkValue <= referenceValue + tolerance && checkValue >= referenceValue - tolerance;
}

/**
* Uses Generics Maps a value in one range to its equivalent in a second range
*
* @param valueToMap value in the first range that needs to be converted
* @param in_min the minimum value for the original range
* @param in_max the maximum value for the original range
* @param out_min the minimum value for the target range
* @param out_max the maximum value for the target range
* @return the equivalent value of valueToMap in the target range
* @param <T>
*/
public static <T extends Number> T mapToRange(
Copy link
Collaborator

@kwvanderlinde kwvanderlinde Mar 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generics don't really gain us anything here since internally it has to check float vs double anyways. I.e., an overload would do just as well.

But it can also just be replaced with a purely double version. The one caller that uses float can cast the result.

Copy link
Collaborator Author

@bubblobill bubblobill Mar 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<whine>But I don't like casting and like generics... </whine>

T valueToMap, T in_min, T in_max, T out_min, T out_max) {
Number mapValue = (Number) valueToMap;
Number inMin = (Number) in_min;
Number inMax = (Number) in_max;
Number outMin = (Number) out_min;
Number outMax = (Number) out_max;
Number result;
if (isFloat(valueToMap)) {
result =
(Number)
((mapValue.floatValue() - inMin.floatValue())
* (outMax.floatValue() - outMin.floatValue())
/ (inMax.floatValue() - inMin.floatValue())
+ outMin.floatValue());
} else {
result =
(Number)
((mapValue.doubleValue() - inMin.doubleValue())
* (outMax.doubleValue() - outMin.doubleValue())
/ (inMax.doubleValue() - inMin.doubleValue())
+ outMin.doubleValue());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This kind of lerp implementation tends to be inaccurate when valueToMap == in_max, due to floating point shenanigans. An alternative with accurate endpoints would be:

var t = (valueToMap - in_min) / (in_max - in_min);
result = (1 - t) * out_min + t * out_max;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good thinking 99

}
return (T) result;
}

/**
* Constrains an integer between an upper and lower limit
*
* @param value
* @param lowBound
* @param highBound
* @return
*/
public static int constrainInt(int value, int lowBound, int highBound) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is the same as Math.clamp(long, int, int) and can be removed in favour of that.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wilco

return Math.max(lowBound, Math.min(highBound, value));
}

/**
* Constrains a Number between an upper and lower limit
*
* @param value
* @param lowBound
* @param highBound
* @return
* @param <T>
*/
public static <T extends Number> T constrainNumber(T value, T lowBound, T highBound) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is equivalent to Math.clamp(double, double, double) and can be removed.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wilco

return (T)
(Number)
Math.max(
lowBound.doubleValue(), Math.min(highBound.doubleValue(), value.doubleValue()));
}
}
Loading
Loading