[wpiutil] Move Color and Color8Bit from wpilib to wpiutil (#8437)

Removes one of the org.wpilib.util package conflicts for modularization.

Only a few minor tweaks were required to remove the wpimath dependency.
This commit is contained in:
Peter Johnson
2025-11-30 11:11:48 -08:00
committed by GitHub
parent e902a98601
commit 42992953ed
32 changed files with 468 additions and 385 deletions

View File

@@ -984,26 +984,6 @@ def wpilib_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], includ
("wpi::Alert", "wpi__Alert.hpp"),
],
),
struct(
class_name = "Color",
yml_file = "semiwrap/Color.yml",
header_root = "$(execpath :robotpy-native-wpilib.copy_headers)",
header_file = "$(execpath :robotpy-native-wpilib.copy_headers)/wpi/util/Color.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::Color", "wpi__Color.hpp"),
],
),
struct(
class_name = "Color8Bit",
yml_file = "semiwrap/Color8Bit.yml",
header_root = "$(execpath :robotpy-native-wpilib.copy_headers)",
header_file = "$(execpath :robotpy-native-wpilib.copy_headers)/wpi/util/Color8Bit.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::Color8Bit", "wpi__Color8Bit.hpp"),
],
),
struct(
class_name = "Preferences",
yml_file = "semiwrap/Preferences.yml",

View File

@@ -75,5 +75,5 @@ void AddressableLED::SetGlobalData(int start, ColorOrder colorOrder,
}
void AddressableLED::LEDData::SetHSV(int h, int s, int v) {
SetLED(Color::FromHSV(h, s, v));
SetLED(wpi::util::Color::FromHSV(h, s, v));
}

View File

@@ -17,24 +17,29 @@
using namespace wpi;
LEDPattern::LEDPattern(std::function<void(wpi::LEDPattern::LEDReader,
std::function<void(int, wpi::Color)>)>
impl)
LEDPattern::LEDPattern(
std::function<void(wpi::LEDPattern::LEDReader,
std::function<void(int, wpi::util::Color)>)>
impl)
: m_impl(std::move(impl)) {}
void LEDPattern::ApplyTo(LEDPattern::LEDReader reader,
std::function<void(int, wpi::Color)> writer) const {
void LEDPattern::ApplyTo(
LEDPattern::LEDReader reader,
std::function<void(int, wpi::util::Color)> writer) const {
m_impl(reader, writer);
}
void LEDPattern::ApplyTo(std::span<AddressableLED::LEDData> data,
std::function<void(int, wpi::Color)> writer) const {
void LEDPattern::ApplyTo(
std::span<AddressableLED::LEDData> data,
std::function<void(int, wpi::util::Color)> writer) const {
ApplyTo(LEDPattern::LEDReader{[=](size_t i) { return data[i]; }, data.size()},
writer);
}
void LEDPattern::ApplyTo(std::span<AddressableLED::LEDData> data) const {
ApplyTo(data, [&](int index, Color color) { data[index].SetLED(color); });
ApplyTo(data, [&](int index, wpi::util::Color color) {
data[index].SetLED(color);
});
}
LEDPattern LEDPattern::MapIndex(
@@ -44,7 +49,9 @@ LEDPattern LEDPattern::MapIndex(
self.ApplyTo(
LEDPattern::LEDReader{
[=](auto i) { return data[indexMapper(bufLen, i)]; }, bufLen},
[&](int i, Color color) { writer(indexMapper(bufLen, i), color); });
[&](int i, wpi::util::Color color) {
writer(indexMapper(bufLen, i), color);
});
}};
}
@@ -131,7 +138,7 @@ LEDPattern LEDPattern::Breathe(wpi::units::second_t period) {
auto periodMicros = wpi::units::microsecond_t{period};
return LEDPattern{[periodMicros, self = *this](auto data, auto writer) {
self.ApplyTo(data, [&writer, periodMicros](int i, Color color) {
self.ApplyTo(data, [&writer, periodMicros](int i, wpi::util::Color color) {
double t = (wpi::util::Now() % periodMicros.to<uint64_t>()) /
periodMicros.to<double>();
double phase = t * 2 * std::numbers::pi;
@@ -140,7 +147,8 @@ LEDPattern LEDPattern::Breathe(wpi::units::second_t period) {
// Use cosine so the period starts at 100% brightness
double dim = (std::cos(phase) + 1) / 2.0;
writer(i, Color{color.red * dim, color.green * dim, color.blue * dim});
writer(i, wpi::util::Color{color.red * dim, color.green * dim,
color.blue * dim});
});
}};
}
@@ -151,7 +159,7 @@ LEDPattern LEDPattern::OverlayOn(const LEDPattern& base) {
base.ApplyTo(data, writer);
// ... then, overwrite with illuminated LEDs from the overlay
self.ApplyTo(data, [&](int i, Color color) {
self.ApplyTo(data, [&](int i, wpi::util::Color color) {
if (color.red > 0 || color.green > 0 || color.blue > 0) {
writer(i, color);
}
@@ -164,12 +172,12 @@ LEDPattern LEDPattern::Blend(const LEDPattern& other) {
// Apply the current pattern down as normal...
self.ApplyTo(data, writer);
other.ApplyTo(data, [&](int i, Color color) {
other.ApplyTo(data, [&](int i, wpi::util::Color color) {
// ... then read the result and average it with the output from the other
// pattern
writer(i, Color{(data[i].r / 255.0 + color.red) / 2,
(data[i].g / 255.0 + color.green) / 2,
(data[i].b / 255.0 + color.blue) / 2});
writer(i, wpi::util::Color{(data[i].r / 255.0 + color.red) / 2,
(data[i].g / 255.0 + color.green) / 2,
(data[i].b / 255.0 + color.blue) / 2});
});
}};
}
@@ -179,23 +187,24 @@ LEDPattern LEDPattern::Mask(const LEDPattern& mask) {
// Apply the current pattern down as normal...
self.ApplyTo(data, writer);
mask.ApplyTo(data, [&](int i, Color color) {
mask.ApplyTo(data, [&](int i, wpi::util::Color color) {
auto currentColor = data[i];
// ... then perform a bitwise AND operation on each channel to apply the
// mask
writer(i, Color{currentColor.r & static_cast<uint8_t>(255 * color.red),
currentColor.g & static_cast<uint8_t>(255 * color.green),
currentColor.b & static_cast<uint8_t>(255 * color.blue)});
writer(i, wpi::util::Color{
currentColor.r & static_cast<uint8_t>(255 * color.red),
currentColor.g & static_cast<uint8_t>(255 * color.green),
currentColor.b & static_cast<uint8_t>(255 * color.blue)});
});
}};
}
LEDPattern LEDPattern::AtBrightness(double relativeBrightness) {
return LEDPattern{[relativeBrightness, self = *this](auto data, auto writer) {
self.ApplyTo(data, [&](int i, Color color) {
writer(i, Color{color.red * relativeBrightness,
color.green * relativeBrightness,
color.blue * relativeBrightness});
self.ApplyTo(data, [&](int i, wpi::util::Color color) {
writer(i, wpi::util::Color{color.red * relativeBrightness,
color.green * relativeBrightness,
color.blue * relativeBrightness});
});
}};
}
@@ -203,10 +212,10 @@ LEDPattern LEDPattern::AtBrightness(double relativeBrightness) {
// Static constants and functions
LEDPattern LEDPattern::Off() {
return LEDPattern::Solid(Color::kBlack);
return LEDPattern::Solid(wpi::util::Color::kBlack);
}
LEDPattern LEDPattern::Solid(const Color color) {
LEDPattern LEDPattern::Solid(const wpi::util::Color color) {
return LEDPattern{[=](auto data, auto writer) {
auto bufLen = data.size();
for (size_t i = 0; i < bufLen; i++) {
@@ -223,15 +232,16 @@ LEDPattern LEDPattern::ProgressMaskLayer(
size_t max = bufLen * progress;
for (size_t led = 0; led < max; led++) {
writer(led, Color::kWhite);
writer(led, wpi::util::Color::kWhite);
}
for (size_t led = max; led < bufLen; led++) {
writer(led, Color::kBlack);
writer(led, wpi::util::Color::kBlack);
}
}};
}
LEDPattern LEDPattern::Steps(std::span<const std::pair<double, Color>> steps) {
LEDPattern LEDPattern::Steps(
std::span<const std::pair<double, wpi::util::Color>> steps) {
if (steps.size() == 0) {
// no colors specified
return LEDPattern::Off();
@@ -247,12 +257,12 @@ LEDPattern LEDPattern::Steps(std::span<const std::pair<double, Color>> steps) {
// precompute relevant positions for this buffer so we don't need to do a
// check on every single LED index
std::unordered_map<int, Color> stopPositions;
std::unordered_map<int, wpi::util::Color> stopPositions;
for (auto step : steps) {
stopPositions[std::floor(step.first * bufLen)] = step.second;
}
auto currentColor = Color::kBlack;
auto currentColor = wpi::util::Color::kBlack;
for (size_t led = 0; led < bufLen; led++) {
if (stopPositions.contains(led)) {
currentColor = stopPositions[led];
@@ -263,12 +273,12 @@ LEDPattern LEDPattern::Steps(std::span<const std::pair<double, Color>> steps) {
}
LEDPattern LEDPattern::Steps(
std::initializer_list<std::pair<double, Color>> steps) {
std::initializer_list<std::pair<double, wpi::util::Color>> steps) {
return Steps(std::span{steps.begin(), steps.end()});
}
LEDPattern LEDPattern::Gradient(GradientType type,
std::span<const Color> colors) {
std::span<const wpi::util::Color> colors) {
if (colors.size() == 0) {
// no colors specified
return LEDPattern::Off();
@@ -300,16 +310,17 @@ LEDPattern LEDPattern::Gradient(GradientType type,
auto color = colors[colorIndex];
auto nextColor = colors[nextColorIndex];
Color gradientColor{wpi::util::Lerp(color.red, nextColor.red, t),
wpi::util::Lerp(color.green, nextColor.green, t),
wpi::util::Lerp(color.blue, nextColor.blue, t)};
wpi::util::Color gradientColor{
wpi::util::Lerp(color.red, nextColor.red, t),
wpi::util::Lerp(color.green, nextColor.green, t),
wpi::util::Lerp(color.blue, nextColor.blue, t)};
writer(led, gradientColor);
}
}};
}
LEDPattern LEDPattern::Gradient(GradientType type,
std::initializer_list<Color> colors) {
LEDPattern LEDPattern::Gradient(
GradientType type, std::initializer_list<wpi::util::Color> colors) {
return Gradient(type, std::span{colors.begin(), colors.end()});
}
@@ -318,7 +329,7 @@ LEDPattern LEDPattern::Rainbow(int saturation, int value) {
auto bufLen = data.size();
for (size_t led = 0; led < bufLen; led++) {
int hue = ((led * 180) / bufLen) % 180;
writer(led, Color::FromHSV(hue, saturation, value));
writer(led, wpi::util::Color::FromHSV(hue, saturation, value));
}
}};
}

View File

@@ -15,7 +15,7 @@ static constexpr std::string_view kBackgroundColor = "backgroundColor";
static constexpr std::string_view kDims = "dims";
Mechanism2d::Mechanism2d(double width, double height,
const Color8Bit& backgroundColor)
const wpi::util::Color8Bit& backgroundColor)
: m_width{width}, m_height{height} {
SetBackgroundColor(backgroundColor);
}
@@ -30,7 +30,7 @@ MechanismRoot2d* Mechanism2d::GetRoot(std::string_view name, double x,
return &it->second;
}
void Mechanism2d::SetBackgroundColor(const Color8Bit& color) {
void Mechanism2d::SetBackgroundColor(const wpi::util::Color8Bit& color) {
m_color = color.HexString();
if (m_colorPub) {
m_colorPub.Set(m_color);

View File

@@ -17,7 +17,7 @@ static constexpr std::string_view kSmartDashboardType = "line";
MechanismLigament2d::MechanismLigament2d(std::string_view name, double length,
wpi::units::degree_t angle,
double lineWeight,
const wpi::Color8Bit& color)
const wpi::util::Color8Bit& color)
: MechanismObject2d{name},
m_length{length},
m_angle{angle.value()},
@@ -42,7 +42,7 @@ void MechanismLigament2d::UpdateEntries(
m_lengthEntry.Set(m_length);
}
void MechanismLigament2d::SetColor(const Color8Bit& color) {
void MechanismLigament2d::SetColor(const wpi::util::Color8Bit& color) {
std::scoped_lock lock(m_mutex);
wpi::util::format_to_n_c_str(m_color, sizeof(m_color), "#{:02X}{:02X}{:02X}",
@@ -69,7 +69,7 @@ void MechanismLigament2d::SetLineWeight(double lineWidth) {
}
}
Color8Bit MechanismLigament2d::GetColor() {
wpi::util::Color8Bit MechanismLigament2d::GetColor() {
std::scoped_lock lock(m_mutex);
if (m_colorEntry) {
auto color = m_colorEntry.Get();

View File

@@ -79,7 +79,7 @@ class AddressableLED {
*
* @param color The color of the LED
*/
void SetLED(const Color& color) {
void SetLED(const wpi::util::Color& color) {
this->r = color.red * 255;
this->g = color.green * 255;
this->b = color.blue * 255;
@@ -90,7 +90,7 @@ class AddressableLED {
*
* @param color The color of the LED
*/
void SetLED(const Color8Bit& color) {
void SetLED(const wpi::util::Color8Bit& color) {
this->r = color.red;
this->g = color.green;
this->b = color.blue;

View File

@@ -41,12 +41,13 @@ class LEDPattern {
size_t m_size;
};
explicit LEDPattern(std::function<void(wpi::LEDPattern::LEDReader,
std::function<void(int, wpi::Color)>)>
impl);
explicit LEDPattern(
std::function<void(wpi::LEDPattern::LEDReader,
std::function<void(int, wpi::util::Color)>)>
impl);
void ApplyTo(LEDReader reader,
std::function<void(int, wpi::Color)> writer) const;
std::function<void(int, wpi::util::Color)> writer) const;
/**
* Writes the pattern to an LED buffer. Dynamic animations should be called
@@ -62,7 +63,7 @@ class LEDPattern {
* @param writer data writer for setting new LED colors on the LED strip
*/
void ApplyTo(std::span<wpi::AddressableLED::LEDData> data,
std::function<void(int, wpi::Color)> writer) const;
std::function<void(int, wpi::util::Color)> writer) const;
/**
* Writes the pattern to an LED buffer. Dynamic animations should be called
@@ -199,8 +200,8 @@ class LEDPattern {
/**
* Creates a pattern that plays this pattern overlaid on another. Anywhere
* this pattern sets an LED to off (or {@link wpi::Color::kBlack}), the base
* pattern will be displayed instead.
* this pattern sets an LED to off (or {@link wpi::util::Color::kBlack}), the
* base pattern will be displayed instead.
*
* @param base the base pattern to overlay on top of
* @return the combined overlay pattern
@@ -256,10 +257,10 @@ class LEDPattern {
*
* <pre>
* // Solid red, but at 50% brightness
* wpi::LEDPattern::Solid(wpi::Color::kRed).AtBrightness(0.5);
* wpi::LEDPattern::Solid(wpi::util::Color::kRed).AtBrightness(0.5);
*
* // Solid white, but at only 10% (i.e. ~0.5V)
* wpi::LEDPattern::Solid(frc:Color::kWhite).AtBrightness(0.1);
* wpi::LEDPattern::Solid(wpi::util::Color::kWhite).AtBrightness(0.1);
* </pre>
*
* @param relativeBrightness the multiplier to apply to all channels to modify
@@ -279,7 +280,7 @@ class LEDPattern {
* @param color the color to display
* @return the pattern
*/
static LEDPattern Solid(const Color color);
static LEDPattern Solid(const wpi::util::Color color);
/**
* Creates a pattern that works as a mask layer for {@link
@@ -296,8 +297,8 @@ class LEDPattern {
*
* <pre>
* wpi::LEDPattern basePattern =
* wpi::LEDPattern::Gradient(wpi::Color::kRed, wpi::Color::kBlue);
* wpi::LEDPattern progressPattern =
* wpi::LEDPattern::Gradient(wpi::util::Color::kRed,
* wpi::util::Color::kBlue); wpi::LEDPattern progressPattern =
* basePattern.Mask(wpi::LEDPattern::ProgressMaskLayer([&]() {
* return elevator.GetHeight() / elevator.MaxHeight();
* });
@@ -322,7 +323,8 @@ class LEDPattern {
* position along the LED strip
* @return a motionless step pattern
*/
static LEDPattern Steps(std::span<const std::pair<double, Color>> steps);
static LEDPattern Steps(
std::span<const std::pair<double, wpi::util::Color>> steps);
/**
* Display a set of colors in steps across the length of the LED strip. No
@@ -337,7 +339,7 @@ class LEDPattern {
* @return a motionless step pattern
*/
static LEDPattern Steps(
std::initializer_list<std::pair<double, Color>> steps);
std::initializer_list<std::pair<double, wpi::util::Color>> steps);
/** Types of gradients. */
enum GradientType {
@@ -368,7 +370,8 @@ class LEDPattern {
* @param colors the colors to display in the gradient
* @return a motionless gradient pattern
*/
static LEDPattern Gradient(GradientType type, std::span<const Color> colors);
static LEDPattern Gradient(GradientType type,
std::span<const wpi::util::Color> colors);
/**
* Creates a pattern that displays a non-animated gradient of colors across
@@ -384,7 +387,7 @@ class LEDPattern {
* @return a motionless gradient pattern
*/
static LEDPattern Gradient(GradientType type,
std::initializer_list<Color> colors);
std::initializer_list<wpi::util::Color> colors);
/**
* Creates an LED pattern that displays a rainbow across the color wheel. The
@@ -398,7 +401,7 @@ class LEDPattern {
private:
std::function<void(wpi::LEDPattern::LEDReader,
std::function<void(int, wpi::Color)>)>
std::function<void(int, wpi::util::Color)>)>
m_impl;
};
} // namespace wpi

View File

@@ -52,7 +52,7 @@ class Mechanism2d : public wpi::nt::NTSendable,
* @param backgroundColor the background color
*/
Mechanism2d(double width, double height,
const Color8Bit& backgroundColor = {0, 0, 32});
const wpi::util::Color8Bit& backgroundColor = {0, 0, 32});
/**
* Get or create a root in this Mechanism2d with the given name and
@@ -73,7 +73,7 @@ class Mechanism2d : public wpi::nt::NTSendable,
*
* @param color the new background color
*/
void SetBackgroundColor(const Color8Bit& color);
void SetBackgroundColor(const wpi::util::Color8Bit& color);
void InitSendable(wpi::nt::NTSendableBuilder& builder) override;

View File

@@ -27,21 +27,21 @@ class MechanismLigament2d : public MechanismObject2d {
public:
MechanismLigament2d(std::string_view name, double length,
wpi::units::degree_t angle, double lineWidth = 6,
const wpi::Color8Bit& color = {235, 137, 52});
const wpi::util::Color8Bit& color = {235, 137, 52});
/**
* Set the ligament color.
*
* @param color the color of the line
*/
void SetColor(const Color8Bit& color);
void SetColor(const wpi::util::Color8Bit& color);
/**
* Get the ligament color.
*
* @return the color of the line
*/
Color8Bit GetColor();
wpi::util::Color8Bit GetColor();
/**
* Set the ligament's length.

View File

@@ -210,8 +210,6 @@ Watchdog = "wpi/system/Watchdog.hpp"
# wpi/util
Alert = "wpi/util/Alert.hpp"
Color = "wpi/util/Color.hpp"
Color8Bit = "wpi/util/Color8Bit.hpp"
Preferences = "wpi/util/Preferences.hpp"
SensorUtil = "wpi/util/SensorUtil.hpp"

View File

@@ -39,6 +39,6 @@ classes:
SetHSV:
SetLED:
overloads:
const Color&:
const Color8Bit&:
const wpi::util::Color&:
const wpi::util::Color8Bit&:

View File

@@ -8,8 +8,8 @@ classes:
ApplyTo:
overloads:
std::span<wpi::AddressableLED::LEDData> [const]:
LEDReader, std::function<void (int, wpi::Color)> [const]:
std::span<wpi::AddressableLED::LEDData>, std::function<void (int, wpi::Color)> [const]:
LEDReader, std::function<void (int, wpi::util::Color)> [const]:
std::span<wpi::AddressableLED::LEDData>, std::function<void (int, wpi::util::Color)> [const]:
Reversed:
OffsetBy:
ScrollAtRelativeSpeed:
@@ -28,13 +28,13 @@ classes:
ProgressMaskLayer:
Steps:
overloads:
std::span<const std::pair<double, Color>>:
std::initializer_list<std::pair<double, Color>>:
std::span<const std::pair<double, wpi::util::Color>>:
std::initializer_list<std::pair<double, wpi::util::Color>>:
ignore: true
Gradient:
overloads:
GradientType, std::span<const Color>:
GradientType, std::initializer_list<Color>:
GradientType, std::span<const wpi::util::Color>:
GradientType, std::initializer_list<wpi::util::Color>:
ignore: true
Rainbow:
MapIndex:

View File

@@ -21,11 +21,11 @@ inline_code: |-
cls_MechanismObject2d
.def("appendLigament", [](MechanismObject2d *self,
std::string_view name, double length, wpi::units::degree_t angle,
double lineWidth, const wpi::Color8Bit& color) {
double lineWidth, const wpi::util::Color8Bit& color) {
return self->Append<MechanismLigament2d>(name, length, angle, lineWidth, color);
},
py::arg("name"), py::arg("length"), py::arg("angle"),
py::arg("lineWidth") = 6, py::arg("color") = wpi::Color8Bit{235, 137, 52},
py::arg("lineWidth") = 6, py::arg("color") = wpi::util::Color8Bit{235, 137, 52},
"Append a ligament node",
py::return_value_policy::reference_internal)
;

View File

@@ -20,11 +20,11 @@ inline_code: |-
.def("getName", [](MechanismRoot2d *self) { return self->GetName(); }, release_gil())
.def("appendLigament", [](MechanismRoot2d *self,
std::string_view name, double length, wpi::units::degree_t angle,
double lineWidth, const wpi::Color8Bit& color) {
double lineWidth, const wpi::util::Color8Bit& color) {
return self->Append<MechanismLigament2d>(name, length, angle, lineWidth, color);
},
py::arg("name"), py::arg("length"), py::arg("angle"),
py::arg("lineWidth") = 6, py::arg("color") = wpi::Color8Bit{235, 137, 52},
py::arg("lineWidth") = 6, py::arg("color") = wpi::util::Color8Bit{235, 137, 52},
release_gil(), "Append a ligament node",
py::return_value_policy::reference_internal)
;

View File

@@ -14,8 +14,6 @@ from ._wpilib import (
AnalogPotentiometer,
CAN,
CANStatus,
Color,
Color8Bit,
Compressor,
CompressorConfigType,
DSControlWord,
@@ -106,8 +104,6 @@ __all__ = [
"AnalogPotentiometer",
"CAN",
"CANStatus",
"Color",
"Color8Bit",
"Compressor",
"CompressorConfigType",
"DSControlWord",

File diff suppressed because it is too large Load Diff

View File

@@ -60,7 +60,7 @@ TEST(Mechanism2dTest, Ligament) {
wpi::MechanismRoot2d* root = mechanism.GetRoot("root", 1, 2);
wpi::MechanismLigament2d* ligament = root->Append<wpi::MechanismLigament2d>(
"ligament", 3, wpi::units::degree_t{90}, 1,
wpi::Color8Bit{255, 255, 255});
wpi::util::Color8Bit{255, 255, 255});
wpi::SmartDashboard::PutData("mechanism", &mechanism);
EXPECT_EQ(ligament->GetAngle(), angleEntry.GetDouble(0.0));
EXPECT_EQ(ligament->GetColor().HexString(), colorEntry.GetString(""));

View File

@@ -61,7 +61,9 @@ class Arm {
wpi::MechanismRoot2d* m_armBase = m_mech2d.GetRoot("ArmBase", 30, 30);
wpi::MechanismLigament2d* m_armTower =
m_armBase->Append<wpi::MechanismLigament2d>(
"Arm Tower", 30, -90_deg, 6, wpi::Color8Bit{wpi::Color::kBlue});
"Arm Tower", 30, -90_deg, 6,
wpi::util::Color8Bit{wpi::util::Color::kBlue});
wpi::MechanismLigament2d* m_arm = m_armBase->Append<wpi::MechanismLigament2d>(
"Arm", 30, m_armSim.GetAngle(), 6, wpi::Color8Bit{wpi::Color::kYellow});
"Arm", 30, m_armSim.GetAngle(), 6,
wpi::util::Color8Bit{wpi::util::Color::kYellow});
};

View File

@@ -66,7 +66,8 @@ class Robot : public wpi::TimedRobot {
m_root->Append<wpi::MechanismLigament2d>("elevator", 1, 90_deg);
wpi::MechanismLigament2d* m_wrist =
m_elevator->Append<wpi::MechanismLigament2d>(
"wrist", 0.5, 90_deg, 6, wpi::Color8Bit{wpi::Color::kPurple});
"wrist", 0.5, 90_deg, 6,
wpi::util::Color8Bit{wpi::util::Color::kPurple});
};
#ifndef RUNNING_WPILIB_TESTS

View File

@@ -8,6 +8,26 @@ load("//shared/bazel/rules/robotpy:semiwrap_tool_helpers.bzl", "scan_headers", "
def wpiutil_extension(srcs = [], header_to_dat_deps = [], extra_hdrs = [], includes = [], extra_pyi_deps = []):
WPIUTIL_HEADER_GEN = [
struct(
class_name = "Color",
yml_file = "semiwrap/Color.yml",
header_root = "$(execpath :robotpy-native-wpiutil.copy_headers)",
header_file = "$(execpath :robotpy-native-wpiutil.copy_headers)/wpi/util/Color.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::util::Color", "wpi__util__Color.hpp"),
],
),
struct(
class_name = "Color8Bit",
yml_file = "semiwrap/Color8Bit.yml",
header_root = "$(execpath :robotpy-native-wpiutil.copy_headers)",
header_file = "$(execpath :robotpy-native-wpiutil.copy_headers)/wpi/util/Color8Bit.hpp",
tmpl_class_names = [],
trampolines = [
("wpi::util::Color8Bit", "wpi__util__Color8Bit.hpp"),
],
),
struct(
class_name = "StackTrace",
yml_file = "semiwrap/StackTrace.yml",

View File

@@ -5,7 +5,6 @@
package org.wpilib.util;
import java.util.Objects;
import org.wpilib.math.util.MathUtil;
/**
* Represents colors.
@@ -197,6 +196,10 @@ public class Color {
return Math.clamp(Math.ceil(value * (1 << 12)) / (1 << 12), 0.0, 1.0);
}
private static int lerp(int a, int b, double t) {
return (int) (a + (b - a) * t);
}
// Helper methods
/**
@@ -369,10 +372,7 @@ public class Color {
* @return the interpolated color, packed in a 32-bit integer
*/
public static int lerpRGB(int r1, int g1, int b1, int r2, int g2, int b2, double t) {
return packRGB(
(int) MathUtil.lerp(r1, r2, t),
(int) MathUtil.lerp(g1, g2, t),
(int) MathUtil.lerp(b1, b2, t));
return packRGB(lerp(r1, r2, t), lerp(g1, g2, t), lerp(b1, b2, t));
}
/*

View File

@@ -11,12 +11,28 @@
#include <string_view>
#include <fmt/format.h>
#include <gcem.hpp>
#include "wpi/util/StringExtras.hpp"
#include "wpi/util/ct_string.hpp"
namespace wpi {
namespace wpi::util {
// replicate ceil from gcem to avoid gcem dependency here
namespace detail {
constexpr double ceil_int(double x, double x_whole) noexcept {
return (x_whole + ((x > 0) && (x > x_whole)));
}
constexpr double ceil(double x) noexcept {
if (std::is_constant_evaluated()) {
return ((x < 0.0 ? -x : x) >= 4503599627370496.
? x
: ceil_int(x, static_cast<double>(static_cast<int64_t>(x))));
} else {
return std::ceil(x);
}
}
} // namespace detail
/**
* Represents colors that can be used with Addressable LEDs.
@@ -906,7 +922,7 @@ class Color {
private:
static constexpr double roundAndClamp(double value) {
return std::clamp(gcem::ceil(value * (1 << 12)) / (1 << 12), 0.0, 1.0);
return std::clamp(detail::ceil(value * (1 << 12)) / (1 << 12), 0.0, 1.0);
}
};
@@ -1083,4 +1099,4 @@ inline constexpr Color Color::kWhiteSmoke{0.9607843f, 0.9607843f, 0.9607843f};
inline constexpr Color Color::kYellow{1.0f, 1.0f, 0.0f};
inline constexpr Color Color::kYellowGreen{0.6039216f, 0.8039216f, 0.19607843f};
} // namespace wpi
} // namespace wpi::util

View File

@@ -15,7 +15,7 @@
#include "wpi/util/StringExtras.hpp"
#include "wpi/util/ct_string.hpp"
namespace wpi {
namespace wpi::util {
/**
* Represents colors that can be used with Addressable LEDs.
@@ -131,4 +131,4 @@ class Color8Bit {
int blue = 0;
};
} // namespace wpi
} // namespace wpi::util

View File

@@ -67,6 +67,8 @@ depends = ["wpiutil-casters"]
[tool.semiwrap.extension_modules."wpiutil._wpiutil".headers]
# wpi
Color = "wpi/util/Color.hpp"
Color8Bit = "wpi/util/Color8Bit.hpp"
StackTrace = "wpi/util/StackTrace.hpp"
Synchronization = "wpi/util/Synchronization.h"
RawFrame = "wpi/util/RawFrame.h"

View File

@@ -2,7 +2,7 @@ extra_includes:
- pybind11/operators.h
classes:
wpi::Color:
wpi::util::Color:
force_type_casters:
- wpi::util::ct_string
attributes:
@@ -173,6 +173,12 @@ classes:
HexString:
operator==:
functions:
ceil_int:
ignore: true
ceil:
ignore: true
inline_code: |
cls_Color
.def("__hash__", [](Color *self) -> size_t {

View File

@@ -2,7 +2,7 @@ extra_includes:
- pybind11/operators.h
classes:
wpi::Color8Bit:
wpi::util::Color8Bit:
force_type_casters:
- wpi::util::ct_string
attributes:
@@ -32,7 +32,7 @@ classes:
inline_code: |
cls_Color8Bit
.def("toColor", [](const Color8Bit &self) -> wpi::Color {
.def("toColor", [](const Color8Bit &self) -> wpi::util::Color {
return self;
})
.def("__hash__", [](Color8Bit *self) -> size_t {

View File

@@ -2,6 +2,8 @@ from . import _init__wpiutil
# autogenerated by 'semiwrap create-imports wpiutil wpiutil._wpiutil'
from ._wpiutil import (
Color,
Color8Bit,
Sendable,
SendableBuilder,
SendableRegistry,
@@ -11,6 +13,8 @@ from ._wpiutil import (
)
__all__ = [
"Color",
"Color8Bit",
"Sendable",
"SendableBuilder",
"SendableRegistry",

View File

@@ -9,7 +9,7 @@
#include <gtest/gtest.h>
TEST(Color8BitTest, ConstructDefault) {
constexpr wpi::Color8Bit color;
constexpr wpi::util::Color8Bit color;
EXPECT_EQ(0, color.red);
EXPECT_EQ(0, color.green);
@@ -17,7 +17,7 @@ TEST(Color8BitTest, ConstructDefault) {
}
TEST(Color8BitTest, ConstructFromInts) {
constexpr wpi::Color8Bit color{255, 128, 64};
constexpr wpi::util::Color8Bit color{255, 128, 64};
EXPECT_EQ(255, color.red);
EXPECT_EQ(128, color.green);
@@ -25,7 +25,7 @@ TEST(Color8BitTest, ConstructFromInts) {
}
TEST(Color8BitTest, ConstructFromColor) {
constexpr wpi::Color8Bit color{wpi::Color{255, 128, 64}};
constexpr wpi::util::Color8Bit color{wpi::util::Color{255, 128, 64}};
EXPECT_EQ(255, color.red);
EXPECT_EQ(128, color.green);
@@ -33,24 +33,24 @@ TEST(Color8BitTest, ConstructFromColor) {
}
TEST(Color8BitTest, ConstructFromHexString) {
constexpr wpi::Color8Bit color{"#FF8040"};
constexpr wpi::util::Color8Bit color{"#FF8040"};
EXPECT_EQ(255, color.red);
EXPECT_EQ(128, color.green);
EXPECT_EQ(64, color.blue);
// No leading #
EXPECT_THROW(wpi::Color8Bit{"112233"}, std::invalid_argument);
EXPECT_THROW(wpi::util::Color8Bit{"112233"}, std::invalid_argument);
// Too long
EXPECT_THROW(wpi::Color8Bit{"#11223344"}, std::invalid_argument);
EXPECT_THROW(wpi::util::Color8Bit{"#11223344"}, std::invalid_argument);
// Invalid hex characters
EXPECT_THROW(wpi::Color8Bit{"#$$$$$$"}, std::invalid_argument);
EXPECT_THROW(wpi::util::Color8Bit{"#$$$$$$"}, std::invalid_argument);
}
TEST(Color8BitTest, ImplicitConversionToColor) {
wpi::Color color = wpi::Color8Bit{255, 128, 64};
wpi::util::Color color = wpi::util::Color8Bit{255, 128, 64};
EXPECT_NEAR(1.0, color.red, 1e-2);
EXPECT_NEAR(0.5, color.green, 1e-2);
@@ -58,13 +58,13 @@ TEST(Color8BitTest, ImplicitConversionToColor) {
}
TEST(Color8BitTest, ToHexString) {
constexpr wpi::Color8Bit color1{255, 128, 64};
constexpr wpi::util::Color8Bit color1{255, 128, 64};
EXPECT_EQ("#FF8040", color1.HexString());
// Ensure conversion to std::string works
[[maybe_unused]]
std::string str = color1.HexString();
wpi::Color8Bit color2{255, 128, 64};
wpi::util::Color8Bit color2{255, 128, 64};
EXPECT_EQ("#FF8040", color2.HexString());
}

View File

@@ -9,7 +9,7 @@
#include <gtest/gtest.h>
TEST(ColorTest, ConstructDefault) {
constexpr wpi::Color color;
constexpr wpi::util::Color color;
EXPECT_DOUBLE_EQ(0.0, color.red);
EXPECT_DOUBLE_EQ(0.0, color.green);
@@ -18,7 +18,7 @@ TEST(ColorTest, ConstructDefault) {
TEST(ColorTest, ConstructFromDoubles) {
{
constexpr wpi::Color color{1.0, 0.5, 0.25};
constexpr wpi::util::Color color{1.0, 0.5, 0.25};
EXPECT_NEAR(1.0, color.red, 1e-2);
EXPECT_NEAR(0.5, color.green, 1e-2);
@@ -26,7 +26,7 @@ TEST(ColorTest, ConstructFromDoubles) {
}
{
constexpr wpi::Color color{1.0, 0.0, 0.0};
constexpr wpi::util::Color color{1.0, 0.0, 0.0};
// Check for exact match to ensure round-and-clamp is correct
EXPECT_EQ(1.0, color.red);
@@ -36,7 +36,7 @@ TEST(ColorTest, ConstructFromDoubles) {
}
TEST(ColorTest, ConstructFromInts) {
constexpr wpi::Color color{255, 128, 64};
constexpr wpi::util::Color color{255, 128, 64};
EXPECT_NEAR(1.0, color.red, 1e-2);
EXPECT_NEAR(0.5, color.green, 1e-2);
@@ -44,46 +44,50 @@ TEST(ColorTest, ConstructFromInts) {
}
TEST(ColorTest, FromHexString) {
constexpr wpi::Color color = wpi::Color::FromString("#FF8040");
constexpr wpi::util::Color color = wpi::util::Color::FromString("#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(wpi::Color::FromString("112233"), std::invalid_argument);
EXPECT_THROW(wpi::util::Color::FromString("112233"), std::invalid_argument);
// Too long
EXPECT_THROW(wpi::Color::FromString("#11223344"), std::invalid_argument);
EXPECT_THROW(wpi::util::Color::FromString("#11223344"),
std::invalid_argument);
// Invalid hex characters
EXPECT_THROW(wpi::Color::FromString("#$$$$$$"), std::invalid_argument);
EXPECT_THROW(wpi::util::Color::FromString("#$$$$$$"), std::invalid_argument);
}
TEST(ColorTest, FromRGBString) {
constexpr wpi::Color color = wpi::Color::FromString("rgb(255, 128, 64)");
constexpr wpi::util::Color color =
wpi::util::Color::FromString("rgb(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);
// Missing rgb()
EXPECT_THROW(wpi::Color::FromString("255, 128, 64"), std::invalid_argument);
EXPECT_THROW(wpi::util::Color::FromString("255, 128, 64"),
std::invalid_argument);
// Too few components
EXPECT_THROW(wpi::Color::FromString("rgb(255, 128)"), std::invalid_argument);
EXPECT_THROW(wpi::util::Color::FromString("rgb(255, 128)"),
std::invalid_argument);
// Too many components
EXPECT_THROW(wpi::Color::FromString("rgb(255, 128, 64, 32)"),
EXPECT_THROW(wpi::util::Color::FromString("rgb(255, 128, 64, 32)"),
std::invalid_argument);
// Non-integer component
EXPECT_THROW(wpi::Color::FromString("rgb(255, abc, 64)"),
EXPECT_THROW(wpi::util::Color::FromString("rgb(255, abc, 64)"),
std::invalid_argument);
}
TEST(ColorTest, FromHSV) {
constexpr wpi::Color color = wpi::Color::FromHSV(90, 128, 64);
constexpr wpi::util::Color color = wpi::util::Color::FromHSV(90, 128, 64);
EXPECT_DOUBLE_EQ(0.125732421875, color.red);
EXPECT_DOUBLE_EQ(0.251220703125, color.green);
@@ -91,13 +95,13 @@ TEST(ColorTest, FromHSV) {
}
TEST(ColorTest, ToHexString) {
constexpr wpi::Color color1{255, 128, 64};
constexpr wpi::util::Color color1{255, 128, 64};
EXPECT_EQ("#FF8040", color1.HexString());
// Ensure conversion to std::string works
[[maybe_unused]]
std::string str = color1.HexString();
wpi::Color color2{255, 128, 64};
wpi::util::Color color2{255, 128, 64};
EXPECT_EQ("#FF8040", color2.HexString());
}