HSL to RGB converter issue - java

This converter assumes H, S, and L are contained in the set [0, 1].
But I want H to be between [0, 359] (0 and 359 included) and S & L between [0, 100] (0 and 100 included) and I don't know how to do it (in Java as well).
I took this converter from here.
public class testdos {
public static void main(String[] args) {
// hslToRgb([0, 359], [0, 100], [0, 100]);
}
public static int[] hslToRgb(float h, float s, float l) {
// i added this sysout line
System.out.println("HSL(" + h + "," + s + "," + l + ")");
float r, g, b;
if (s == 0f) {
r = g = b = l; // achromatic
} else {
float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
float p = 2 * l - q;
r = hueToRgb(p, q, h + 1f / 3f);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1f / 3f);
}
int[] rgb = { to255(r), to255(g), to255(b) };
// and i added this to print it, don't know if its the best way to do it
System.out.print("RGB(");
for (int i = 0; i < rgb.length; i++) {
if ((i + 1) != rgb.length) {
System.out.print(rgb[i] + ",");
}
else {
System.out.print(rgb[i]);
}
}
System.out.println(")");
return rgb;
}
public static int to255(float v) {
return (int) Math.min(255, 256 * v);
}
/** Helper method that converts hue to rgb */
public static float hueToRgb(float p, float q, float t) {
if (t < 0f)
t += 1f;
if (t > 1f)
t -= 1f;
if (t < 1f / 6f)
return p + (q - p) * 6f * t;
if (t < 1f / 2f)
return q;
if (t < 2f / 3f)
return p + (q - p) * (2f / 3f - t) * 6f;
return p;
}
}

The hslToRgb() method you provided accepts three parameters of float data type. The value range that must be provided to all of these parameters is: 0.0f to 1.0f:
int[] hslToRgb(float h, float s, float l) { ... }
Instead you want to supply a specific range values to these parameters which in turn makes the method much easier to utilize in layman's terms with respect to what needs to actually be supplied.
H (0° to 360° as in Degrees)
S (0% to 100% as in Percent)
L (0% to 100% as in Percent)
It's all a matter of doing a little division. This can be done as the parameters are supplied to the method or built into the method which appears to be what you want. The latter is somewhat more advantageous since you don't need to remember the formula (regardless of how simple it is) to carry out those divisions when applying the arguments to the hslToRgb() method.
Doing the math before passing arguments:
// Hue is: 65° Saturation is: 36% Lightness is: 87%
float hue = (float) 65/360;
float saturation = (float) 36/100;
float lightness = (float) 87/100;
int[] rgb = hslToRgb(hue, saturation, lightness);
Doing the math when passing arguments:
// Hue is: 65° Saturation is: 36% Lightness is: 87%
int[] rgb = hslToRgb((float)65/360, (float)36/100, (float)87/100);
Who cares! Let the method figure it out:
// Hue is: 65° Saturation is: 36% Lightness is: 87%
int[] rgb = hslToRgb(65, 36, 87);
This last example requires a little bit of code to be added to the hslToRgb() method. It's nothing major but before we get into that you need to be sure to realize that for color accuracy, Hue, Saturation, and Lightness arguments can be given respectively in floating point degree and floating point percentage values. In other words, supplying arguments such as:
int[] rgb = hslToRgb(65, 36, 87);
AND
int[] rgb = hslToRgb(65.8, 36.2, 87.5);
are to be considered valid calls with valid arguments. With the slight code addition to the hslToRgb() method, either or can be supplied, for example:
int[] rgb = hslToRgb(0.1806f, 36, 87.5);
This is a valid call with valid arguments and will produce the very same RGB values as the other above examples.
Modifying the hslToRgb() method:
Simply add these three lines of code to the top of the hslToRGB() method:
public static int[] hslToRGB(float h, float s, float l) {
if (h > 1.0f) { h = (h < 0 ? 0 : h > 360 ? 360 : h) / 360; }
if (s > 1.0f) { s = (s < 0 ? 0 : s > 100 ? 100 : s) / 100; }
if (l > 1.0f) { l = (l < 0 ? 0 : l > 100 ? 100 : l) / 100; }
// .... the rest of method code here ....
}
What are these three lines of code doing?
With these lines of code the Hue (h) argument can be supplied as a set [0.0 to 1.0] or a set [0.0 to 360.0] or a set [0 to 360]. The Saturation (s) and Lightness (l) arguments can be supplied as a set [0.0 to 1.0] or a set [0.0 to 100.0] or a set [0 to 100]. If any argument supplied is less than its minimum range (0) then it is defaulted to that arguments specific minimum which for all arguments would be 0. On the same hand, if any argument supplied is greater than its maximum range then it is defaulted to that arguments specific maximum.
Since all three lines of code are basically doing the same thing for each specific argument, we'll explain the first line:
if (h > 1.0f) { h = (h < 0 ? 0 : h > 360 ? 360 : h) / 360; }
The condition for the if statement (h > 1.0f) checks to see if the supplied Hue (h) argument is within the set of [0.0 to 1.0]. If the argument supplied is greater than 1.0 then it must be supplied as a literal degree value and the if statement condition is true therefore running the code contained within this statement's code block which consists of nested Ternary Operator statements. If the if statement condition is false then the supplied value is merely used. Ultimately, if the condition is true then we want to take the supplied argument and divide it by 360 but first we use the nested Ternary Operator statements to ensure the argument supplied is within valid range (0 to 360):
h < 0 ? 0 :
If the supplied argument (h) is less than 0 (such as an arbitrary value of -22 or -0.202) then the h is set to hold a default of 0 otherwise we check to see if the argument value is greater then 360:
: h > 360 ? 360 :
If the supplied argument (h) is greater than 360 (such as an arbitrary value of 365 or 378.8) then the h is set to hold a default of 360 otherwise it is deemed that the argument supplied is indeed within the proper range and we'll use it therefore dividing the supplied value by 360;
: h) / 360
How you might use this method:
int[] rgb = hslToRGB(65, 36, 87);
System.out.println("RGB is: " + java.util.Arrays.toString(rgb));
String hex = "#" + Integer.toHexString(new Color(rgb[0], rgb[1], rgb[2]).getRGB() & 0x00ffffff);
System.out.println("In Hexadecimal: " + hex.toUpperCase());
Console Output:
RGB is: [232, 234, 210]
In Hexadecimal: #E8EAD2

