Skip to content
Snippets Groups Projects
Commit 95bcb8a3 authored by René Schöne's avatar René Schöne
Browse files

TupleHSB actually has different value ranges.

- Hue goes from 0..360 (and wraps)
- Saturation and brightness go from 0..100
- Ensure those bounds during creation
- Added test for TupleHSB
parent bafabb58
No related branches found
No related tags found
1 merge request!19dev to master
......@@ -11,16 +11,17 @@ import java.util.function.Consumer;
*
* @author rschoene - Initial contribution
*/
@SuppressWarnings("WeakerAccess")
public class ColorUtils {
/**
* Value class comprising three double values X, Y, Z ranging from 0 to 1.
* The values represent coordinates.
*/
public static class XYZvalues {
public static class ValuesXYZ {
double x, y, z;
public static XYZvalues of(double x, double y, double z) {
XYZvalues result = new XYZvalues();
public static ValuesXYZ of(double x, double y, double z) {
ValuesXYZ result = new ValuesXYZ();
result.x = x;
result.y = y;
result.z = z;
......@@ -32,22 +33,22 @@ public class ColorUtils {
* Value class comprising three integral values R, G, B ranging from 0 to 255.
* The values represent red, green and blue.
*/
public static class RGBvalues {
public static class ValuesRGB {
public int red;
public int green;
public int blue;
public static RGBvalues of(int red, int green, int blue) {
RGBvalues result = new RGBvalues();
public static ValuesRGB of(int red, int green, int blue) {
ValuesRGB result = new ValuesRGB();
result.red = red;
result.green = green;
result.blue = blue;
return result;
}
public static RGBvalues of(int[] rgb) {
public static ValuesRGB of(int[] rgb) {
return of(rgb[0], rgb[1], rgb[2]);
}
/** Set all values to a minimum of zero. */
public RGBvalues minZero() {
public ValuesRGB minZero() {
red = Math.max(0, red);
green = Math.max(0, green);
blue = Math.max(0, blue);
......@@ -59,52 +60,52 @@ public class ColorUtils {
* Value class comprising three double values H, S, B ranging from 0 to 1.
* The values represent hue, saturation and brightness.
*/
public static class HSBvalues {
public static class ValuesHSB {
public double hue;
public double saturation;
public double brightness;
public static HSBvalues of(double hue, double saturation, double brightness) {
HSBvalues result = new HSBvalues();
public static ValuesHSB of(double hue, double saturation, double brightness) {
ValuesHSB result = new ValuesHSB();
result.hue = hue;
result.saturation = saturation;
result.brightness = brightness;
return result;
}
/** Set all values to a minimum of zero. */
public HSBvalues minZero() {
public ValuesHSB minZero() {
hue = Math.max(0, hue);
saturation = Math.max(0, saturation);
brightness = Math.max(0, brightness);
return this;
}
/** Convert to HSB with value range of 0..255 */
public HSBvalues255 toIntegral() {
return HSBvalues255.of(
(int) Math.round(255 * hue),
(int) Math.round(255 * saturation),
(int) Math.round(255 * brightness)
/** Convert to the same HSB with integral values */
public ValuesIntegralHSB toIntegral() {
return ValuesIntegralHSB.of(
(int) Math.round(360 * hue),
(int) Math.round(100 * saturation),
(int) Math.round(100 * brightness)
);
}
}
/**
* Value class comprising three integral values H, S, B ranging from 0 to 255.
* Value class comprising three integral values H, S, B ranging from 0 either to 360 (H) or 100 (S and B).
* The values represent hue, saturation and brightness.
*/
public static class HSBvalues255 {
public static class ValuesIntegralHSB {
public int hue;
public int saturation;
public int brightness;
public static HSBvalues255 of(int hue, int saturation, int brightness) {
HSBvalues255 result = new HSBvalues255();
public static ValuesIntegralHSB of(int hue, int saturation, int brightness) {
ValuesIntegralHSB result = new ValuesIntegralHSB();
result.hue = hue;
result.saturation = saturation;
result.brightness = brightness;
return result;
}
/** Set all values to a minimum of zero, and a maximum of 255. */
public HSBvalues255 ensureBounds() {
hue = Math.min(Math.max(0, hue), 255);
/** Set all values to a minimum of zero, and a maximum of either 360 (for H) or 100 (for S and B). */
public ValuesIntegralHSB ensureBounds() {
hue = Math.min(Math.max(0, hue), 360);
saturation = Math.min(Math.max(0, saturation), 100);
brightness = Math.min(Math.max(0, brightness), 100);
return this;
......@@ -122,15 +123,15 @@ public class ColorUtils {
{ 0.0134474, -0.1183897, 1.0154096}};
private static RealMatrix mInverted = MatrixUtils.createRealMatrix(matrixData);
public static RGBvalues convertXYtoRGB(XYZvalues values) {
public static ValuesRGB convertXYtoRGB(ValuesXYZ values) {
return convertXYtoRGB(values.x, values.y, values.z);
}
public static RGBvalues convertXYtoRGB(double x, double y, double z) {
public static ValuesRGB convertXYtoRGB(double x, double y, double z) {
RealMatrix xyz = MatrixUtils.createColumnRealMatrix(new double[] {x, y, z});
RealMatrix result = mInverted.multiply(xyz);
double[][] rgb = result.getData();
return RGBvalues.of(
return ValuesRGB.of(
(int) Math.round(255 * rgb[0][0]), // red
(int) Math.round(255 * rgb[1][0]), // green
(int) Math.round(255 * rgb[2][0])); // blue
......@@ -138,24 +139,24 @@ public class ColorUtils {
public static void convertXYtoRGB(double x, double y, double z, Consumer<Integer> setRed,
Consumer<Integer> setGreen, Consumer<Integer> setBlue) {
RGBvalues result = convertXYtoRGB(x, y, z);
ValuesRGB result = convertXYtoRGB(x, y, z);
setRed.accept(result.red);
setGreen.accept(result.green);
setBlue.accept(result.blue);
}
public static HSBvalues convertRGBtoHSB(RGBvalues values) {
public static ValuesHSB convertRGBtoHSB(ValuesRGB values) {
return convertRGBtoHSB(values.red, values.green, values.blue);
}
public static HSBvalues convertRGBtoHSB(int red, int green, int blue) {
public static ValuesHSB convertRGBtoHSB(int red, int green, int blue) {
float[] hsb = Color.RGBtoHSB(red, green, blue, null);
return HSBvalues.of(hsb[0], hsb[1], hsb[2]);
return ValuesHSB.of(hsb[0], hsb[1], hsb[2]);
}
public static void convertRGBtoHSB(int red, int green, int blue, Consumer<Double> setHue,
Consumer<Double> setSaturation, Consumer<Double> setBrightness) {
HSBvalues values = convertRGBtoHSB(red, green, blue);
ValuesHSB values = convertRGBtoHSB(red, green, blue);
setHue.accept(values.hue);
setSaturation.accept(values.saturation);
setBrightness.accept(values.brightness);
......
......@@ -13,12 +13,16 @@ public class TupleHSB implements Cloneable {
private int brightness;
public static TupleHSB of(int hue, int saturation, int brightness) {
TupleHSB result = new TupleHSB();
result.hue = hue;
result.saturation = saturation;
result.brightness = brightness;
result.hue = hue % 360;
result.saturation = ensureBetweenZeroAndHundred(saturation);
result.brightness = ensureBetweenZeroAndHundred(brightness);
return result;
}
private static int ensureBetweenZeroAndHundred(int value) {
return Math.max(0, Math.min(value, 100));
}
public int getHue() {
return hue;
}
......
......@@ -7,7 +7,6 @@ import org.junit.Assert;
import org.junit.Test;
import java.time.Instant;
import java.util.Date;
/**
* Testing wrapper methods of {@link Item}.
......@@ -70,6 +69,20 @@ public class ItemTests {
sut.stateEquals(TupleHSB.of(99, 99, 3)));
Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(5,5,5)'",
sut.stateEquals(TupleHSB.of(5, 5, 5)));
sut.setState(TupleHSB.of(347, 80, 95));
Assert.assertTrue("State 'TupleHSB(357,80,95)' should match 'TupleHSB(357,80,95)'",
sut.stateEquals(TupleHSB.of(1, 2, 3)));
Assert.assertFalse("State 'TupleHSB(357,80,95)' should not match 'TupleHSB(1,2,4)'",
sut.stateEquals(TupleHSB.of(1, 2, 4)));
Assert.assertFalse("State 'TupleHSB(357,80,95)' should not match 'TupleHSB(9,2,3)'",
sut.stateEquals(TupleHSB.of(9, 2, 3)));
Assert.assertFalse("State 'TupleHSB(357,80,95)' should not match 'TupleHSB(1,17,3)'",
sut.stateEquals(TupleHSB.of(1, 17, 3)));
Assert.assertFalse("State 'TupleHSB(357,80,95)' should not match 'TupleHSB(99,99,3)'",
sut.stateEquals(TupleHSB.of(99, 99, 3)));
Assert.assertFalse("State 'TupleHSB(357,80,95)' should not match 'TupleHSB(5,5,5)'",
sut.stateEquals(TupleHSB.of(5, 5, 5)));
}
@Test
......
package de.tudresden.inf.st.eraser.jastadd_test.core;
import de.tudresden.inf.st.eraser.jastadd.model.TupleHSB;
import org.junit.Assert;
import org.junit.Test;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
/**
* Testing the class {@link de.tudresden.inf.st.eraser.jastadd.model.TupleHSB}.
*
* @author rschoene - Initial contribution
*/
public class TupleHSBTest {
@Test
public void testBounds() {
checkWithinBounds(TupleHSB.of(1,2,3));
checkWithinBounds(TupleHSB.of(340,2,3));
checkWithinBounds(TupleHSB.of(999,2,3));
checkWithinBounds(TupleHSB.of(11,200,3));
checkWithinBounds(TupleHSB.of(240,200,3));
checkWithinBounds(TupleHSB.of(899,200,3));
checkWithinBounds(TupleHSB.of(21,2,300));
checkWithinBounds(TupleHSB.of(140,2,300));
checkWithinBounds(TupleHSB.of(799,2,300));
}
@Test
public void testEquals() {
TupleHSB one = TupleHSB.of(1,2,3);
TupleHSB two = TupleHSB.of(1,2,3);
TupleHSB three = TupleHSB.of(99,99,99);
Assert.assertEquals(one, two);
Assert.assertNotEquals(one, three);
Assert.assertNotEquals(two, three);
TupleHSB oneEqualModuloHue = TupleHSB.of(361,2,3);
Assert.assertEquals(one, oneEqualModuloHue);
TupleHSB bigSB = TupleHSB.of(50,100,100);
TupleHSB biggerS = TupleHSB.of(50,123,100);
TupleHSB biggerB = TupleHSB.of(50,123,6484);
Assert.assertEquals(bigSB, biggerS);
Assert.assertEquals(bigSB, biggerB);
}
@Test
public void testWithDifferent() {
TupleHSB one = TupleHSB.of(1,2,3);
TupleHSB oneDifferentHue = one.withDifferentHue(43);
TupleHSB oneDifferentSaturation = one.withDifferentSaturation(43);
TupleHSB oneDifferentBrightness = one.withDifferentBrightness(43);
TupleHSB oneEqualModuloHue = one.withDifferentHue(721);
Assert.assertNotEquals(one, oneDifferentHue);
Assert.assertNotEquals(one, oneDifferentSaturation);
Assert.assertNotEquals(one, oneDifferentBrightness);
Assert.assertNotEquals(oneDifferentHue, oneDifferentSaturation);
Assert.assertNotEquals(oneDifferentHue, oneDifferentBrightness);
Assert.assertNotEquals(oneDifferentSaturation, oneDifferentBrightness);
Assert.assertEquals(one, oneEqualModuloHue);
}
@Test
public void testPrint() {
TupleHSB one = TupleHSB.of(1,2,3);
String expectedForOne = "1,2,3";
Assert.assertEquals(expectedForOne, one.toString());
TupleHSB two = TupleHSB.of(341,92,555);
String expectedForTwo = "341,92,100";
Assert.assertEquals(expectedForTwo, two.toString());
}
@Test
public void testParse() {
String one = "3,2,1";
TupleHSB expectedForOne = TupleHSB.of(3, 2, 1);
Assert.assertEquals(expectedForOne, TupleHSB.parse(one));
String two = "399,201,17";
TupleHSB expectedForTwo = TupleHSB.of(39, 100, 17);
Assert.assertEquals(expectedForTwo, TupleHSB.parse(two));
}
@Test
public void testClone() {
TupleHSB one = TupleHSB.of(361,2,3);
TupleHSB two = TupleHSB.of(50,123,100);
TupleHSB clone = one.clone();
Assert.assertEquals(one, clone);
Assert.assertNotEquals(one, two);
Assert.assertNotEquals(clone, two);
}
private void checkWithinBounds(TupleHSB hsb) {
Assert.assertThat(hsb.getHue(), greaterThanOrEqualTo(0));
Assert.assertThat(hsb.getHue(), lessThanOrEqualTo(360));
Assert.assertThat(hsb.getSaturation(), greaterThanOrEqualTo(0));
Assert.assertThat(hsb.getSaturation(), lessThanOrEqualTo(100));
Assert.assertThat(hsb.getBrightness(), greaterThanOrEqualTo(0));
Assert.assertThat(hsb.getBrightness(), lessThanOrEqualTo(100));
}
}
package de.tudresden.inf.st.eraser.feedbackloop.api;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.RGBvalues;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.ValuesRGB;
import de.tudresden.inf.st.eraser.jastadd.model.ItemUpdate;
import de.tudresden.inf.st.eraser.jastadd.model.Root;
import de.tudresden.inf.st.eraser.util.Tuple;
......@@ -25,7 +25,7 @@ public interface Execute {
* @param brightnessAndRgbForItems Map, keys are item names, values are RGB and brightness values
*/
@Deprecated
void updateItems(Map<String, Tuple<Integer, RGBvalues>> brightnessAndRgbForItems);
void updateItems(Map<String, Tuple<Integer, ValuesRGB>> brightnessAndRgbForItems);
/**
* Updates items according to given updates
......
package de.tudresden.inf.st.eraser.feedbackloop.execute;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.HSBvalues255;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.RGBvalues;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.ValuesIntegralHSB;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.ValuesRGB;
import de.tudresden.inf.st.eraser.feedbackloop.api.Execute;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import de.tudresden.inf.st.eraser.util.Tuple;
......@@ -28,23 +28,23 @@ public class ExecuteImpl implements Execute {
}
@Override
public void updateItems(Map<String, Tuple<Integer, RGBvalues>> brightnessAndRgbForItems) {
public void updateItems(Map<String, Tuple<Integer, ValuesRGB>> brightnessAndRgbForItems) {
List<ItemUpdate> updates = new ArrayList<>();
for (Map.Entry<String, Tuple<Integer, RGBvalues>> entry : brightnessAndRgbForItems.entrySet()) {
for (Map.Entry<String, Tuple<Integer, ValuesRGB>> entry : brightnessAndRgbForItems.entrySet()) {
String itemId = entry.getKey();
resolveOrLogError(itemId, item -> {
if (entry.getValue() == null) {
return;
}
Integer brightness = entry.getValue().x;
RGBvalues rgb = entry.getValue().y;
HSBvalues255 hsb;
ValuesRGB rgb = entry.getValue().y;
ValuesIntegralHSB hsb;
if (rgb != null) {
// also set rgb values
hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral();
hsb.brightness = brightness;
} else {
hsb = HSBvalues255.of(0, 100, brightness);
hsb = ValuesIntegralHSB.of(0, 100, brightness);
}
hsb.ensureBounds();
updates.add(new ItemUpdateColor(item, TupleHSB.of(hsb.hue, hsb.saturation, hsb.brightness)));
......
......@@ -2,7 +2,7 @@ package de.tudresden.inf.st.eraser.skywriter_hue_integration;
import beaver.Parser;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.RGBvalues;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.ValuesRGB;
import de.tudresden.inf.st.eraser.jastadd.model.*;
import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater;
import de.tudresden.inf.st.eraser.util.ParserUtils;
......@@ -147,10 +147,10 @@ public class Main {
}
private static void updateStateHSB(Item skywriter1_x, Item skywriter1_y, Item irisItem) {
RGBvalues rgb = ColorUtils.convertXYtoRGB(Double.parseDouble(skywriter1_x.getStateAsString()),
ValuesRGB rgb = ColorUtils.convertXYtoRGB(Double.parseDouble(skywriter1_x.getStateAsString()),
Double.parseDouble(skywriter1_y.getStateAsString()), 1.0);
// irisItem.setState(String.format("%d,%d,%d", rgb.red, rgb.green, rgb.blue));
ColorUtils.HSBvalues255 hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral().ensureBounds();
ColorUtils.ValuesIntegralHSB hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral().ensureBounds();
irisItem.setStateFromString(String.format("%d,%d,%d", hsb.hue, hsb.saturation, hsb.brightness));
}
......@@ -161,7 +161,7 @@ public class Main {
double xyzToken = Double.parseDouble(tokens[i]);
rgbArray[i] = (int) Math.round(255 * xyzToken);
}
ColorUtils.HSBvalues255 hsb = ColorUtils.convertRGBtoHSB(RGBvalues.of(rgbArray))
ColorUtils.ValuesIntegralHSB hsb = ColorUtils.convertRGBtoHSB(ValuesRGB.of(rgbArray))
.toIntegral().ensureBounds();
return String.format("%d,%d,%d", hsb.hue, hsb.saturation, hsb.brightness);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment