1
0
Форкнуть 0

Adding hex value for colors and brightness to the light support (#9)

PR #9 [PBI #28283]

* Methods to allow colors input in hexadecimal value

* Adding brightness support

* Adding getitem() method to the mock CPX API

* Minor formatting changes

* Adressing PR's comments about naming and convention

* Correcting Python naming convention and re-writing function adapted from an online source
This commit is contained in:
Christellah 2019-06-11 13:40:25 -07:00 коммит произвёл GitHub
Родитель bcd5b6dbb1
Коммит 4d457bbfc0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 136 добавлений и 39 удалений

Просмотреть файл

@ -2,7 +2,7 @@ from .pixel import Pixel
class Express:
def __init__(self):
# Our actual state
# State in the Python process
self.state = {
'pixels': [
(0, 0, 0),
@ -16,6 +16,7 @@ class Express:
(0, 0, 0),
(0, 0, 0)
],
'brightness': 1.0,
'button_a': False,
'button_b': False,
}

Просмотреть файл

@ -11,20 +11,50 @@ class Pixel:
sys.stdout.flush()
def __setitem__(self, index, val):
self._state['pixels'][index] = self.extractPixelValue(val)
self._state['pixels'][index] = self.extract_pixel_value(val)
def extractPixelValue(self, val):
def __getitem__(self, index):
return self._state['pixels'][index]
def extract_pixel_value(self, val):
# Convert HEX to RGB
if type(val) is not tuple:
val = self.hex_to_rgb(val)
# Check it's a valid tuple
if len(val) != 3:
raise ValueError('The pixel value should be a tuple with 3 values.')
raise ValueError('The pixel value should be a tuple with 3 values between 0 and 255 or an hexadecimal color between #000000 and #FFFFFF.')
# Convert to int
val = tuple(map(int, val))
# Prevent negative values
if any(pix < 0 or pix > 255 for pix in val):
raise ValueError('The pixel value should be in range 0, 255.')
raise ValueError('The pixel value should between 0 and 255 or an hexadecimal color between #000000 and #FFFFFF.')
return val
def fill(self, val):
for index in range(len(self._state['pixels'])):
self._state['pixels'][index] = self.extractPixelValue(val)
self._state['pixels'][index] = self.extract_pixel_value(val)
def hex_to_rgb(self, hexValue):
hexValue = hexValue.lstrip('#')
if len(hexValue) != 6:
raise ValueError('The pixel hexadicimal color value should be in range #000000 and #FFFFFF.')
# Convert the string hex to rgb tuple
hexToRgbValue = []
for i in range(0, len(hexValue), 2):
hexColor = hexValue[i:i+2]
hexToRgbValue.append(int(hexColor, 16))
return tuple(hexToRgbValue)
@property
def brightness(self):
return self._state['brightness']
@brightness.setter
def brightness(self, brightness):
if not self.valid_brightness(brightness):
raise ValueError('The brightness value should be a number between 0 and 1.')
self._state['brightness'] = brightness
def valid_brightness(self, brightness):
return (type(brightness) is float or type(brightness) is int) and (brightness >= 0 and brightness <= 1)

Просмотреть файл

@ -3,6 +3,7 @@ import Cpx from "./cpx/Cpx";
interface IState {
pixels: Array<Array<number>>;
brightness: number;
button_a: any;
button_b: any;
}
@ -35,6 +36,7 @@ class Simulator extends React.Component<any, IState> {
[0, 0, 0],
[0, 0, 0]
],
brightness: 1.0,
button_a: false,
button_b: false
};
@ -59,7 +61,7 @@ class Simulator extends React.Component<any, IState> {
render() {
return (
<div>
<Cpx pixels={this.state.pixels} onClick={this.sendClickInfo} />
<Cpx pixels={this.state.pixels} brightness={this.state.brightness} onClick={this.sendClickInfo} />
</div>
);
}
@ -78,6 +80,7 @@ class Simulator extends React.Component<any, IState> {
[0, 0, 0],
[0, 0, 0]
],
brightness: 1.0,
button_a: false,
button_b: false
});

Просмотреть файл