You divide by the h, s, and l range to get the numbers between 0 and 1. You also start class names with an upper-case character.
Edited to add: Why do I have to start class names with an upper-case character?
Because it's the Java convention, and so you can write code like:
Testdoc testdoc = new TestDoc();
and visually see the difference between the Testdoc class name and the testdoc field name.
Here's the revised code.
public class Testdos {
public static void main(String[] args) {
// hslToRgb([0, 359], [0, 100], [0, 100]);
}
/**
* Converts an HSL color value to RGB. Conversion formula adapted from
* http://en.wikipedia.org/wiki/HSL_color_space. Assumes h, s, and l are
* contained in the set [0, 1] and returns r, g, and b in the set [0, 255].
*
* #param h The hue
* #param s The saturation
* #param l The lightness
* #return int array, the RGB representation
*/
public static int[] hslToRgb(float h, float s, float l) {
// i added this sysout line
System.out.println("HSL(" + h + "," + s + "," + l + ")");
h /= 359.0;
s /= 100.0;
l /= 100.0;
float r, g, b;
if (s == 0f) {
r = g = b = l; // achromatic
} else {
float q = l < 0.5f ? l * (1 + s) : l + s - l * s;
float p = 2 * l - q;
r = hueToRgb(p, q, h + 1f / 3f);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1f / 3f);
}
int[] rgb = { to255(r), to255(g), to255(b) };
// and i added this to print it, don't know if its the best way to do it
System.out.print("RGB(");
for (int i = 0; i < rgb.length; i++) {
if ((i + 1) != rgb.length) {
System.out.print(rgb[i] + ",");
} else {
System.out.print(rgb[i]);
}
}
System.out.println(")");
return rgb;
}
public static int to255(float v) {
return (int) Math.min(255, 256 * v);
}
/** Helper method that converts hue to rgb */
public static float hueToRgb(float p, float q, float t) {
if (t < 0f)
t += 1f;
if (t > 1f)
t -= 1f;
if (t < 1f / 6f)
return p + (q - p) * 6f * t;
if (t < 1f / 2f)
return q;
if (t < 2f / 3f)
return p + (q - p) * (2f / 3f - t) * 6f;
return p;
}
}

Related

converting python to java float vs double

