mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[wpilib] Add hex string constructor to Color and Color8Bit (#5063)
Co-authored-by: Tyler Veness <calcmogul@gmail.com>
This commit is contained in:
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "frc/util/Color.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
using namespace frc;
|
||||
|
||||
std::string Color::HexString() const {
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
#include "frc/util/Color8Bit.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
using namespace frc;
|
||||
|
||||
std::string Color8Bit::HexString() const {
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
namespace frc {
|
||||
|
||||
@@ -739,6 +744,9 @@ class Color {
|
||||
*/
|
||||
static const Color kYellowGreen;
|
||||
|
||||
/**
|
||||
* Constructs a default color (black).
|
||||
*/
|
||||
constexpr Color() = default;
|
||||
|
||||
/**
|
||||
@@ -763,6 +771,33 @@ class Color {
|
||||
constexpr Color(int r, int g, int b)
|
||||
: Color(r / 255.0, g / 255.0, b / 255.0) {}
|
||||
|
||||
/**
|
||||
* Constructs a Color from a hex string.
|
||||
*
|
||||
* @param hexString a string of the format <tt>\#RRGGBB</tt>
|
||||
* @throws std::invalid_argument if the hex string is invalid.
|
||||
*/
|
||||
explicit constexpr Color(std::string_view hexString) {
|
||||
if (hexString.length() != 7 || !hexString.starts_with("#") ||
|
||||
!wpi::isHexDigit(hexString[1]) || !wpi::isHexDigit(hexString[2]) ||
|
||||
!wpi::isHexDigit(hexString[3]) || !wpi::isHexDigit(hexString[4]) ||
|
||||
!wpi::isHexDigit(hexString[5]) || !wpi::isHexDigit(hexString[6])) {
|
||||
throw std::invalid_argument(
|
||||
fmt::format("Invalid hex string for Color \"{}\"", hexString));
|
||||
}
|
||||
|
||||
int r = wpi::hexDigitValue(hexString[1]) * 16 +
|
||||
wpi::hexDigitValue(hexString[2]);
|
||||
int g = wpi::hexDigitValue(hexString[3]) * 16 +
|
||||
wpi::hexDigitValue(hexString[4]);
|
||||
int b = wpi::hexDigitValue(hexString[5]) * 16 +
|
||||
wpi::hexDigitValue(hexString[6]);
|
||||
|
||||
red = r / 255.0;
|
||||
green = g / 255.0;
|
||||
blue = b / 255.0;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Color&) const = default;
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
|
||||
#include "Color.h"
|
||||
|
||||
@@ -16,6 +21,9 @@ namespace frc {
|
||||
*/
|
||||
class Color8Bit {
|
||||
public:
|
||||
/**
|
||||
* Constructs a default color (black).
|
||||
*/
|
||||
constexpr Color8Bit() = default;
|
||||
|
||||
/**
|
||||
@@ -40,11 +48,59 @@ class Color8Bit {
|
||||
green(color.green * 255),
|
||||
blue(color.blue * 255) {}
|
||||
|
||||
/**
|
||||
* Constructs a Color8Bit from a hex string.
|
||||
*
|
||||
* @param hexString a string of the format <tt>\#RRGGBB</tt>
|
||||
* @throws std::invalid_argument if the hex string is invalid.
|
||||
*/
|
||||
explicit constexpr Color8Bit(std::string_view hexString) {
|
||||
if (hexString.length() != 7 || !hexString.starts_with("#") ||
|
||||
!wpi::isHexDigit(hexString[1]) || !wpi::isHexDigit(hexString[2]) ||
|
||||
!wpi::isHexDigit(hexString[3]) || !wpi::isHexDigit(hexString[4]) ||
|
||||
!wpi::isHexDigit(hexString[5]) || !wpi::isHexDigit(hexString[6])) {
|
||||
throw std::invalid_argument(
|
||||
fmt::format("Invalid hex string for Color \"{}\"", hexString));
|
||||
}
|
||||
|
||||
red = wpi::hexDigitValue(hexString[1]) * 16 +
|
||||
wpi::hexDigitValue(hexString[2]);
|
||||
green = wpi::hexDigitValue(hexString[3]) * 16 +
|
||||
wpi::hexDigitValue(hexString[4]);
|
||||
blue = wpi::hexDigitValue(hexString[5]) * 16 +
|
||||
wpi::hexDigitValue(hexString[6]);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Color8Bit&) const = default;
|
||||
|
||||
constexpr operator Color() const { // NOLINT
|
||||
return Color(red / 255.0, green / 255.0, blue / 255.0);
|
||||
}
|
||||
|
||||
constexpr bool operator==(const Color8Bit&) const = default;
|
||||
/**
|
||||
* Create a Color8Bit from a hex string.
|
||||
*
|
||||
* @param hexString a string of the format <tt>\#RRGGBB</tt>
|
||||
* @return Color8Bit object from hex string.
|
||||
* @throws std::invalid_argument if the hex string is invalid.
|
||||
*/
|
||||
static constexpr Color8Bit FromHexString(std::string_view hexString) {
|
||||
if (hexString.length() != 7 || !hexString.starts_with("#") ||
|
||||
!wpi::isHexDigit(hexString[1]) || !wpi::isHexDigit(hexString[2]) ||
|
||||
!wpi::isHexDigit(hexString[3]) || !wpi::isHexDigit(hexString[4]) ||
|
||||
!wpi::isHexDigit(hexString[5]) || !wpi::isHexDigit(hexString[6])) {
|
||||
throw std::invalid_argument(
|
||||
fmt::format("Invalid hex string for Color \"{}\"", hexString));
|
||||
}
|
||||
|
||||
int r = wpi::hexDigitValue(hexString[0]) * 16 +
|
||||
wpi::hexDigitValue(hexString[1]);
|
||||
int g = wpi::hexDigitValue(hexString[2]) * 16 +
|
||||
wpi::hexDigitValue(hexString[3]);
|
||||
int b = wpi::hexDigitValue(hexString[4]) * 16 +
|
||||
wpi::hexDigitValue(hexString[5]);
|
||||
return Color8Bit{r, g, b};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return this color represented as a hex string.
|
||||
|
||||
62
wpilibc/src/test/native/cpp/util/Color8BitTest.cpp
Normal file
62
wpilibc/src/test/native/cpp/util/Color8BitTest.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "frc/util/Color8Bit.h"
|
||||
|
||||
TEST(Color8BitTest, ConstructDefault) {
|
||||
constexpr frc::Color8Bit color;
|
||||
|
||||
EXPECT_EQ(0, color.red);
|
||||
EXPECT_EQ(0, color.green);
|
||||
EXPECT_EQ(0, color.blue);
|
||||
}
|
||||
|
||||
TEST(Color8BitTest, ConstructFromInts) {
|
||||
constexpr frc::Color8Bit color{255, 128, 64};
|
||||
|
||||
EXPECT_EQ(255, color.red);
|
||||
EXPECT_EQ(128, color.green);
|
||||
EXPECT_EQ(64, color.blue);
|
||||
}
|
||||
|
||||
TEST(Color8BitTest, ConstructFromColor) {
|
||||
constexpr frc::Color8Bit color{frc::Color{255, 128, 64}};
|
||||
|
||||
EXPECT_EQ(255, color.red);
|
||||
EXPECT_EQ(128, color.green);
|
||||
EXPECT_EQ(64, color.blue);
|
||||
}
|
||||
|
||||
TEST(Color8BitTest, ConstructFromHexString) {
|
||||
constexpr frc::Color8Bit color{"#FF8040"};
|
||||
|
||||
EXPECT_EQ(255, color.red);
|
||||
EXPECT_EQ(128, color.green);
|
||||
EXPECT_EQ(64, color.blue);
|
||||
|
||||
// No leading #
|
||||
EXPECT_THROW(frc::Color8Bit{"112233"}, std::invalid_argument);
|
||||
|
||||
// Too long
|
||||
EXPECT_THROW(frc::Color8Bit{"#11223344"}, std::invalid_argument);
|
||||
|
||||
// Invalid hex characters
|
||||
EXPECT_THROW(frc::Color8Bit{"#$$$$$$"}, std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(Color8BitTest, ImplicitConversionToColor) {
|
||||
frc::Color color = frc::Color8Bit{255, 128, 64};
|
||||
|
||||
EXPECT_NEAR(1.0, color.red, 1e-2);
|
||||
EXPECT_NEAR(0.5, color.green, 1e-2);
|
||||
EXPECT_NEAR(0.25, color.blue, 1e-2);
|
||||
}
|
||||
|
||||
TEST(Color8BitTest, ToHexString) {
|
||||
constexpr frc::Color8Bit color{255, 128, 64};
|
||||
|
||||
EXPECT_EQ("#FF8040", color.HexString());
|
||||
}
|
||||
62
wpilibc/src/test/native/cpp/util/ColorTest.cpp
Normal file
62
wpilibc/src/test/native/cpp/util/ColorTest.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "frc/util/Color.h"
|
||||
|
||||
TEST(ColorTest, ConstructDefault) {
|
||||
constexpr frc::Color color;
|
||||
|
||||
EXPECT_DOUBLE_EQ(0.0, color.red);
|
||||
EXPECT_DOUBLE_EQ(0.0, color.green);
|
||||
EXPECT_DOUBLE_EQ(0.0, color.blue);
|
||||
}
|
||||
|
||||
TEST(ColorTest, ConstructFromDoubles) {
|
||||
constexpr frc::Color color{1.0, 0.5, 0.25};
|
||||
|
||||
EXPECT_NEAR(1.0, color.red, 1e-2);
|
||||
EXPECT_NEAR(0.5, color.green, 1e-2);
|
||||
EXPECT_NEAR(0.25, color.blue, 1e-2);
|
||||
}
|
||||
|
||||
TEST(ColorTest, ConstructFromInts) {
|
||||
constexpr frc::Color color{255, 128, 64};
|
||||
|
||||
EXPECT_NEAR(1.0, color.red, 1e-2);
|
||||
EXPECT_NEAR(0.5, color.green, 1e-2);
|
||||
EXPECT_NEAR(0.25, color.blue, 1e-2);
|
||||
}
|
||||
|
||||
TEST(ColorTest, ConstructFromHexString) {
|
||||
constexpr frc::Color color{"#FF8040"};
|
||||
|
||||
EXPECT_NEAR(1.0, color.red, 1e-2);
|
||||
EXPECT_NEAR(0.5, color.green, 1e-2);
|
||||
EXPECT_NEAR(0.25, color.blue, 1e-2);
|
||||
|
||||
// No leading #
|
||||
EXPECT_THROW(frc::Color{"112233"}, std::invalid_argument);
|
||||
|
||||
// Too long
|
||||
EXPECT_THROW(frc::Color{"#11223344"}, std::invalid_argument);
|
||||
|
||||
// Invalid hex characters
|
||||
EXPECT_THROW(frc::Color{"#$$$$$$"}, std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(ColorTest, FromHSV) {
|
||||
constexpr frc::Color color = frc::Color::FromHSV(90, 128, 64);
|
||||
|
||||
EXPECT_DOUBLE_EQ(0.1256103515625, color.red);
|
||||
EXPECT_DOUBLE_EQ(0.2510986328125, color.green);
|
||||
EXPECT_DOUBLE_EQ(0.2510986328125, color.blue);
|
||||
}
|
||||
|
||||
TEST(ColorTest, ToHexString) {
|
||||
constexpr frc::Color color{255, 128, 64};
|
||||
|
||||
EXPECT_EQ("#FF8040", color.HexString());
|
||||
}
|
||||
@@ -21,6 +21,13 @@ public class Color {
|
||||
public final double blue;
|
||||
private String m_name;
|
||||
|
||||
/** Constructs a default color (black). */
|
||||
public Color() {
|
||||
red = 0.0;
|
||||
green = 0.0;
|
||||
blue = 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Color from doubles.
|
||||
*
|
||||
@@ -69,6 +76,22 @@ public class Color {
|
||||
this.m_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Color from a hex string.
|
||||
*
|
||||
* @param hexString a string of the format <code>#RRGGBB</code>
|
||||
* @throws IllegalArgumentException if the hex string is invalid.
|
||||
*/
|
||||
public Color(String hexString) {
|
||||
if (hexString.length() != 7 || !hexString.startsWith("#")) {
|
||||
throw new IllegalArgumentException("Invalid hex string \"" + hexString + "\"");
|
||||
}
|
||||
|
||||
this.red = Integer.valueOf(hexString.substring(1, 3), 16) / 255.0;
|
||||
this.green = Integer.valueOf(hexString.substring(3, 5), 16) / 255.0;
|
||||
this.blue = Integer.valueOf(hexString.substring(5, 7), 16) / 255.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Color from HSV values.
|
||||
*
|
||||
|
||||
@@ -14,6 +14,13 @@ public class Color8Bit {
|
||||
public final int green;
|
||||
public final int blue;
|
||||
|
||||
/** Constructs a default color (black). */
|
||||
public Color8Bit() {
|
||||
red = 0;
|
||||
green = 0;
|
||||
blue = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Color8Bit.
|
||||
*
|
||||
@@ -36,6 +43,22 @@ public class Color8Bit {
|
||||
this((int) (color.red * 255), (int) (color.green * 255), (int) (color.blue * 255));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Color8Bit from a hex string.
|
||||
*
|
||||
* @param hexString a string of the format <code>#RRGGBB</code>
|
||||
* @throws IllegalArgumentException if the hex string is invalid.
|
||||
*/
|
||||
public Color8Bit(String hexString) {
|
||||
if (hexString.length() != 7 || !hexString.startsWith("#")) {
|
||||
throw new IllegalArgumentException("Invalid hex string \"" + hexString + "\"");
|
||||
}
|
||||
|
||||
this.red = Integer.valueOf(hexString.substring(1, 3), 16);
|
||||
this.green = Integer.valueOf(hexString.substring(3, 5), 16);
|
||||
this.blue = Integer.valueOf(hexString.substring(5, 7), 16);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class Color8BitTest {
|
||||
@Test
|
||||
void testConstructDefault() {
|
||||
var color = new Color8Bit();
|
||||
|
||||
assertEquals(0, color.red);
|
||||
assertEquals(0, color.green);
|
||||
assertEquals(0, color.blue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructFromInts() {
|
||||
var color = new Color8Bit(255, 128, 64);
|
||||
|
||||
assertEquals(255, color.red);
|
||||
assertEquals(128, color.green);
|
||||
assertEquals(64, color.blue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructFromColor() {
|
||||
var color = new Color8Bit(new Color(255, 128, 64));
|
||||
|
||||
assertEquals(255, color.red);
|
||||
assertEquals(128, color.green);
|
||||
assertEquals(64, color.blue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructFromHexString() {
|
||||
var color = new Color8Bit("#FF8040");
|
||||
|
||||
assertEquals(255, color.red);
|
||||
assertEquals(128, color.green);
|
||||
assertEquals(64, color.blue);
|
||||
|
||||
// No leading #
|
||||
assertThrows(IllegalArgumentException.class, () -> new Color8Bit("112233"));
|
||||
|
||||
// Too long
|
||||
assertThrows(IllegalArgumentException.class, () -> new Color8Bit("#11223344"));
|
||||
|
||||
// Invalid hex characters
|
||||
assertThrows(IllegalArgumentException.class, () -> new Color8Bit("#$$$$$$"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToHexString() {
|
||||
var color = new Color8Bit(255, 128, 64);
|
||||
|
||||
assertEquals("#FF8040", color.toHexString());
|
||||
assertEquals("#FF8040", color.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) FIRST and other WPILib contributors.
|
||||
// Open Source Software; you can modify and/or share it under the terms of
|
||||
// the WPILib BSD license file in the root directory of this project.
|
||||
|
||||
package edu.wpi.first.wpilibj.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ColorTest {
|
||||
@Test
|
||||
void testConstructDefault() {
|
||||
var color = new Color();
|
||||
|
||||
assertEquals(0.0, color.red);
|
||||
assertEquals(0.0, color.green);
|
||||
assertEquals(0.0, color.blue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructFromDoubles() {
|
||||
var color = new Color(1.0, 0.5, 0.25);
|
||||
|
||||
assertEquals(1.0, color.red, 1e-2);
|
||||
assertEquals(0.5, color.green, 1e-2);
|
||||
assertEquals(0.25, color.blue, 1e-2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructFromInts() {
|
||||
var color = new Color(255, 128, 64);
|
||||
|
||||
assertEquals(1.0, color.red, 1e-2);
|
||||
assertEquals(0.5, color.green, 1e-2);
|
||||
assertEquals(0.25, color.blue, 1e-2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConstructFromHexString() {
|
||||
var color = new Color("#FF8040");
|
||||
|
||||
assertEquals(1.0, color.red, 1e-2);
|
||||
assertEquals(0.5, color.green, 1e-2);
|
||||
assertEquals(0.25, color.blue, 1e-2);
|
||||
|
||||
// No leading #
|
||||
assertThrows(IllegalArgumentException.class, () -> new Color("112233"));
|
||||
|
||||
// Too long
|
||||
assertThrows(IllegalArgumentException.class, () -> new Color("#11223344"));
|
||||
|
||||
// Invalid hex characters
|
||||
assertThrows(IllegalArgumentException.class, () -> new Color("#$$$$$$"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromHSV() {
|
||||
var color = Color.fromHSV(90, 128, 64);
|
||||
|
||||
assertEquals(0.125732421875, color.red);
|
||||
assertEquals(0.251220703125, color.green);
|
||||
assertEquals(0.251220703125, color.blue);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToHexString() {
|
||||
var color = new Color(255, 128, 64);
|
||||
|
||||
assertEquals("#FF8040", color.toHexString());
|
||||
assertEquals("#FF8040", color.toString());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user