I have a function that returns a random color. I push these colors into an array. I don't want the colors to be repeated in the array. So I did this:
$scope.getRandomColor = function getRandomColor(arrayToCheckIfAlreadyContains) {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
//check if array contains the generated color
if(arrayToCheckIfAlreadyContains.indexOf(color) >= 0){
let nonRepeatColor = $scope.getRandomColor(arrayToCheckIfAlreadyContains);
console.log("color repeated", color, arrayToCheckIfAlreadyContains);
return nonRepeatColor;
}
return color;
}
But I don't know if this is efficient or will even work for sure. Also, it would be great if the colors are distinguishable. Sometimes I get colors that are almost same. How do I make sure that doesn't happen.
hsl can help you produce distinguishable colors. Try this.
function makeColor(colorNum, colors){
if (colors < 1) colors = 1;
// defaults to one color - avoid divide by zero
return colorNum * (360 / colors) % 360;
}
// This could be length of your array.
var totalDIVs = 20;
var totalColors = totalDIVs;
for (var i = 0; i < totalDIVs; i++){
var element = document.createElement('div');
document.body.appendChild(element);
var color = "hsl( " + makeColor(i, totalColors) + ", 100%, 50% )";
element.style.backgroundColor = color;
element.innerHTML = color;
}
You might consider using hsl instead of hex notation - pick a number between 0 and 359 for the initial color, then select the other colors such that they're equidistant. For example:
function getColors(num) {
const initialColor = Math.floor(Math.random() * 360);
const increment = 360 / num;
const hsls = [];
for (let i = 0; i < num; i++) {
hsls.push(Math.round((initialColor + (i * increment)) % 360));
}
return hsls;
}
function displayNew() {
container.innerHTML = '';
const hsls = getColors(input.value);
hsls.forEach((hsl) => {
const div = container.appendChild(document.createElement('div'));
div.style.backgroundColor = 'hsl(' + hsl + ', 100%, 50%)';
});
}
#container > div {
height: 30px;
}
<input id="input" onkeyup="displayNew()" type="number">
<div id="container"></div>
From your code I don't quite understand what you are doing if the color is already in the array: do you want to pick another random color until you find one color that is not in the array?
Anyways, since your second goal (distinguishable colors), I guess you need some extra work: every time you pick a random color, you need to check its similarity against ALL the colors in the array!
Something like the following:
getRandomColor = function getRandomColor(arrayToCheckIfAlreadyContains) {
let colorFound = true;
let letters = '0123456789ABCDEF';
do {
colorFound = true;
var randomColor = '#';
for (var i = 0; i < 6; i++) {
randomColor += letters[Math.floor(Math.random() * 16)];
}
arrayToCheckIfAlreadyContains.some(color => {
if (distanceBetweenColor(color, randomColor) < TRESHOLD) {
/* Branch taken when randomColor is too similar
* to an already existing color. */
colorFound = false;
return true;
}
return false;
});
} while (!colorFound);
}
Now, how implementing distanceBetweenColor()? You should use Delta-E algorithm: I suggest you to read this answer in SO: https://stackoverflow.com/a/15189004/6070423
EDIT: Notice the use of some instead of forEach: doing this, you stop the iteration as soon as you find a color that is too similar.
Related
I'm trying to make a program that takes a string, converts it to base64, and then to binary. It then takes the binary and changes the pixels a black pixel for 0 and a white pixel for 1.
I've gotten the pixel array to change to what I want, but it's not actually changing when I call updatePixels().
My goal is to then take the canvas and export it as an image.
My sketch:
let hw;
let input, button;
let binaryOut;
function setup() {
createCanvas(140,140);
input=createInput();
pixelDensity(1);
button = createButton("get image");
button.mousePressed(txtTo64ToBin)
loadPixels();
}
function txtTo64ToBin(){
str = input.value();
str = btoa(str);
let output = '';
str = str.split("")
for(let i=0;i<str.length;i++){
let base = str[i].charCodeAt(0).toString(2)
while(base.length < 8){
base = "0"+base;
}
output += base;
}
binaryOut = output;
console.log(binaryOut)
updateImage(binaryOut.split(''))
}
function updateImage(binArr){
hw = factors(binArr.length);
hw = hw[hw.length-1];
console.log(hw);
resizeCanvas(...hw,false)
pixels = []
for(let i=0; i<binArr.length; i++){
pixels[i*4] = map(binArr[i],0,1,0,255);
pixels[i*4+1] = map(binArr[i],0,1,0,255);
pixels[i*4+2] = map(binArr[i],0,1,0,255);
pixels[i*4+3] = 255;
}
console.log(pixels)
updatePixels() //here is the updatePixels function call
}
function draw() {
noLoop();
}
function factors(num) {
var half = Math.floor(num / 2),
arr = [],
i, j;
num % 2 === 0 ? (i = 2, j = 1) : (i = 3, j = 2);
for (i; i <= half; i += j) {
if(num % i === 0 && i <= num/i){
arr.push([i,num/i]);
}
}
return arr;
}
I'm very confused and any help would be much appreciated.
Please try to break your problem down into smaller steps and isolate the problem in a smaller example.
Here is an example sketch that shows the same problem:
let button;
function setup() {
createCanvas(140,140);
button = createButton("test");
button.mousePressed(updateImage);
loadPixels();
}
function updateImage(){
pixels = [];
for(let i=0; i < width * height; i++){
pixels[i*4] = 255;
pixels[i*4+1] = 0;
pixels[i*4+2] = 0;
pixels[i*4+3] = 255;
}
updatePixels();
}
function draw() {
noLoop();
}
We might expect this to turn the canvas red when we click the button, but it does not. See how this example is easier to play with, because we don't have to think about any of your logic?
Anyway, the problem is caused by this line:
pixels = [];
Take that line out, and the example program works.
My guess is this is because pixels is not a standard JavaScript array. From the reference:
Uint8ClampedArray containing the values for all the pixels in the display window.
...
Note that this is not a standard javascript array.
I've created a simple list from 0 to 10,000, where each li has a style type of upper-roman, a random background color as well as a child abbr to display hexadecimal string of the color.
When running the program I the list style changes from upper-roman to decimal after index 4000.
Can anyone explain to me what's happening here?
NOTE: Running the test takes a few seconds.
$(document).ready(readyHandler);
function readyHandler()
{
var $list = $("#MyList");
for (let i = 0; i < 10000; i++)
{
$list.append("<li><abbr title=\"\">Auto Insert " + i + "</abbr></li>");
}
colors = [];
for (let i = 0; i < $list.children().length; i++)
{
colors.push(getRandomColor());
}
$list.children().each(listItemsHandler);
}
function listItemsHandler(index, item)
{
var $target = $(item);
var color = colors[index];
$target.css("background-color", color);
$target.find("abbr").attr("title", color);
}
function getRandomColor()
{
var color = "#";
for (var i = 0; i < 3; i++)
{
var part = Math.round(Math.random() * 255).toString(16);
color += (part.length > 1) ? part : "0" + part;
}
return color;
}
#MyList
{
list-style-type: upper-roman;
margin-left: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ol id = "MyList"></ol>
At 4000, roman numerals begin to include the over-line which doesn't have an ASCII representation for display in plain text. For example, 4000 is IV with a line over the top. For this reason, there is no way for browser renderers to represent the numeral in char form as a <li> bullet.
The only way to represent this would be with an ADDITIONAL css property overline.
In short, it is not possible to represent numerals over 3999 as bullet items.
Can you explain me what is wrong with this code ?
var a = document.getElementById("id1").style.color;
a = "blue" is supposed to set the text color of the element with id1 to blue
function randomColor() {
var cArr = '1234567890ABCDEF'.split('');
var c = "#";
for (var i = 0; i < 6; i++) {
c += cArr[Math.floor(Math.random() * 16)]
}
a = c;
return c;
}
randomColor();
Looks pretty good to me but for sure it isn't because it isn't working.
Can you explain me what am I doing wrong ?
You are setting a to a primitive value. Therfore you can not assign the color.
function randomColor() {
var c = "#";
for (var i = 0; i < 6; i++) {
c += (Math.random() * 16 | 0).toString(16);
}
return c;
}
var a = document.getElementById("id1").style;
a.color = randomColor();
<h1 id="id1">stackoverflow</h1>
a = "blue" is supposed to set the text color of the element with id1 to blue
No that not true a contain the current color of the element id1.
To set the color you could store the element in variable then after that set the blue color to it like following :
var a = document.getElementById("id1");
a.style.color = "blue";
Hope this helps.
Snippet
function randomColor() {
var cArr = '1234567890ABCDEF'.split('');
var c = "#";
for (var i = 0; i < 6; i++) {
c += cArr[Math.floor(Math.random() * 16)]
}
return c;
}
var a = document.getElementById("id1");
a.style.color = randomColor();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1 id="id1">H1 element</h1>
To answer this question you need to understand how data is passed and stored in variables. You seem to know that objects are passed by reference, and if you store an object in several different variables you can modify one and it will be different in all of them.
However when you're storing an object's property in a variable, and that property has a plain data type such as number, string or boolean, that variable only stores the value not a reference. What you want to do is store the resulting element from your document.getElementById call in a el variable, then access its style.color within your function.
el.style.color = 'blue'
I have an Array of partial hex values that get random letters appended to them forming a full hex value.
These are then randomly applied to div layers effectively shading them different colors. However what I get currently is a "Matisse" effect instead of variations of one color.
If you force var color = setHex(); to var color = '#CC0'; in the getRandomColor function you will see the effect I am after.
I want to know why the "Matisse" effect is happening when I should only be passing one hex value. How do I stop this?
See example here: http://jsfiddle.net/fyQhg/
// Set Hex
function setHex() {
var hexArray = ['#CC0','#FF9','#339'];
var randomHex = hexArray[Math.floor(Math.random() * hexArray.length)];
document.getElementById('inner').innerHTML = randomHex;
return randomHex;
}
// random color
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = setHex();
for (var i = 0; i < 3; i++ ) {
color += letters[Math.round(Math.random() * 7)];
}
return color;
}
//ditribute random colors
function buttonClick() {
var i,j, colorblock = document.getElementsByClassName('shade');
for (i=0, j=colorblock.length; i<j; i++) {
colorblock[i].style.background = getRandomColor();
}
}
window.onload = buttonClick();
var base = setHex();
// random color
function getRandomColor() {
var letters = '0123456789ABCDEF'.split('');
var color = base;
for (var i = 0; i < 3; i++ ) {
color += letters[Math.round(Math.random() * 7)];
}
return color;
}
Declaring the base outside of the method works. Example
Explaination
In this method:
for (i=0, j=colorblock.length; i<j; i++) {
colorblock[i].style.background = getRandomColor();
}
You are calling getRandomColor() repeatedly in that loop. Therefore you are also calling setHex() repeatedly which is creating a new random color each time the loop loops.
So by moving setHex() outside the method that is inside that loop into base you are effectively only calling setHex() once per load.
Why?
Because function getRandomColor() calls setHex() which returns something else every time.
How to stop?
var color = setHex();
for (i=0, j=colorblock.length; i<j; i++) {
colorblock[i].style.background = getRandomColor(color);
}
}
and
function getRandomColor(color) {
var letters = '0123456789ABCDEF'.split('');
for (var i = 0; i < 3; i++ ) {
color += letters[Math.round(Math.random() * 7)];
}
return color;
}
Every time you call getRandomColor you are calling setHex and picking another random base color. Just set the base color once, store it in a variable and use it.
For example:
var hexBase;
function setHex() {
var hexArray = ['#CC0','#FF9','#339'];
if (!hexBase) {
hexBase = hexArray[Math.floor(Math.random() * hexArray.length)];
}
return hexBase;
}
Fiddle
It seems you were close to this solution because you were storing the result of setHex in a div, but you never checked it again. Also, you shouldn't need to store things in the DOM when you can just store them in JavaScript.
Finally, if you want to avoid the global variable, you can wrap the whole thing in a function (e.g. your onload function or a IIFE).
I have created an array of color values which represents a smooth transition of colors from red to blue.
Now I want this array to take you from red to blue and back again. The obvious solution is to append the reverse of the array to the array.
I have written code to do it, but it isn't working as I understand it should. Instead, it's creating the reversed array, repeated. Instead of "Red to Blue, Blue to Red", it's going "Blue to Red, Blue To Red".
Clearly, there's some behavior of arrays in javascript that I haven't grasped yet.
What should I be doing?
My first attempt is this:
colors = colors.concat(colors.reverse());
Based on the first stackoverflow answer, I tried this:
var arrayCopy = colors;
arrayCopy.reverse();
colors = colors.concat(arrayCopy);
But this produces identical results!
For context, here's the surrounding code:
///////////////////////////////////////////////////////////
// Creating the array which takes you from Red to Blue
//
var colorSteps = 400;
var startColor = [255, 0, 0];
var endColor = [0, 127, 255];
var steps = new Array();
var j = 0;
for (j = 0; j < 3; ++j) {
steps[j] = (endColor[j] - startColor[j]) / colorSteps;
}
var colors = Array();
for (j = 0; j < colorSteps; ++j) {
colors[j] = [
Math.floor(startColor[0] + steps[0] * j),
Math.floor(startColor[1] + steps[1] * j),
Math.floor(startColor[2] + steps[2] * j)
];
}
////////////////////////////////////////////////////////
// Here's the bit where I'm trying to make it a mirror
// of itself!
//
// It ain't working
//
colors = colors.concat(colors.reverse());
///////////////////////////////////////////////////////
// Demonstrating what the colors are
//
j = 0;
var changeColorFunction = function () {
if (++j >= colors.length) {
j = 0;
}
var colorName = "rgb(" + colors[j][0] + ", " + colors[j][1] + ", " + colors[j][2] + ")";
debugText.style.background = colorName;
debugText.innerHTML = j;
}
setInterval(changeColorFunction, 10);
The problem with:
colors = colors.concat(colors.reverse());
... is that colors.reverse() mutates the colors array itself, meaning that you're appending a reversed array to an already-reversed array. Try this instead:
colors = colors.concat(colors.slice().reverse());
Copy your colors array to somewhere first. reverse changes the array itself, not merely returns a reverted one.
UPDATE
Code sample:
colors.concat(colors.slice(0).reverse());