I need to convert the following python code to Java and remember from the past that handling decimal values must be done carefully otherwise the results will not be accurate.
My question is, can I use doubles for all non-interger values given doubles are more accurate that floats?
Here is my start of the conversion:
Python code
def degrees(rads):
return (rads/pi)*180
def radians(degrees):
return (degrees/180 * pi)
def fnday(y, m, d, h):
a = 367 * y - 7 * (y + (m + 9) // 12) // 4 + 275 * m // 9
a += d - 730530 + h / 24
return a
Java conversion
public double degress(double rads)
{
return (rads/PI)*180;
}
public double radians(double degrees)
{
return (degrees/180 * PI);
}
public double fnday(int y, int m, int d, int h)
{
double a = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9;
a += d - 730530 + h / 24;
return a;
}
I know it may be a simple answer but I need to know the postion of the moon and sun for the app and do not want to rely on an api for this data. I simple want to put in the latitude and longitdue and get the sun and moon rise and set times.
Using a double for each variable would suffice; however, you have an issue that results from integer division:
double a = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9;
If I were you, I'd change y, m, d, and h to all be doubles, so you retain the decimal places when dividing:
public double fnday(double y, double m, double d, double h) {
double a = 367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9;
a += d - 730530 + h / 24;
return a;
}
If you need a really big precision, the best way is use,
java.lang.BigDecimal, that extends from java.math.Number.
You can even use your existing doubles if you need:
double d = 67.67;
BigDecimal bd = new BigDecimal(d);
But you will need to use the methods from the class like this:
public BigDecimal degress(BigDecimal rads)
{
BigDecimal pi = new BigDecimal(PI);
return (rads.divide(pi))*180;
}

HSL to RGB Converter Not Working

I've been making an image editor in my free time and tried to use the HSL to RGB conversion formula here: HSL to RGB color conversion however I have come across an error.
Whenever I got to run my program the conversion does not work as it should, leaving the rgb with an unexpected grey colour. Here's some sample output data:
HSL: 0.0, 0.0, 1.0
RGB: 255.0, 255.0, 255.0
HSL: 214.0, 0.73, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.74, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.75, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.76, 0.5
RGB: 128.0, 128.0, 128.0
HSL: 214.0, 0.76, 0.5
RGB: 128.0, 128.0, 128.0
And below is my code. It's all written in Java.
public double[] hslToRgb(double h, double s, double l){
System.out.println("HSL: " + h + ", " + s + ", " + l);
double r = -1;
double b = -1;
double g = -1;
if(s == 0){
r = l;
b = l;
g = l;
}else{
double q = 1 < 0.5 ? l * (1 + s) : l + s - 1 * s;
double p = 2 * l - q;
r = hueToRgb(p, q, h + (1 / 3));
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - (1/ 3));
}
r = Math.round(r * 255);
b = Math.round(b * 255);
g = Math.round(g * 255);
System.out.println("RGB: " + r + ", " + g + ", " + b);
double[] rgb = {r, g, b};
return rgb;
}
private double hueToRgb(double p, double q, double t){
if(t < 0){
t++;
}
if(t > 1){
t--;
}
if(t < 1 / 6){
return p + (q - p) * 6 * t;
}
if(t < 1 / 2){
return q;
}
if(t < 2 / 3){
return p + (q - p) * ((2 / 3) - t) * 6;
}
return p;
}
Can anybody give me some insight as to what might be causing this? I feel like it's a minor error in my code but I can't find it anywhere.
Firstly, in the line double q = 1 < 0.5 ..., it's supposed to be the letter EL l, not the number 1 in two places.
double q = 1 < 0.5 ? l * (1 + s) : l + s - 1 * s;
^ ----- change from 1 to l ---- ^
Secondly, you have to be very careful about arithmetic which works differently in Java than JavaScript. When you have code like 1 / 3, since both are integers, the result will be an integer which means you get 0 rather than 0.3333.... In order to prevent using integer division, all of your divisions need to use at least one floating-point type:
if(t < 1.0 / 6){
^^^ change from 1 to 1.0
return p + (q - p) * 6 * t;
}
if(t < 1.0 / 2){
^^^ change from 1 to 1.0
return q;
}
if(t < 2.0 / 3){
^^^ change from 2 to 2.0
return p + (q - p) * ((2.0 / 3) - t) * 6;
^^^ change from 2 to 2.0
}
Also:
r = hueToRgb(p, q, h + (1.0 / 3));
^^^ change from 1 to 1.0
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - (1.0 / 3));
^^^ change from 1 to 1.0
Finally, in the code you copied from, the values for h, s, and l are all supposed to be between 0 and 1. So an H of 214 is not valid in your examples - you need to divide it by 360 first.
Here is a working code sample with the changes applied: http://ideone.com/QbXyYi
Input: 240.0 / 360.0, 1.0, 0.5
Output: [0, 0, 255.0]