@ -1,4 +1,3 @@
import * as React from "react";
import CPX_SVG from "./Cpx_svg";
import * as SvgStyle from "./Cpx_svg_style";
@ -7,6 +6,7 @@ import svg from "./Svg_utils";
interface IProps {
pixels: Array<Array<number>>;
brightness: number;
onClick: () => void;
}
@ -14,21 +14,21 @@ interface IProps {
/** Functional Component render */
const Cpx: React.FC<IProps> = props => {
let svgElement = window.document.getElementById('svg');
let svgElement = window.document.getElementById("cpx_svg");
if (svgElement)
initSvgStyle(svgElement);
if (svgElement) {
initSvgStyle(svgElement, props.brightness);
// Update Neopixels state
updateNeopixels(props);
}
// Update LEDs state
updateLEDs(props.pixels);
return (
CPX_SVG
);
};
}
const initSvgStyle = (svgElement: HTMLElement): void => {
const initSvgStyle = (svgElement: HTMLElement, brightness: number): void => {
let style: SVGStyleElement = svg.child(svgElement, "style", {}) as SVGStyleElement;
style.textContent = SvgStyle.SVG_STYLE;
@ -39,7 +39,7 @@ const initSvgStyle = (svgElement: HTMLElement): void => {
svg.child(glow, "feGaussianBlur", { stdDeviation: "5", result: "glow" });
let merge = svg.child(glow, "feMerge", {});
for (let i = 0; i < 3; ++i) {
svg.child(merge, "feMergeNode", { in: "glow" })
svg.child(merge, "feMergeNode", { in: "glow" });
}
let neopixelglow = svg.child(defs, "filter", { id: "neopixelglow", x: "-300%", y: "-300%", width: "600%", height: "600%" });
@ -48,34 +48,58 @@ const initSvgStyle = (svgElement: HTMLElement): void => {
svg.child(neopixelmerge, "feMergeNode", { in: "coloredBlur" });
svg.child(neopixelmerge, "feMergeNode", { in: "coloredBlur" });
svg.child(neopixelmerge, "feMergeNode", { in: "SourceGraphic" });
// Brightness
let neopixelfeComponentTransfer = svg.child(neopixelglow, "feComponentTransfer", {});
svg.child(neopixelfeComponentTransfer, "feFuncR", {id:"brightnessFilterR", type: "linear", slope: brightness});
svg.child(neopixelfeComponentTransfer, "feFuncG", {id:"brightnessFilterG", type: "linear", slope: brightness});
svg.child(neopixelfeComponentTransfer, "feFuncB", {id:"brightnessFilterB", type: "linear", slope: brightness});
}
const isLightOn = (pixValue: Array<number>): boolean => {
return ! pixValue.every((val) => { return (val == 0) });
}
const setLED = (pixValue: Array<number>, led: HTMLElement): void => {
if (isLightOn(pixValue)) {
led.style.fill = "rgb(" + pixValue.toString() + ")";
led.style.filter = `url(#neopixelglow)`;
}
else {
led.style.fill = "#c8c8c8";
led.style.filter = `none`;
}
};
const updateLEDs = (pixelsState: Array<Array<number>>): void => {
for (let i = 0; i < 10 ; i ++) {
let led = window.document.getElementById(`LED${i}`);
const updateNeopixels = (props: IProps): void => {
for (let i = 0; i < props.pixels.length; i ++) {
let led = window.document.getElementById(`NEOPIXEL_${i}`);
if (led) {
setLED(pixelsState[i], led);
setNeopixel(led, props.pixels[i], props.brightness);
}
}
}
const setNeopixel = (led: HTMLElement, pixValue: Array<number>, brightness: number): void => {
if (isLightOn(pixValue) && brightness > 0) {
// Neopixels style (Adapted from : https://github.com/microsoft/pxt-adafruit/blob/master/sim/visuals/board.ts)
changeBrightness("brightnessFilterR", brightness);
changeBrightness("brightnessFilterG", brightness);
changeBrightness("brightnessFilterB", brightness);
let [hue, sat, lum] = SvgStyle.rgbToHsl([pixValue[0], pixValue[1], pixValue[2]]);
let innerLum = Math.max(lum * SvgStyle.INTENSITY_FACTOR, SvgStyle.MIN_INNER_LUM);
lum = lum * 90 / 100 + 10; // at least 10% luminosity for the stroke
led.style.filter = `url(#neopixelglow)`;
led.style.fill = `hsl(${hue}, ${sat}%, ${innerLum}%)`;
led.style.stroke = `hsl(${hue}, ${sat}%, ${Math.min(lum * 3, SvgStyle.MAX_STROKE_LUM)}%)`
led.style.strokeWidth = `1.5`;
} else {
led.style.fill = SvgStyle.OFF_COLOR;
led.style.filter = `none`;
led.style.stroke = `none`;
}
};
const isLightOn = (pixValue: Array<number>): boolean => {
return ! pixValue.every((val) => { return (val == 0) });
}
const changeBrightness = (filterID: string, brightness: number): void => {
let brightnessFilter: HTMLElement | null = window.document.getElementById(filterID);
if (brightnessFilter)
brightnessFilter.setAttribute("slope", brightness.toString());
}
export default Cpx;

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -4,6 +4,45 @@
export const MB_WIDTH = 180.09375;
export const MB_HEIGHT = 179.22874;
export const OFF_COLOR = "#c8c8c8";
export const MAX_STROKE_LUM = 75;
export const MIN_INNER_LUM = 85;
export const INTENSITY_FACTOR = 1.3;
// Adapted from : https://github.com/microsoft/pxt/blob/master/pxtsim/simlib.ts
export function rgbToHsl(rgb: [number, number, number]): [number, number, number] {
let [r, g, b] = rgb;
let [r$, g$, b$] = [r / 255, g / 255, b / 255];
let cMin = Math.min(r$, g$, b$);
let cMax = Math.max(r$, g$, b$);
let cDelta = cMax - cMin;
let h: number = 0, s: number, l: number;
let maxAndMin = cMax + cMin;
// Luminosity
l = (maxAndMin / 2) * 100
if (cDelta === 0) {
s = 0; h = 0;
} else {
// Hue
if (cMax === r$)
h = 60 * (((g$ - b$) / cDelta) % 6);
else if (cMax === g$)
h = 60 * (((b$ - r$) / cDelta) + 2);
else if (cMax === b$)
h = 60 * (((r$ - g$) / cDelta) + 4);
// Saturation
if (l > 50)
s = 100 * (cDelta / (2 - maxAndMin));
else
s = 100 * (cDelta / maxAndMin);
}
return [Math.floor(h), Math.floor(s), Math.floor(l)];
}
export const SVG_STYLE = `
svg.sim {
box-sizing: border-box;