To illustrate some algorithmic problems I found while writing this code review this code review, I needed to include a live demonstration, which I implemented using a Stack Snippet with Angular.js.
To illustrate some algorithmic problems I found while writing this code review, I needed to include a live demonstration, which I implemented using a Stack Snippet with Angular.js.
To illustrate some algorithmic problems I found while writing this code review, I needed to include a live demonstration, which I implemented using a Stack Snippet with Angular.js.
- 145.5k
- 22
- 190
- 478
WhileTo illustrate some algorithmic problems I found while writing this code review, I found it necessaryneeded to include a live demonstration to illustrate the problems I found, which I implemented using a Stack Snippet with Angular.js.
The demonstration takes a color input and a scalar parameter, and produces two read-only color outputs. The "Original" output is known to behave erratically (which is the point of the demo).
I also think that the CSS is sloppy and not robust to various sizing conditions. Recommendations in that area would also be appreciated.
While writing this code review, I found it necessary to include a live demonstration to illustrate the problems I found, which I implemented using a Stack Snippet with Angular.js.
The demonstration takes a color input and a scalar parameter, and produces two read-only color outputs.
To illustrate some algorithmic problems I found while writing this code review, I needed to include a live demonstration, which I implemented using a Stack Snippet with Angular.js.
The demonstration takes a color input and a scalar parameter, and produces two read-only color outputs. The "Original" output is known to behave erratically (which is the point of the demo).
I also think that the CSS is sloppy and not robust to various sizing conditions. Recommendations in that area would also be appreciated.
- 145.5k
- 22
- 190
- 478
var ColorTweakerCtrl = function ColorTweakerCtrl($scope) {
$scope.inColor = new RGBColor(234, 150, 0);
$scope.diff = 127;
$scope.tweak = function tweak() {
$scope.inColor.r = Math.round($scope.inColor.r);
$scope.inColor.g = Math.round($scope.inColor.g);
$scope.inColor.b = Math.round($scope.inColor.b);
$scope.diff = Math.round($scope.diff);
$scope.outColor = tweakColor($scope.inColor, $scope.diff);
$scope.newColor = newTweakColor($scope.inColor, $scope.diff);
};
$scope.tweak();
};
function RGBColor(r, g, b) {
this.r = r; this.g = g; this.b = b;
}
RGBColor.prototype.toString = function() {
return 'rgb(' + Math.round(this.r) + ',' + Math.round(this.g) + ',' + Math.round(this.b) + ')';
};
/* Based on formulas from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm */
RGBColor.prototype.toHSV = function toHSV() {
var r = this.r / 255,
g = this.g / 255,
b = this.b / 255;
var cMax = Math.max(r, g, b),
cMin = Math.min(r, g, b);
var Δ = cMax - cMin;
var hue = 60 * ( (cMax == r) ? ((g - b) / Δ) % 6
: (cMax == g) ? ((b - r) / Δ) + 2
: ((r - g) / Δ) + 4 );
var sat = cMax == 0 ? 0 : Δ / cMax;
var val = cMax;
return new HSVColor((hue + 360) % 360, sat, val);
}
function HSVColor(h, s, v) {
this.h = h; this.s = s; this.v = v;
}
HSVColor.prototype.toString = function() {
return 'hsl(' + Math.round(this.h) + ',' + Math.round(100 * this.s) + '%,' + Math.round(100 * this.v) + '%)';
};
/* http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV */
HSVColor.prototype.toRGB = function toRGB() {
var c = this.v * this.s;
var h = this.h / 60;
var x = c * (1 - Math.abs(h % 2 - 1));
var r1 = (h < 1 || h >= 5) ? c
: (h < 2 || h >= 4) ? x : 0;
var g1 = (h >= 1 && h < 3) ? c
: (h >= 0 && h < 4) ? x : 0;
var b1 = (h >= 3 && h < 5) ? c
: (h >= 2 && h < 6) ? x : 0;
var m = this.v - c;
return new RGBColor(Math.round(255 * (r1 + m)),
Math.round(255 * (g1 + m)),
Math.round(255 * (b1 + m)));
};
var ColorTweakerCtrl = function ColorTweakerCtrl($scope) {
$scope.inColor = new RGBColor(234, 150, 0);
$scope.diff = 127;
$scope.tweak = function tweak() {
$scope.inColor.r = Math.round($scope.inColor.r);
$scope.inColor.g = Math.round($scope.inColor.g);
$scope.inColor.b = Math.round($scope.inColor.b);
$scope.diff = Math.round($scope.diff);
$scope.outColor = tweakColor($scope.inColor, $scope.diff);
$scope.newColor = newTweakColor($scope.inColor, $scope.diff);
};
$scope.tweak();
};
function tweakColor(aColor, aDiff) {
function intRange(value, min, max) {
return value < min ? min
: value > max ? max
: value;
}
var r = aColor.r,
g = aColor.g,
b = aColor.b;
var d = (r + g + b) / 3;
var dir = (d >= (256 / 2)) ? -aDiff : aDiff;
r = intRange(r + dir, 0, 255);
g = intRange(g + dir, 0, 255);
b = intRange(b + dir, 0, 255);
return new RGBColor(r, g, b);
}
function newTweakColor(aColor, aDiff) {
var hsv = aColor.toHSV();
var vAdj = (aDiff / 128) * (hsv.v - 0.5);
return (new HSVColor(hsv.h, hsv.s, hsv.v - vAdj)).toRGB();
}
fieldset {
display: inline-block;
width: 25%;
}
fieldset#tweak {
display: block;
width: 100%;
border-width: 0;
}
label {
display: block;
}
.swatch {
border: 1px solid grey;
width: 100%;
height: 1em;
}
input[type=range] {
width: 40%;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<form ng-controller="ColorTweakerCtrl">
<fieldset id="tweak">
<label>Diff
<input ng-model="diff" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="diff" type="number" min="0" max="255" ng-change="tweak()">
</label>
</fieldset>
<fieldset>
<legend>Input Color</legend>
<div class="swatch" style="background-color: {{ inColor.toString() }}"></div>
<label>R
<input ng-model="inColor.r" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.r" type="number" min="0" max="255" ng-change="tweak()">
</label>
<label>G
<input ng-model="inColor.g" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.g" type="number" min="0" max="255" ng-change="tweak()">
</label>
<label>B
<input ng-model="inColor.b" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.b" type="number" min="0" max="255" ng-change="tweak()">
</label>
</fieldset>
<fieldset>
<legend>Output (Original)</legend>
<div class="swatch" style="background-color: {{ outColor.toString() }}"></div>
<label>R
<input ng-model="outColor.r" type="range" min="0" max="255" disabled>
<input ng-model="outColor.r" type="number" min="0" max="255" disabled>
</label>
<label>G
<input ng-model="outColor.g" type="range" min="0" max="255" disabled>
<input ng-model="outColor.g" type="number" min="0" max="255" disabled>
</label>
<label>B
<input ng-model="outColor.b" type="range" min="0" max="255" disabled>
<input ng-model="outColor.b" type="number" min="0" max="255" disabled>
</label>
</fieldset>
<fieldset>
<legend>Output (Suggested)</legend>
<div class="swatch" style="background-color: {{ newColor.toString() }}"></div>
<label>R
<input ng-model="newColor.r" type="range" min="0" max="255" disabled>
<input ng-model="newColor.r" type="number" min="0" max="255" disabled>
</label>
<label>G
<input ng-model="newColor.g" type="range" min="0" max="255" disabled>
<input ng-model="newColor.g" type="number" min="0" max="255" disabled>
</label>
<label>B
<input ng-model="newColor.b" type="range" min="0" max="255" disabled>
<input ng-model="newColor.b" type="number" min="0" max="255" disabled>
</label>
</fieldset>
</form>
</div>
function RGBColor(r, g, b) {
this.r = r; this.g = g; this.b = b;
}
RGBColor.prototype.toString = function() {
return 'rgb(' + Math.round(this.r) + ',' + Math.round(this.g) + ',' + Math.round(this.b) + ')';
};
/* Based on formulas from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm */
RGBColor.prototype.toHSV = function toHSV() {
var r = this.r / 255,
g = this.g / 255,
b = this.b / 255;
var cMax = Math.max(r, g, b),
cMin = Math.min(r, g, b);
var Δ = cMax - cMin;
var hue = 60 * ( (cMax == r) ? ((g - b) / Δ) % 6
: (cMax == g) ? ((b - r) / Δ) + 2
: ((r - g) / Δ) + 4 );
var sat = cMax == 0 ? 0 : Δ / cMax;
var val = cMax;
return new HSVColor((hue + 360) % 360, sat, val);
}
function HSVColor(h, s, v) {
this.h = h; this.s = s; this.v = v;
}
HSVColor.prototype.toString = function() {
return 'hsl(' + Math.round(this.h) + ',' + Math.round(100 * this.s) + '%,' + Math.round(100 * this.v) + '%)';
};
/* http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV */
HSVColor.prototype.toRGB = function toRGB() {
var c = this.v * this.s;
var h = this.h / 60;
var x = c * (1 - Math.abs(h % 2 - 1));
var r1 = (h < 1 || h >= 5) ? c
: (h < 2 || h >= 4) ? x : 0;
var g1 = (h >= 1 && h < 3) ? c
: (h >= 0 && h < 4) ? x : 0;
var b1 = (h >= 3 && h < 5) ? c
: (h >= 2 && h < 6) ? x : 0;
var m = this.v - c;
return new RGBColor(Math.round(255 * (r1 + m)),
Math.round(255 * (g1 + m)),
Math.round(255 * (b1 + m)));
};
var ColorTweakerCtrl = function ColorTweakerCtrl($scope) {
$scope.inColor = new RGBColor(234, 150, 0);
$scope.diff = 127;
$scope.tweak = function tweak() {
$scope.inColor.r = Math.round($scope.inColor.r);
$scope.inColor.g = Math.round($scope.inColor.g);
$scope.inColor.b = Math.round($scope.inColor.b);
$scope.diff = Math.round($scope.diff);
$scope.outColor = tweakColor($scope.inColor, $scope.diff);
$scope.newColor = newTweakColor($scope.inColor, $scope.diff);
};
$scope.tweak();
};
function tweakColor(aColor, aDiff) {
function intRange(value, min, max) {
return value < min ? min
: value > max ? max
: value;
}
var r = aColor.r,
g = aColor.g,
b = aColor.b;
var d = (r + g + b) / 3;
var dir = (d >= (256 / 2)) ? -aDiff : aDiff;
r = intRange(r + dir, 0, 255);
g = intRange(g + dir, 0, 255);
b = intRange(b + dir, 0, 255);
return new RGBColor(r, g, b);
}
function newTweakColor(aColor, aDiff) {
var hsv = aColor.toHSV();
var vAdj = (aDiff / 128) * (hsv.v - 0.5);
return (new HSVColor(hsv.h, hsv.s, hsv.v - vAdj)).toRGB();
}
fieldset {
display: inline-block;
width: 25%;
}
fieldset#tweak {
display: block;
width: 100%;
border-width: 0;
}
label {
display: block;
}
.swatch {
border: 1px solid grey;
width: 100%;
height: 1em;
}
input[type=range] {
width: 40%;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<form ng-controller="ColorTweakerCtrl">
<fieldset id="tweak">
<label>Diff
<input ng-model="diff" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="diff" type="number" min="0" max="255" ng-change="tweak()">
</label>
</fieldset>
<fieldset>
<legend>Input Color</legend>
<div class="swatch" style="background-color: {{ inColor.toString() }}"></div>
<label>R
<input ng-model="inColor.r" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.r" type="number" min="0" max="255" ng-change="tweak()">
</label>
<label>G
<input ng-model="inColor.g" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.g" type="number" min="0" max="255" ng-change="tweak()">
</label>
<label>B
<input ng-model="inColor.b" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.b" type="number" min="0" max="255" ng-change="tweak()">
</label>
</fieldset>
<fieldset>
<legend>Output (Original)</legend>
<div class="swatch" style="background-color: {{ outColor.toString() }}"></div>
<label>R
<input ng-model="outColor.r" type="range" min="0" max="255" disabled>
<input ng-model="outColor.r" type="number" min="0" max="255" disabled>
</label>
<label>G
<input ng-model="outColor.g" type="range" min="0" max="255" disabled>
<input ng-model="outColor.g" type="number" min="0" max="255" disabled>
</label>
<label>B
<input ng-model="outColor.b" type="range" min="0" max="255" disabled>
<input ng-model="outColor.b" type="number" min="0" max="255" disabled>
</label>
</fieldset>
<fieldset>
<legend>Output (Suggested)</legend>
<div class="swatch" style="background-color: {{ newColor.toString() }}"></div>
<label>R
<input ng-model="newColor.r" type="range" min="0" max="255" disabled>
<input ng-model="newColor.r" type="number" min="0" max="255" disabled>
</label>
<label>G
<input ng-model="newColor.g" type="range" min="0" max="255" disabled>
<input ng-model="newColor.g" type="number" min="0" max="255" disabled>
</label>
<label>B
<input ng-model="newColor.b" type="range" min="0" max="255" disabled>
<input ng-model="newColor.b" type="number" min="0" max="255" disabled>
</label>
</fieldset>
</form>
</div>
var ColorTweakerCtrl = function ColorTweakerCtrl($scope) {
$scope.inColor = new RGBColor(234, 150, 0);
$scope.diff = 127;
$scope.tweak = function tweak() {
$scope.inColor.r = Math.round($scope.inColor.r);
$scope.inColor.g = Math.round($scope.inColor.g);
$scope.inColor.b = Math.round($scope.inColor.b);
$scope.diff = Math.round($scope.diff);
$scope.outColor = tweakColor($scope.inColor, $scope.diff);
$scope.newColor = newTweakColor($scope.inColor, $scope.diff);
};
$scope.tweak();
};
function RGBColor(r, g, b) {
this.r = r; this.g = g; this.b = b;
}
RGBColor.prototype.toString = function() {
return 'rgb(' + Math.round(this.r) + ',' + Math.round(this.g) + ',' + Math.round(this.b) + ')';
};
/* Based on formulas from http://www.rapidtables.com/convert/color/rgb-to-hsv.htm */
RGBColor.prototype.toHSV = function toHSV() {
var r = this.r / 255,
g = this.g / 255,
b = this.b / 255;
var cMax = Math.max(r, g, b),
cMin = Math.min(r, g, b);
var Δ = cMax - cMin;
var hue = 60 * ( (cMax == r) ? ((g - b) / Δ) % 6
: (cMax == g) ? ((b - r) / Δ) + 2
: ((r - g) / Δ) + 4 );
var sat = cMax == 0 ? 0 : Δ / cMax;
var val = cMax;
return new HSVColor((hue + 360) % 360, sat, val);
}
function HSVColor(h, s, v) {
this.h = h; this.s = s; this.v = v;
}
HSVColor.prototype.toString = function() {
return 'hsl(' + Math.round(this.h) + ',' + Math.round(100 * this.s) + '%,' + Math.round(100 * this.v) + '%)';
};
/* http://en.wikipedia.org/wiki/HSL_and_HSV#From_HSV */
HSVColor.prototype.toRGB = function toRGB() {
var c = this.v * this.s;
var h = this.h / 60;
var x = c * (1 - Math.abs(h % 2 - 1));
var r1 = (h < 1 || h >= 5) ? c
: (h < 2 || h >= 4) ? x : 0;
var g1 = (h >= 1 && h < 3) ? c
: (h >= 0 && h < 4) ? x : 0;
var b1 = (h >= 3 && h < 5) ? c
: (h >= 2 && h < 6) ? x : 0;
var m = this.v - c;
return new RGBColor(Math.round(255 * (r1 + m)),
Math.round(255 * (g1 + m)),
Math.round(255 * (b1 + m)));
};
function tweakColor(aColor, aDiff) {
function intRange(value, min, max) {
return value < min ? min
: value > max ? max
: value;
}
var r = aColor.r,
g = aColor.g,
b = aColor.b;
var d = (r + g + b) / 3;
var dir = (d >= (256 / 2)) ? -aDiff : aDiff;
r = intRange(r + dir, 0, 255);
g = intRange(g + dir, 0, 255);
b = intRange(b + dir, 0, 255);
return new RGBColor(r, g, b);
}
function newTweakColor(aColor, aDiff) {
var hsv = aColor.toHSV();
var vAdj = (aDiff / 128) * (hsv.v - 0.5);
return (new HSVColor(hsv.h, hsv.s, hsv.v - vAdj)).toRGB();
}
fieldset {
display: inline-block;
width: 25%;
}
fieldset#tweak {
display: block;
width: 100%;
border-width: 0;
}
label {
display: block;
}
.swatch {
border: 1px solid grey;
width: 100%;
height: 1em;
}
input[type=range] {
width: 40%;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
<form ng-controller="ColorTweakerCtrl">
<fieldset id="tweak">
<label>Diff
<input ng-model="diff" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="diff" type="number" min="0" max="255" ng-change="tweak()">
</label>
</fieldset>
<fieldset>
<legend>Input Color</legend>
<div class="swatch" style="background-color: {{ inColor.toString() }}"></div>
<label>R
<input ng-model="inColor.r" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.r" type="number" min="0" max="255" ng-change="tweak()">
</label>
<label>G
<input ng-model="inColor.g" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.g" type="number" min="0" max="255" ng-change="tweak()">
</label>
<label>B
<input ng-model="inColor.b" type="range" min="0" max="255" ng-change="tweak()">
<input ng-model="inColor.b" type="number" min="0" max="255" ng-change="tweak()">
</label>
</fieldset>
<fieldset>
<legend>Output (Original)</legend>
<div class="swatch" style="background-color: {{ outColor.toString() }}"></div>
<label>R
<input ng-model="outColor.r" type="range" min="0" max="255" disabled>
<input ng-model="outColor.r" type="number" min="0" max="255" disabled>
</label>
<label>G
<input ng-model="outColor.g" type="range" min="0" max="255" disabled>
<input ng-model="outColor.g" type="number" min="0" max="255" disabled>
</label>
<label>B
<input ng-model="outColor.b" type="range" min="0" max="255" disabled>
<input ng-model="outColor.b" type="number" min="0" max="255" disabled>
</label>
</fieldset>
<fieldset>
<legend>Output (Suggested)</legend>
<div class="swatch" style="background-color: {{ newColor.toString() }}"></div>
<label>R
<input ng-model="newColor.r" type="range" min="0" max="255" disabled>
<input ng-model="newColor.r" type="number" min="0" max="255" disabled>
</label>
<label>G
<input ng-model="newColor.g" type="range" min="0" max="255" disabled>
<input ng-model="newColor.g" type="number" min="0" max="255" disabled>
</label>
<label>B
<input ng-model="newColor.b" type="range" min="0" max="255" disabled>
<input ng-model="newColor.b" type="number" min="0" max="255" disabled>
</label>
</fieldset>
</form>
</div>