Trying to shoot a bullet in a 3D world. Experiencing an odd offset in x and z

Alright, so I am using Bukkit API (Minecraft), which should not be too much of a problem in this as it is used minimally. So a Location contains world, x, y, z, yaw and pitch. This may come in handy, but I doubt it.
My problem is that I go to shoot using the Shot class (below), and there appears to be a +-5 difference in either one when approximately 3 blocks away, and the HitBox is instantiated about 5 blocks away (this could be the issue (move/rotate methods)). I have tried working this out on paper, and have used half a notepad doing so, but I am not yet able to figure out a solution. What I need is someone who understands trigonometry and java well, so they can help out.
Other information that may be of use:
+z is 0 degrees yaw, -x is 90 degrees, -z is 180, and +x is 270.
The variables seem to be incorrect sporadically, and under certain circumstances they work correctly and constitute a hit.
The Location (from) parameter in the Shot constructor is of the player's location in the world, therefore from is not (0, 0, 0).
ShotData should not affect any values, as wind speed and wind direction in my case are 0 (if there is a problem with the math involving this though, feel free to let me know, haha)
Pitch appears to be fine, although +y is -90, and -y is 90 (weird right?)
So my question is... Where is the problem, and how do I fix it? I am sorry that it is such a general question and that it is a very common one, but this is one of those times when it is truly necessary. I tried to remove all unnecessary code, but you can remove more if needed. Also, if you want to see anything else that may be referenced in here, I can get that for you.
Shot.java:
private final Location from;
private ShotData data;
public Shot(Location from, ShotData data) {
this.from = from;
this.data = data;
}
// TODO - Checking for obstacles
public List<Hit> shoot(List<HitBox> hitBoxes) {
List<Hit> hits = new ArrayList<Hit>();
for (HitBox hitBox : hitBoxes) {
hitBox.update();
float fromYaw = from.getYaw() % 360;
float fromPitch = from.getPitch() % 360;
// making sure the center location is within range
if (hitBox.getCenter().distanceSquared(from) > Math.pow(data.getDistanceToTravel(), 2)) {
continue;
}
/* TODO Only allow hits on parts of the rectangle that are within range,
* not just the whole thing if the center is within range. */
// accounting for wind speed/direction
float windCompassDirection = data.getWindCompassDirection(from.getWorld());
float windSpeed = data.getWindSpeedMPH(from.getWorld());
fromYaw += (windCompassDirection > fromYaw ? 1 : windCompassDirection < fromYaw ? -1 : 0) * windSpeed;
fromYaw %= 360;
int[] orderClockwise = new int[] {0, 1, 4, 3};
Location thisSideCorner = hitBox.getCorner(0);
Location oppositeSideCorner = hitBox.getCorner(0);
for (int i = 0; i < orderClockwise.length; i++) {
int num = orderClockwise[i];
Location corner = hitBox.getCorner(num);
Location clockwise = hitBox.getCorner(orderClockwise[(i + 1) % 3]);
if ((Math.atan2(from.getZ() - corner.getZ(), from.getX() - corner.getX()) * 180 / Math.PI) > 0 && corner.distanceSquared(from) < clockwise.distanceSquared(from)) {
thisSideCorner = corner;
int exitCornerClockwiseAmount = (Math.atan2(from.getZ() - clockwise.getZ(), from.getX() - clockwise.getX()) * 180 / Math.PI) < 0 ? 2 : 3;
oppositeSideCorner = hitBox.getCorner((i + exitCornerClockwiseAmount) % 3);
}
}
Location entrance = getProjectileLocation(thisSideCorner, data, hitBox, fromYaw, fromPitch);
double distance = entrance.distance(from);
double deltaX = data.getDeltaX(distance, fromYaw);
double deltaY = data.getDeltaY(distance, fromPitch);
double deltaZ = data.getDeltaZ(distance, fromYaw);
entrance.add(deltaX, deltaY, deltaZ);
Location exit = getProjectileLocation(oppositeSideCorner, data, hitBox, deltaX, deltaY, deltaZ, fromYaw, fromPitch);
// hit detection and reaction
boolean hitX = entrance.getX() <= hitBox.getHighestX() && entrance.getX() >= hitBox.getLowestX();
boolean hitY = entrance.getY() <= hitBox.getHighestY() && entrance.getY() >= hitBox.getLowestY();
boolean hitZ = entrance.getZ() <= hitBox.getHighestZ() && entrance.getZ() >= hitBox.getLowestZ();
if (hitX && hitY && hitZ) {
hits.add(new Hit(from, entrance, exit, hitBox, data));
}
}
return hits;
}
private Location getProjectileLocation(Location thisSideCorner, ShotData data, HitBox hitBox, float fromYaw, float fromPitch) {
return getProjectileLocation(thisSideCorner, data, hitBox, 0, 0, 0, fromYaw, fromPitch);
}
private Location getProjectileLocation(Location thisSideCorner, ShotData data, HitBox hitBox, double addX, double addY, double addZ, float fromYaw, float fromPitch) {
double deltaFromToSideCornerX = thisSideCorner.getX() - from.getX();
double deltaFromToSideCornerY = thisSideCorner.getY() - from.getY();
double deltaFromToSideCornerZ = thisSideCorner.getZ() - from.getZ();
double xzDistFromSideCorner = Math.sqrt(Math.pow(deltaFromToSideCornerX, 2) + Math.pow(deltaFromToSideCornerZ, 2));
double yawToSideCorner = Math.atan2(deltaFromToSideCornerX, deltaFromToSideCornerZ) * 180 / Math.PI;// flipped x and z from normal
double theta1 = yawToSideCorner - fromYaw;
double theta2 = yawToSideCorner - theta1;
double outerAngle = 180 - yawToSideCorner - 90;// previously theta1
double outerAngleInShotCone = outerAngle + 90 + hitBox.getYawRotation();
double lastAngleInShotCone = 180 - theta1 - outerAngleInShotCone;
double xzDistanceFromHit = (xzDistFromSideCorner * Math.sin(Math.toRadians(outerAngleInShotCone))) / Math.sin(Math.toRadians(lastAngleInShotCone));
double deltaX = xzDistanceFromHit * Math.sin(Math.toRadians(theta2));// leaves out sin 90 because its just equal to 1...
double deltaZ = xzDistanceFromHit * Math.sin(Math.toRadians(90 - theta2));// leaves out sin 90 because its just equal to 1...
double xyzDistFromSideCorner = Math.sqrt(Math.pow(xzDistFromSideCorner, 2) + Math.pow(deltaFromToSideCornerY, 2));
double theta3 = Math.atan2(Math.abs(deltaFromToSideCornerY), xzDistFromSideCorner) * 180 / Math.PI;
double theta4 = Math.abs(fromPitch) - theta3;
double theta5 = 90 + theta3;
double theta6 = 180 - theta4 - theta5;
double hitDistance = (xyzDistFromSideCorner * Math.sin(Math.toRadians(theta5))) / Math.sin(Math.toRadians(theta6));
double deltaY = hitDistance * Math.sin(Math.toRadians(Math.abs(fromPitch)));// leaves out sin 90 because its just equal to 1...
if (deltaFromToSideCornerX < 0 && deltaX > 0) {
deltaX *= -1;
}
if (fromPitch > 0 && deltaY > 0) {// pitch in minecraft is backwards, normally it would be fromPitch < 0
deltaY *= -1;
}
if (deltaFromToSideCornerZ < 0 && deltaZ > 0) {
deltaZ *= -1;
}
Location hit = from.clone().add(deltaX + addX, deltaY + addY, deltaZ + addZ);
hit.setYaw(fromYaw);
hit.setPitch(fromPitch);
return hit;
}
HitBox.java:
private float yawRotation;
private double x, y, z;
private double[][] additions;
private Location center;
private Location[] corners = new Location[8];
private List<DataZone> dataZones = new ArrayList<DataZone>();
private UUID uuid = UUID.randomUUID();
//#formatter:off
/*
* O = origin
* X = x-axis
* Y = y-axis
* Z = z-axis
* C = center
*
* ---------------------
* / /|
* / / |
* Y-------------------- |
* | 90 | | 0 yaw
* | ^ | | /
* | | | |
* | | | | /
* | HEIGHT C | |
* | | | |/
* | | | Z
* | v | /
* | <---WIDTH---> |/<---LENGTH
* O-------------------X - - - - - - - - - -270 yaw
*/
/**
* An invisible box in the world that can be hit with a shot.
* Additionally, {#link DataZone} instances can be added to this,
* allowing for different damage and thickness on an area of the box.
*
* #param center The center of the hit box
* #param length The length (z axis) of the hit box
* #param width The width (x axis) of the hit box
* #param height The height (y axis) of the hit box
* #param yawRotation The rotation around the center of the origin (or any other point)
*/
public HitBox(Location center, double length, double width, double height, float yawRotation) {
corners[0] = center.clone().add(-1 * width / 2, -1 * height / 2, -1 * length / 2);
this.center = center;
this.x = width;
this.y = height;
this.z = length;
rotate(yawRotation);
}
//#formatter:on
public Location[] getCorners() {
return corners;
}
public Location getCorner(int corner) {
return corners[corner];
}
public Location getOrigin() {
return corners[0];
}
public void update() {};
public boolean isZoneOpen(DataZone zone) {
for (DataZone placed : dataZones) {
boolean Xs = overlap_1D(placed.xFrom, placed.xTo, zone.xFrom, zone.xTo);
boolean Ys = overlap_1D(placed.yFrom, placed.yTo, zone.yFrom, zone.yTo);
boolean Zs = overlap_1D(placed.zFrom, placed.zTo, zone.zFrom, zone.zTo);
if (Xs && Ys && Zs) {
return true;
}
}
return false;
}
public void rotate(float degrees) {
Location origin = corners[0];
this.yawRotation = (yawRotation + degrees) % 360;
additions = new double[][] { {0, 0, 0}, {x, 0, 0}, {0, y, 0}, {0, 0, z}, {x, 0, z}, {x, y, 0}, {x, y, z}, {0, y, z}};
for (int i = 0; i < 8; i++) {
double[] addition = additions[i];
double xPrime = center.getX() + (center.getX() - (origin.getX() + addition[0])) * Math.cos(Math.toRadians(yawRotation)) - (center.getZ() - (origin.getZ() + addition[2])) * Math.sin(Math.toRadians(yawRotation));
double zPrime = center.getZ() + (center.getX() - (origin.getX() + addition[0])) * Math.sin(Math.toRadians(yawRotation)) + (center.getZ() - (origin.getZ() + addition[2])) * Math.cos(Math.toRadians(yawRotation));
corners[i] = new Location(center.getWorld(), xPrime, origin.getY() + addition[1], zPrime, yawRotation, 0);
}
}
public void move(Location center) {
double deltaX = center.getX() - this.center.getX();
double deltaY = center.getY() - this.center.getY();
double deltaZ = center.getZ() - this.center.getZ();
for (int i = 0; i < 8; i++) {
corners[i].add(deltaX, deltaY, deltaZ);
}
this.center = center;
}
protected void setY(double y) {
int[] toChange = new int[] {2, 5, 6, 7};
for (int i : toChange) {
corners[i].setY(corners[0].getY() + y);
}
this.y = y;
}
public double getHighestX() {
double highestX = Double.MIN_VALUE;
for (Location location : corners) {
if (location.getX() > highestX) {
highestX = location.getX();
}
}
return highestX;
}
public double getHighestY() {
return corners[0].getY() + y;
}
public double getHighestZ() {
double highestZ = Double.MIN_VALUE;
for (Location location : corners) {
if (location.getZ() > highestZ) {
highestZ = location.getZ();
}
}
return highestZ;
}
public double getLowestX() {
double lowestX = Double.MAX_VALUE;
for (Location location : corners) {
if (location.getX() < lowestX) {
lowestX = location.getX();
}
}
return lowestX;
}
public double getLowestY() {
return corners[0].getY();
}
public double getLowestZ() {
double lowestZ = Double.MAX_VALUE;
for (Location location : corners) {
if (location.getZ() < lowestZ) {
lowestZ = location.getZ();
}
}
return lowestZ;
}
public float getYawRotation() {
return yawRotation;
}
Perhaps consider drawing a line following the same vector that your bullet travels along, this will provide a visual indicator for what is happening, pass in the same calculations and etc.
As other have mentioned also include lots of debug printouts. Hopefully once you have a visual cue you can see when/where the problem calculations are occuring.
Also you should aim to use a standard data type for calculations, a float or a double, NOT both as this can cause some weird rounding and calculation problems.
I know this is extremely late (almost 5 years in fact), but I developed a solution a few weeks after this question. After revisiting StackOverflow I decided to provide my solution for any who may find it useful.
The issue with the massive wall of code found in the question is that many values are being computed and every computation loses precision, resulting in some degree of variation (like I said, +/-5 blocks).
The solution source may be found here:
https://github.com/JamesNorris/MCShot
To compute intersect (found in util/Plane3D.java):
public Vector getIntersect(LineSegment3D segment) {
Vector u = segment.getEnd().subtract(segment.getStart());
Vector w = segment.getStart().subtract(point);
double D = normal.dot(u);
double N = -normal.dot(w);
if (Math.abs(D) < .00000001) {
/* if N == 0, segment lies in the plane */
return null;
}
double sI = N / D;
if (sI < 0 || sI > 1) {
return null;
}
return segment.getStart().add(u.multiply(sI));
}
As you can see, this requires way fewer computations and as a result provides much greater precision. This function gets the intersection of a 3D plane. A 3D plane is constructed using the values:
point - some point found within the plane normal - the normal
vector of the plane
I hope someone finds this solution helpful, or at least can use it as a lesson in computational precision!

