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; ...@@ -11,16 +11,17 @@ import java.util.function.Consumer;
* *
* @author rschoene - Initial contribution * @author rschoene - Initial contribution
*/ */
@SuppressWarnings("WeakerAccess")
public class ColorUtils { public class ColorUtils {
/** /**
* Value class comprising three double values X, Y, Z ranging from 0 to 1. * Value class comprising three double values X, Y, Z ranging from 0 to 1.
* The values represent coordinates. * The values represent coordinates.
*/ */
public static class XYZvalues { public static class ValuesXYZ {
double x, y, z; double x, y, z;
public static XYZvalues of(double x, double y, double z) { public static ValuesXYZ of(double x, double y, double z) {
XYZvalues result = new XYZvalues(); ValuesXYZ result = new ValuesXYZ();
result.x = x; result.x = x;
result.y = y; result.y = y;
result.z = z; result.z = z;
...@@ -32,22 +33,22 @@ public class ColorUtils { ...@@ -32,22 +33,22 @@ public class ColorUtils {
* Value class comprising three integral values R, G, B ranging from 0 to 255. * Value class comprising three integral values R, G, B ranging from 0 to 255.
* The values represent red, green and blue. * The values represent red, green and blue.
*/ */
public static class RGBvalues { public static class ValuesRGB {
public int red; public int red;
public int green; public int green;
public int blue; public int blue;
public static RGBvalues of(int red, int green, int blue) { public static ValuesRGB of(int red, int green, int blue) {
RGBvalues result = new RGBvalues(); ValuesRGB result = new ValuesRGB();
result.red = red; result.red = red;
result.green = green; result.green = green;
result.blue = blue; result.blue = blue;
return result; return result;
} }
public static RGBvalues of(int[] rgb) { public static ValuesRGB of(int[] rgb) {
return of(rgb[0], rgb[1], rgb[2]); return of(rgb[0], rgb[1], rgb[2]);
} }
/** Set all values to a minimum of zero. */ /** Set all values to a minimum of zero. */
public RGBvalues minZero() { public ValuesRGB minZero() {
red = Math.max(0, red); red = Math.max(0, red);
green = Math.max(0, green); green = Math.max(0, green);
blue = Math.max(0, blue); blue = Math.max(0, blue);
...@@ -59,52 +60,52 @@ public class ColorUtils { ...@@ -59,52 +60,52 @@ public class ColorUtils {
* Value class comprising three double values H, S, B ranging from 0 to 1. * Value class comprising three double values H, S, B ranging from 0 to 1.
* The values represent hue, saturation and brightness. * The values represent hue, saturation and brightness.
*/ */
public static class HSBvalues { public static class ValuesHSB {
public double hue; public double hue;
public double saturation; public double saturation;
public double brightness; public double brightness;
public static HSBvalues of(double hue, double saturation, double brightness) { public static ValuesHSB of(double hue, double saturation, double brightness) {
HSBvalues result = new HSBvalues(); ValuesHSB result = new ValuesHSB();
result.hue = hue; result.hue = hue;
result.saturation = saturation; result.saturation = saturation;
result.brightness = brightness; result.brightness = brightness;
return result; return result;
} }
/** Set all values to a minimum of zero. */ /** Set all values to a minimum of zero. */
public HSBvalues minZero() { public ValuesHSB minZero() {
hue = Math.max(0, hue); hue = Math.max(0, hue);
saturation = Math.max(0, saturation); saturation = Math.max(0, saturation);
brightness = Math.max(0, brightness); brightness = Math.max(0, brightness);
return this; return this;
} }
/** Convert to HSB with value range of 0..255 */ /** Convert to the same HSB with integral values */
public HSBvalues255 toIntegral() { public ValuesIntegralHSB toIntegral() {
return HSBvalues255.of( return ValuesIntegralHSB.of(
(int) Math.round(255 * hue), (int) Math.round(360 * hue),
(int) Math.round(255 * saturation), (int) Math.round(100 * saturation),
(int) Math.round(255 * brightness) (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. * The values represent hue, saturation and brightness.
*/ */
public static class HSBvalues255 { public static class ValuesIntegralHSB {
public int hue; public int hue;
public int saturation; public int saturation;
public int brightness; public int brightness;
public static HSBvalues255 of(int hue, int saturation, int brightness) { public static ValuesIntegralHSB of(int hue, int saturation, int brightness) {
HSBvalues255 result = new HSBvalues255(); ValuesIntegralHSB result = new ValuesIntegralHSB();
result.hue = hue; result.hue = hue;
result.saturation = saturation; result.saturation = saturation;
result.brightness = brightness; result.brightness = brightness;
return result; return result;
} }
/** Set all values to a minimum of zero, and a maximum of 255. */ /** Set all values to a minimum of zero, and a maximum of either 360 (for H) or 100 (for S and B). */
public HSBvalues255 ensureBounds() { public ValuesIntegralHSB ensureBounds() {
hue = Math.min(Math.max(0, hue), 255); hue = Math.min(Math.max(0, hue), 360);
saturation = Math.min(Math.max(0, saturation), 100); saturation = Math.min(Math.max(0, saturation), 100);
brightness = Math.min(Math.max(0, brightness), 100); brightness = Math.min(Math.max(0, brightness), 100);
return this; return this;
...@@ -122,15 +123,15 @@ public class ColorUtils { ...@@ -122,15 +123,15 @@ public class ColorUtils {
{ 0.0134474, -0.1183897, 1.0154096}}; { 0.0134474, -0.1183897, 1.0154096}};
private static RealMatrix mInverted = MatrixUtils.createRealMatrix(matrixData); 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); 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 xyz = MatrixUtils.createColumnRealMatrix(new double[] {x, y, z});
RealMatrix result = mInverted.multiply(xyz); RealMatrix result = mInverted.multiply(xyz);
double[][] rgb = result.getData(); double[][] rgb = result.getData();
return RGBvalues.of( return ValuesRGB.of(
(int) Math.round(255 * rgb[0][0]), // red (int) Math.round(255 * rgb[0][0]), // red
(int) Math.round(255 * rgb[1][0]), // green (int) Math.round(255 * rgb[1][0]), // green
(int) Math.round(255 * rgb[2][0])); // blue (int) Math.round(255 * rgb[2][0])); // blue
...@@ -138,24 +139,24 @@ public class ColorUtils { ...@@ -138,24 +139,24 @@ public class ColorUtils {
public static void convertXYtoRGB(double x, double y, double z, Consumer<Integer> setRed, public static void convertXYtoRGB(double x, double y, double z, Consumer<Integer> setRed,
Consumer<Integer> setGreen, Consumer<Integer> setBlue) { Consumer<Integer> setGreen, Consumer<Integer> setBlue) {
RGBvalues result = convertXYtoRGB(x, y, z); ValuesRGB result = convertXYtoRGB(x, y, z);
setRed.accept(result.red); setRed.accept(result.red);
setGreen.accept(result.green); setGreen.accept(result.green);
setBlue.accept(result.blue); setBlue.accept(result.blue);
} }
public static HSBvalues convertRGBtoHSB(RGBvalues values) { public static ValuesHSB convertRGBtoHSB(ValuesRGB values) {
return convertRGBtoHSB(values.red, values.green, values.blue); 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); 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, public static void convertRGBtoHSB(int red, int green, int blue, Consumer<Double> setHue,
Consumer<Double> setSaturation, Consumer<Double> setBrightness) { Consumer<Double> setSaturation, Consumer<Double> setBrightness) {
HSBvalues values = convertRGBtoHSB(red, green, blue); ValuesHSB values = convertRGBtoHSB(red, green, blue);
setHue.accept(values.hue); setHue.accept(values.hue);
setSaturation.accept(values.saturation); setSaturation.accept(values.saturation);
setBrightness.accept(values.brightness); setBrightness.accept(values.brightness);
......
...@@ -13,12 +13,16 @@ public class TupleHSB implements Cloneable { ...@@ -13,12 +13,16 @@ public class TupleHSB implements Cloneable {
private int brightness; private int brightness;
public static TupleHSB of(int hue, int saturation, int brightness) { public static TupleHSB of(int hue, int saturation, int brightness) {
TupleHSB result = new TupleHSB(); TupleHSB result = new TupleHSB();
result.hue = hue; result.hue = hue % 360;
result.saturation = saturation; result.saturation = ensureBetweenZeroAndHundred(saturation);
result.brightness = brightness; result.brightness = ensureBetweenZeroAndHundred(brightness);
return result; return result;
} }
private static int ensureBetweenZeroAndHundred(int value) {
return Math.max(0, Math.min(value, 100));
}
public int getHue() { public int getHue() {
return hue; return hue;
} }
......
...@@ -7,7 +7,6 @@ import org.junit.Assert; ...@@ -7,7 +7,6 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.time.Instant; import java.time.Instant;
import java.util.Date;
/** /**
* Testing wrapper methods of {@link Item}. * Testing wrapper methods of {@link Item}.
...@@ -70,6 +69,20 @@ public class ItemTests { ...@@ -70,6 +69,20 @@ public class ItemTests {
sut.stateEquals(TupleHSB.of(99, 99, 3))); sut.stateEquals(TupleHSB.of(99, 99, 3)));
Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(5,5,5)'", Assert.assertFalse("State 'TupleHSB(1,2,3)' should not match 'TupleHSB(5,5,5)'",
sut.stateEquals(TupleHSB.of(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 @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; 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.ItemUpdate;
import de.tudresden.inf.st.eraser.jastadd.model.Root; import de.tudresden.inf.st.eraser.jastadd.model.Root;
import de.tudresden.inf.st.eraser.util.Tuple; import de.tudresden.inf.st.eraser.util.Tuple;
...@@ -25,7 +25,7 @@ public interface Execute { ...@@ -25,7 +25,7 @@ public interface Execute {
* @param brightnessAndRgbForItems Map, keys are item names, values are RGB and brightness values * @param brightnessAndRgbForItems Map, keys are item names, values are RGB and brightness values
*/ */
@Deprecated @Deprecated
void updateItems(Map<String, Tuple<Integer, RGBvalues>> brightnessAndRgbForItems); void updateItems(Map<String, Tuple<Integer, ValuesRGB>> brightnessAndRgbForItems);
/** /**
* Updates items according to given updates * Updates items according to given updates
......
package de.tudresden.inf.st.eraser.feedbackloop.execute; 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;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils.HSBvalues255; import de.tudresden.inf.st.eraser.commons.color.ColorUtils.ValuesIntegralHSB;
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.feedbackloop.api.Execute; import de.tudresden.inf.st.eraser.feedbackloop.api.Execute;
import de.tudresden.inf.st.eraser.jastadd.model.*; import de.tudresden.inf.st.eraser.jastadd.model.*;
import de.tudresden.inf.st.eraser.util.Tuple; import de.tudresden.inf.st.eraser.util.Tuple;
...@@ -28,23 +28,23 @@ public class ExecuteImpl implements Execute { ...@@ -28,23 +28,23 @@ public class ExecuteImpl implements Execute {
} }
@Override @Override
public void updateItems(Map<String, Tuple<Integer, RGBvalues>> brightnessAndRgbForItems) { public void updateItems(Map<String, Tuple<Integer, ValuesRGB>> brightnessAndRgbForItems) {
List<ItemUpdate> updates = new ArrayList<>(); 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(); String itemId = entry.getKey();
resolveOrLogError(itemId, item -> { resolveOrLogError(itemId, item -> {
if (entry.getValue() == null) { if (entry.getValue() == null) {
return; return;
} }
Integer brightness = entry.getValue().x; Integer brightness = entry.getValue().x;
RGBvalues rgb = entry.getValue().y; ValuesRGB rgb = entry.getValue().y;
HSBvalues255 hsb; ValuesIntegralHSB hsb;
if (rgb != null) { if (rgb != null) {
// also set rgb values // also set rgb values
hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral(); hsb = ColorUtils.convertRGBtoHSB(rgb).toIntegral();
hsb.brightness = brightness; hsb.brightness = brightness;
} else { } else {
hsb = HSBvalues255.of(0, 100, brightness); hsb = ValuesIntegralHSB.of(0, 100, brightness);
} }
hsb.ensureBounds(); hsb.ensureBounds();
updates.add(new ItemUpdateColor(item, TupleHSB.of(hsb.hue, hsb.saturation, hsb.brightness))); 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; ...@@ -2,7 +2,7 @@ package de.tudresden.inf.st.eraser.skywriter_hue_integration;
import beaver.Parser; import beaver.Parser;
import de.tudresden.inf.st.eraser.commons.color.ColorUtils; 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.jastadd.model.*;
import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater; import de.tudresden.inf.st.eraser.openhab2.mqtt.MQTTUpdater;
import de.tudresden.inf.st.eraser.util.ParserUtils; import de.tudresden.inf.st.eraser.util.ParserUtils;
...@@ -147,10 +147,10 @@ public class Main { ...@@ -147,10 +147,10 @@ public class Main {
} }
private static void updateStateHSB(Item skywriter1_x, Item skywriter1_y, Item irisItem) { 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); Double.parseDouble(skywriter1_y.getStateAsString()), 1.0);
// irisItem.setState(String.format("%d,%d,%d", rgb.red, rgb.green, rgb.blue)); // 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)); irisItem.setStateFromString(String.format("%d,%d,%d", hsb.hue, hsb.saturation, hsb.brightness));
} }
...@@ -161,7 +161,7 @@ public class Main { ...@@ -161,7 +161,7 @@ public class Main {
double xyzToken = Double.parseDouble(tokens[i]); double xyzToken = Double.parseDouble(tokens[i]);
rgbArray[i] = (int) Math.round(255 * xyzToken); 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(); .toIntegral().ensureBounds();
return String.format("%d,%d,%d", hsb.hue, hsb.saturation, hsb.brightness); 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