How to solve 2D integral?

I've been trying to implement trapezoid rule for double integral. I've tried many approaches, but I can't get it to work right.
static double f(double x) {
return Math.exp(- x * x / 2);
}
// trapezoid rule
static double trapezoid(double a, double b, int N) {
double h = (b - a) / N;
double sum = 0.5 * h * (f(a) + f(b));
for (int k = 1; k < N; k++)
sum = sum + h * f(a + h*k);
return sum;
}
I understand the method for a single variable integral, but I don't know how to do it for a 2D integral, say: x + (y*y).
Could someone please explain it briefly?
If you're intent on using the trapezoid rule then you would do it like so:
// The example function you provided.
public double f(double x, double y) {
return x + y * y;
}
/**
* Finds the volume under the surface described by the function f(x, y) for a <= x <= b, c <= y <= d.
* Using xSegs number of segments across the x axis and ySegs number of segments across the y axis.
* #param a The lower bound of x.
* #param b The upper bound of x.
* #param c The lower bound of y.
* #param d The upper bound of y.
* #param xSegs The number of segments in the x axis.
* #param ySegs The number of segments in the y axis.
* #return The volume under f(x, y).
*/
public double trapezoidRule(double a, double b, double c, double d, int xSegs, int ySegs) {
double xSegSize = (b - a) / xSegs; // length of an x segment.
double ySegSize = (d - c) / ySegs; // length of a y segment.
double volume = 0; // volume under the surface.
for (int i = 0; i < xSegs; i++) {
for (int j = 0; j < ySegs; j++) {
double height = f(a + (xSegSize * i), c + (ySegSize * j));
height += f(a + (xSegSize * (i + 1)), c + (ySegSize * j));
height += f(a + (xSegSize * (i + 1)), c + (ySegSize * (j + 1)));
height += f(a + (xSegSize * i), c + (ySegSize * (j + 1)));
height /= 4;
// height is the average value of the corners of the current segment.
// We can use the average value since a box of this height has the same volume as the original segment shape.
// Add the volume of the box to the volume.
volume += xSegSize * ySegSize * height;
}
}
return volume;
}
Hope this helps. Feel free to ask any questions you may have about my code (warning: The code is untested).
Many ways to do it.
If you already know it for 1d you could make it like this:
write a function g(x) that calculates the 1d integral over f(x,y) for a fixed x
then integrate the 1d integral over g(x)
Success :)
That way you can basically have as many dimensions as you like. Though it scales poorly. For larger problems it might be neccesary to use monte carlo integration.
Consider using the class jhplot.F2D from the DataMelt Java program. You can integrate and visualize 2D functions doing something like:
f1=F2D("x*y",-1,1,-1,1) # define in a range
print f1.integral()

Compute hex color code for an arbitrary string

Heading
Is there a way to map an arbitrary string to a HEX COLOR code. I tried to compute the HEX number for string using string hashcode. Now I need to convert this hex number to six digits which are in HEX color code range. Any suggestions ?
String [] programs = {"XYZ", "TEST1", "TEST2", "TEST3", "SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"};
for(int i = 0; i < programs.length; i++) {
System.out.println( programs[i] + " -- " + Integer.toHexString(programs[i].hashCode()));
}
If you don't really care about the "meaning" of the color you can just split up the bits of the int (remove the first for just RGB instead of ARGB)
String [] programs = {"XYZ", "TEST1", "TEST2", "TEST3", "SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"};
for(int i = 0; i < programs.length; i++) {
System.out.println( programs[i] + " -- " + intToARGB(programs[i].hashCode()));
}
....
public static String intToARGB(int i){
return Integer.toHexString(((i>>24)&0xFF))+
Integer.toHexString(((i>>16)&0xFF))+
Integer.toHexString(((i>>8)&0xFF))+
Integer.toHexString((i&0xFF));
}
How about anding the hashcode with 0x00FFFFFF (or 0xFFFFFF if you want to default the alpha channel)? For example:
private String getColorCode(String inputString)
{
String colorCode = String.format("#%06x", 0xFFFFFF & inputString.hashCode());
}
I ran into this question while looking for a Ruby solution, so I thought I would add an answer for Ruby in case someone follows the same path I did. I ended up using the following method, which creates the same six digit hex code from a string by using String.hash and the optional base-specifying parameter of Fixnum.to_s. It slices from 1 rather than 0 to skip negative signs.
def color_from_string(query)
'#'+query.hash.to_s(16).slice(1,6)
end
In case anyone else is looking for a solution for Flutter/Dart:
Color _fromInt(int i) {
final a = (i >> 24) & 0xFF;
final r = (i >> 16) & 0xFF;
final g = (i >> 8) & 0xFF;
final b = i & 0xFF;
return Color.fromARGB(a, r, g, b);
}
It's also worth noting that with certain background colours e.g. black, it may be difficult to differentiate the colours.
To this end, I set the alpha channel to the max value of 255:
Color _fromInt(int i) {
const a = 255;
final r = (i >> 16) & 0xFF;
final g = (i >> 8) & 0xFF;
final b = i & 0xFF;
return Color.fromARGB(a, r, g, b);
}
The following class takes a String and converts it to a color.
It is a simplified Java port of the Color-Hash TypeScript project (MIT license): https://github.com/zenozeng/color-hash.
The original project contains some parameters to adjust the generated colours.
These were not included.
The advantage of the Color-Hash algorithm, compared using a hash value directly, is that the generated colours are more perceptually uniform.
A lot of copy/paste was going on here:
Mostly from here the Color-Hash project (MIT license): https://github.com/zenozeng/color-hash
Convert HSL to RGB: https://stackoverflow.com/a/33947547/2021763
Easy way to convert Color to hex string: https://stackoverflow.com/a/18194652/2021763
Result:
XYZ: #bf40b3
TEST1: #86432d
TEST2: #3a2dd2
TEST3: #bf4073
SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS: #53ac8b
public class ColorHash
{
private static final double[] LigthnessArray = new double[] { 0.35, 0.5, 0.65 };
private static final double[] SaturationArray = new double[] { 0.35, 0.5, 0.65 };
public Color generateColor(String input) throws NoSuchAlgorithmException
{
// Easiest way would be String.hashCode()
// But "Test1" and "Test2" result in practically the same color
// Therefore another hash algorithm should be used
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(input.getBytes());
byte[] digest = md.digest();
int hash = Math.abs(ByteBuffer.wrap(digest).getInt());
double hue, saturation, lightness;
hue = hash % 359 / 359.; // note that 359 is a prime
hash = (int) Math.ceil(hash / 360);
saturation = SaturationArray[hash % SaturationArray.length];
hash = (int) Math.ceil(hash / SaturationArray.length);
lightness = LigthnessArray[hash % LigthnessArray.length];
return hslColor((float) hue, (float) saturation, (float) lightness);
}
public String generateColorHash(String input) throws NoSuchAlgorithmException
{
return "#" + Integer.toHexString(generateColor(input).getRGB()).substring(2);
}
private static Color hslColor(float h, float s, float l)
{
float q, p, r, g, b;
if (s == 0)
{
r = g = b = l; // achromatic
} else
{
q = l < 0.5 ? (l * (1 + s)) : (l + s - l * s);
p = 2 * l - q;
r = hue2rgb(p, q, h + 1.0f / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1.0f / 3);
}
return new Color(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255));
}
private static float hue2rgb(float p, float q, float h)
{
if (h < 0)
{
h += 1;
}
if (h > 1)
{
h -= 1;
}
if (6 * h < 1) { return p + ((q - p) * 6 * h); }
if (2 * h < 1) { return q; }
if (3 * h < 2) { return p + ((q - p) * 6 * ((2.0f / 3.0f) - h)); }
return p;
}
public static void main(String[] args) throws NoSuchAlgorithmException
{
String [] programs = {"XYZ", "TEST1", "TEST2", "TEST3", "SDFSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS"};
ColorHash gen = new ColorHash();
for(String p : programs) {
System.out.println(p + ": " + gen.generateColorHash(p));
}
}
}

Categories