pixijs very slow in mobile compared to css - performance

I'm testing PIXIjs for a simple 2D graphics, basically I'm sliding tiles with some background color and borders animation, plus I'm masking some parts of the layout.
While it works great in desktops it's really slower than the same slide+animations made with pure css in mobile devices (where by the way I'm using crosswalk+cordova so the browser is always the same)
For moving tiles and animating color I'm calling requestAnimationFrame for each tile and I've disabled PIXI's ticker:
ticker.autoStart = false;
ticker.stop();
This slowness could be due to a weaker GPU on mobiles? or is just about the way I use PIXI?
I'm not showing the full code because is quite long ~ 800 lines.
The following is the routine I use for each tile once a slide is captured:
const animateTileBorderAndText = (tileObj, steps, _color, radius, textSize, strokeThickness, _config) => {
let pixiTile = tileObj.tile;
let s = 0;
let graphicsData = pixiTile.graphicsData[0];
let shape = graphicsData.shape;
let textStyle = pixiTile.children[0].style;
let textInc = (textSize - textStyle.fontSize) / steps;
let strokeInc = (strokeThickness - textStyle.strokeThickness) / steps;
let prevColor = graphicsData.fillColor;
let color = _color !== null ? _color : prevColor;
let alpha = pixiTile.alpha;
let h = shape.height;
let w = shape.width;
let rad = shape.radius;
let radiusInc = (radius - rad) / steps;
let r = (prevColor & 0xFF0000) >> 16;
let g = (prevColor & 0x00FF00) >> 8;
let b = prevColor & 0x0000FF;
let rc = (color & 0xFF0000) >> 16;
let rg = (color & 0x00FF00) >> 8;
let rb = color & 0x0000FF;
let redStep = (rc - r) / steps;
let greenStep = (rg - g) / steps;
let blueStep = (rb - b) / steps;
let paintColor = prevColor;
let goPaint = color !== prevColor;
let animate = (t) => {
if (s === steps) {
textStyle.fontSize = textSize;
textStyle.strokeThickness = strokeThickness;
//pixiTile.tint = color;
if (!_config.SEMAPHORES.slide) {
_config.SEMAPHORES.slide = true;
PUBSUB.publish(_config.SLIDE_CODE, _config.torusModel.getData());
}
return true;
}
if (goPaint) {
r += redStep;
g += greenStep;
b += blueStep;
paintColor = (r << 16) + (g << 8) + b;
}
textStyle.fontSize += textInc;
textStyle.strokeThickness += strokeInc;
pixiTile.clear()
pixiTile.beginFill(paintColor, alpha)
pixiTile.drawRoundedRect(0, 0, h, w, rad + radiusInc * (s + 1))
pixiTile.endFill();
s++;
return requestAnimationFrame(animate);
};
return animate();
};
the above function is called after the following one, which is called for each tile to make it slide.
const slideSingleTile = (tileObj, delta, axe, conf, SEM, tilesMap) => {
let tile = tileObj.tile;
let steps = conf.animationSteps;
SEM.slide = false;
let s = 0;
let stepDelta = delta / steps;
let endPos = tile[axe] + delta;
let slide = (time) => {
if (s === steps) {
tile[axe] = endPos;
tileObj.resetPosition();
tilesMap[tileObj.row][tileObj.col] = tileObj;
return tileObj.onSlideEnd(axe == 'x' ? 0 : 2);
}
tile[axe] += stepDelta;
s++;
return requestAnimationFrame(slide);
};
return slide();
};
For each finger gesture a single column/row (of NxM matrix of tiles) is slided and animated using the above two functions.
It's the first time I use canvas.
I red that canvas is way faster then DOM animations and I red very good review of PIXIjs, so I believe I'm doing something wrong.
Can someone help?

In the end I'm a complete donk...
The issue is not with pixijs.
Basically I was forcing 60fps! The number of steps to complete the animation is set to 12 that implies 200ms animation at 60FPS (using requestAnimationFrame) but in low end devices its going to be obviously slower.
Css animation works with timing as parameter so it auto adapt FPS to devices hardware.
To solve the issue I'm adapting the number of steps during animations, basically if animations takes longer than 200ms I just reduce number of steps proportionally.
I hope this could be of help for each web developer used to css animation who have just started developing canvas.

Related

What's wrong with this nearest neighbor interpolation shader?

GPU.js converts a JS func into a shader. The following function knows this.thread.x as the current index being operated on, but it is ultimately working as a WebGL shader.
export default function(sprite, w, h, scale) {
var bufferWidth = w * 4;
var channel = this.thread.x % 4;
var thread = this.thread.x - channel;
var y = Math.round(this.thread.x / bufferWidth);
var x = (thread % bufferWidth) / 4;
var upscale = scale * 10;
var upscaleY = y * 10;
var upscaleX = x * 10;
var scaledY = Math.round(upscaleY / upscale);
var scaledX = Math.round(upscaleX / upscale);
var newIndex = scaledY * bufferWidth + scaledX * 4;
if (x <= w * scale && y <= h * scale) {
return sprite[newIndex + channel];
} else {
return 0;
}
}
This almost works, but rows become skipped completely, actually making the result shorter than it should, and lines where those missing rows travel up and down and left to right on the image as it's scaled over time.
You can see this effect here: https://enviziion.github.io/lost-worlds/
What's wrong with my algo? Ive tried tweaking rounding and all sorts of stuff but no luck.
Use Math.floor when computing y:
var y = Math.floor(thread / bufferWidth);
If you use Math.round then it will start rounding up to the next row halfway across the buffer, which will produce a weird discontinuity.
Mathematically, you should be able to get back thread.x from y * bufferWidth + x * 4, which works for floor but not round.

2D Circular search pattern

I need an algorithm to give me coordinates to the nearest cells (in order of distance) to another cell in a 2D grid. Its for a search algorithm that then checks those coordinates for all sorts of things for suitability. Anyways, so far I came up with this:
function testy(cx, cy, idx) {
var radius = Math.floor(Math.sqrt(idx / Math.PI));
var segment = Math.round(idx - (radius * Math.PI));
var angle = segment / radius;
var x = Math.round(cx + radius * Math.cos(angle));
var y = Math.round(cy + radius * Math.sin(angle));
return [x, y];
}
addEventListener("load", function() {
var canv = document.createElement("canvas");
document.body.appendChild(canv);
canv.width = 800;
canv.height = 600;
var ctx = canv.getContext("2d");
var scale = 5;
var idx = 0;
var idx_end = 10000;
var func = function() {
var xy = testy(0,0,idx++);
var x = xy[0] * scale + canv.width / 2;
var y = xy[1] * scale + canv.height / 2;
ctx.rect(x, y, scale, scale);
ctx.fill();
if (idx < idx_end) setTimeout(func, 0);
}
func();
});
but as you can tell, its kinda crap because it skips some cells. There's a few assumptions I'm making there:
That the circumference of a circle of a certain radius corresponds to the number of cells on the path of that circle. I didn't think that would be too great of a problem though since the actual number of cells in a radius should be lower than the circumference leading to duplication(which in small amounts is ok) but not exclusion(not ok).
That the radius of a circle by the n-th index specified would be slightly more than Math.floor(Math.sqrt(idx / Math.PI)) because each increase of 1 to the radius corresponds to 2 * Math.PI being added to the circumference of the circle. Again, should lead to slight duplication but no exclusion.
Other than that I have no idea what could be wrong with it, I fail at math any more complex than this so probably something to do with that.
Perhaps there is another algorithm like this already out there though? One that doesn't skip cells? Language doesn't really matter, I'm using js to prototype it but it can be whatever.
Instead of thinking about the full circle, think about a quadrant. Adapting that to the full circle later should be fairly easy. Use (0,0) as the center of the circle for convenience. So you want to list grid cells with x,y ≥ 0 in order of non-decreasing x² + y².
One useful data structure is a priority queue. It can be used to keep track of the next y value for every x value, and you can extract the one with minimal x² + y² easily.
q = empty priority queue, for easy access to element with minimal x²+y²
Insert (0,0) into queue
while queue is not empty:
remove minimal element from queue and call it (x,y)
insert (x,y+1) into queue unless y+1 is off canvas
if y = 0:
insert (x+1,0) into queue unless x+1 is off canvas
do whatever you want to do with (x,y)
So for a canvas of size n this will enumerate all the n² points, but the priority queue will only contain n elements at most. The whole loop runs in O(n² log(n)). And if you abort the loop eraly because you found what you were looking for, it gets cheaper still, in contrast to simply sorting all the points. Another benefit is that you can use integer arithmetic exclusively, so numeric errors won't be an issue. One drawback is that JavaScript does not come with a priority queue out of the box, but I'm sure you can find an implementation you can reuse, e.g. tiniqueue.
When doing full circle, you'd generate (−x,y) unless x=0, and likewise for (x,−y) and (−x,−y). You could exploit symmetry a bit more by only having the loop over ⅛ of the circle, i.e. not inserting (x,y+1) if x=y, and then also generating (y,x) as a separate point unless x=y. Difference in performance should be marginal for many use cases.
"use strict";
function distCompare(a, b) {
const a2 = a.x*a.x + a.y*a.y;
const b2 = b.x*b.x + b.y*b.y;
return a2 < b2 ? -1 : a2 > b2 ? 1 : 0;
}
// Yields points in the range -w <= x <= w and -h <= y <= h
function* aroundOrigin(w,h) {
const q = TinyQueue([{x:0, y:0}], distCompare);
while (q.length) {
const p = q.pop();
yield p;
if (p.x) yield {x:-p.x, y:p.y};
if (p.y) yield {x:p.x, y:-p.y};
if (p.x && p.y) yield {x:-p.x, y:-p.y};
if (p.y < h) q.push({x:p.x, y:p.y+1});
if (p.y == 0 && p.x < w) q.push({x:p.x + 1, y:0});
}
}
// Yields points around (cx,cy) in range 0 <= x < w and 0 <= y < h
function* withOffset(cx, cy, w, h) {
const delegate = aroundOrigin(
Math.max(cx, w - cx - 1), Math.max(cy, h - cy - 1));
for(let p of delegate) {
p = {x: p.x + cx, y: p.y + cy};
if (p.x >= 0 && p.x < w && p.y >= 0 && p.y < h) yield p;
}
}
addEventListener("load", function() {
const canv = document.createElement("canvas");
document.body.appendChild(canv);
const cw = 800, ch = 600;
canv.width = cw;
canv.height = ch;
const ctx = canv.getContext("2d");
const scale = 5;
const w = Math.ceil(cw / scale);
const h = Math.ceil(ch / scale);
const cx = w >> 1, cy = h >> 1;
const pointgen = withOffset(cx, cy, w, h);
let cntr = 0;
var func = function() {
const {value, done} = pointgen.next();
if (done) return;
if (cntr++ % 16 === 0) {
// lighten older parts so that recent activity is more visible
ctx.fillStyle = "rgba(255,255,255,0.01)";
ctx.fillRect(0, 0, cw, ch);
ctx.fillStyle = "rgb(0,0,0)";
}
ctx.fillRect(value.x * scale, value.y*scale, scale, scale);
setTimeout(func, 0);
}
func();
});
<script type="text/javascript">module={};</script>
<script src="https://cdn.rawgit.com/mourner/tinyqueue/54dc3eb1/index.js"></script>

SceneKit: orient one node toward another in 3Dspace

I need to orient one node to point its Z-axis at another node in 3D. Yeah, the perfect job for the LookAtConstraint. And for most of my work LookAt is fine. But when I apply LookAt to a particular node, I can no longer animate that node's translation with SCNAction. Picture a hydrogen atom leaving a molecule as it ionizes. The orientation is needed to properly rotate the bond (a cylinder) bewteen the hydrogen and an oxygen atom on the molecule.
I can orient the bond FROM the oxygen TO the hydrogen and animate. But this disorients most of the other bonds which were getting by just fine with LookAt's.
I gave this a mighty try before realizing it answers a somewhat different question:
Calculate rotations to look at a 3D point?
I had a similar issue with a project. What I eventually realized was that I need to use multiple constraints. One for translation (movement) and the other using the look at constraint.
I would move the object and then apply the look at constraint; in this case, it was a camera following an objects being moved using actions. Code snippet follows:
let targetNodeConstraint = SCNLookAtConstraint(target: someObject)
targetNodeConstraint.gimbalLockEnabled = true
let followObjectConstraint = SCNTransformConstraint(inWorldSpace: true, withBlock: { (node, matrix) -> SCNMatrix4 in
let transformMatrix = SCNMatrix4MakeTranslation(
self.someObject.position.x - 1.0,
self.someObject.position.y, self.someObject.position.z + 1.0)
return transformMatrix
})
// Position the object behind the other object & rotate it to
roadCamera.constraints = [followObjectConstraint, targetNodeConstraint]
The important thing to note is the order in which the constraints are added to the object using an array. In the code above, I am ignoring the current matrix before I apply a transform matrix (I should re-write this code someday)
The complete source code of this "experiment" is on GitHub as I try things out.
https://github.com/ManjitBedi/CubeTrip
Hopefully, this is helpful.
My solution here. Deal with situation that node continuously translate in space and should always toward a position.
#discardableResult
func yew(_ node:SCNNode, toPosition position:SCNVector3) -> Float
{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
forward = GLKVector3Normalize(GLKVector3Make(forward.x, 0, forward.z))
toWard = GLKVector3Normalize(GLKVector3Make(toWard.x, 0, toWard.z))
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var yew = acos(dotProduct)
if yew < 0 {
assert(false)
}
//toward is clockwise of forward
let isCW = GLKVector3CrossProduct(forward, toWard).y < 0
if isCW {
yew = -yew
}
eularAngle.y = yew
node.eulerAngles = SCNVector3Make(eularAngle.x + wrapperNode.eulerAngles.x,
eularAngle.y + wrapperNode.eulerAngles.y,
eularAngle.z + wrapperNode.eulerAngles.z)
return yew
}
#discardableResult
func pitch(_ node:SCNNode, toPosition position:SCNVector3) -> Float{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
forward = GLKVector3Normalize(forward)
toWard = GLKVector3Normalize(toWard)
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var pitch = acos(dotProduct)
//toward is clockwise of forward, if right vector of model and crossProfuct.x has same direction
let crossProduct = GLKVector3CrossProduct(forward, toWard)
let isCW = (crossProduct.x <= 0) != (tranform.m11 <= 0)
if isCW {
pitch = -pitch
}
eularAngle.x = pitch
node.eulerAngles = SCNVector3Make(eularAngle.x + node.eulerAngles.x,
eularAngle.y + node.eulerAngles.y,
eularAngle.z + node.eulerAngles.z)
return pitch
}
func orient(_ node:SCNNode, toPosition position:SCNVector3) {
self.yew(node, toPosition: position)
self.pitch(node, toPosition: position)
}

Path Tracing Shadowing Error

I really dont know what else do to to fix this problem.I have written a path tracer using explicit light sampling in c++ and I keep getting this weird really black shadows which I know is wrong.I have done everything to fix it but I still keep getting it,even on higher samples.What am I doing wrong ? Below is a image of the scene.
And The Radiance Main Code
RGB Radiance(Ray PixRay,std::vector<Primitive*> sceneObjects,int depth,std::vector<AreaLight> AreaLights,unsigned short *XI,int E)
{
int MaxDepth = 10;
if(depth > MaxDepth) return RGB();
double nearest_t = INFINITY;
Primitive* nearestObject = NULL;
for(int i=0;i<sceneObjects.size();i++)
{
double root = sceneObjects[i]->intersect(PixRay);
if(root > 0)
{
if(root < nearest_t)
{
nearest_t = root;
nearestObject = sceneObjects[i];
}
}
}
RGB EstimatedRadiance;
if(nearestObject)
{
EstimatedRadiance = nearestObject->getEmission() * E;
Point intersectPoint = nearestObject->intersectPoint(PixRay,nearest_t);
Vector intersectNormal = nearestObject->surfacePointNormal(intersectPoint).Normalize();
if(nearestObject->getBRDF().Type == 1)
{
for(int x=0;x<AreaLights.size();x++)
{
Point pointOnTriangle = RandomPointOnTriangle(AreaLights[x].shape,XI);
Vector pointOnTriangleNormal = AreaLights[x].shape.surfacePointNormal(pointOnTriangle).Normalize();
Vector LightDistance = (pointOnTriangle - intersectPoint).Normalize();
//Geometric Term
RGB Geometric_Term = GeometricTerm(intersectPoint,pointOnTriangle,sceneObjects);
//Lambertian BRDF
RGB LambertianBRDF = nearestObject->getColor() * (1. / M_PI);
//Emitted Light Power
RGB Emission = AreaLights[x].emission;
double MagnitudeOfXandY = (pointOnTriangle - intersectPoint).Magnitude() * (pointOnTriangle - intersectPoint).Magnitude();
RGB DirectLight = Emission * LambertianBRDF * Dot(intersectNormal,-LightDistance) *
Dot(pointOnTriangleNormal,LightDistance) * (1./MagnitudeOfXandY) * AreaLights[x].shape.Area() * Geometric_Term;
EstimatedRadiance = EstimatedRadiance + DirectLight;
}
//
Vector diffDir = CosWeightedRandHemiDirection(intersectNormal,XI);
Ray diffRay = Ray(intersectPoint,diffDir);
EstimatedRadiance = EstimatedRadiance + ( Radiance(diffRay,sceneObjects,depth+1,AreaLights,XI,0) * nearestObject->getColor() * (1. / M_PI) * M_PI );
}
//Mirror
else if(nearestObject->getBRDF().Type == 2)
{
Vector reflDir = PixRay.d-intersectNormal*2*Dot(intersectNormal,PixRay.d);
Ray reflRay = Ray(intersectPoint,reflDir);
return nearestObject->getColor() *Radiance(reflRay,sceneObjects,depth+1,AreaLights,XI,0);
}
}
return EstimatedRadiance;
}
I haven't debugged your code, so there may be any number of bugs of course, but I can give you some tips: First, go look at SmallPT, and see what it does that you don't. It's tiny but still quite easy to read.
From the look of it, it seems there are issues with either the sampling and/or gamma correction. The easiest one is gamma: when converting RGB intensity in the range 0..1 to RGB in the range 0..255, remember to always gamma correct. Use a gamma of 2.2
R = r^(1.0/gamma)
G = g^(1.0/gamma)
B = b^(1.0/gamma)
Having the wrong gamma will make any path traced image look bad.
Second: sampling. It's not obvious from the code how the sampling is weighted. I'm only familiar with Path Tracing using russian roulette sampling. With RR the radiance basically works like so:
if (depth > MaxDepth)
return RGB();
RGB color = mat.Emission;
// Russian roulette:
float survival = 1.0f;
float pContinue = material.Albedo();
survival = 1.0f / pContinue;
if (Rand.Next() > pContinue)
return color;
color += DirectIllumination(sceneIntersection);
color += Radiance(sceneIntersection, depth+1) * survival;
RR is basically a way of terminating rays at random, but still maintaining an unbiased estimate of the true radiance. Since it adds a weight to the indirect term, and the shadow and bottom of the speheres are only indirectly lit, I'd suspect that has something to do with it (if it isn't just the gamma).

Algorithm to randomly generate an aesthetically-pleasing color palette [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I'm looking for a simple algorithm to generate a large number of random, aesthetically pleasing colors. So no crazy neon colors, colors reminiscent of feces, etc.
I've found solutions to this problem but they rely on alternative color palettes than RGB.
I would rather just use straight RGB than mapping back and forth. These other solutions also can at most generate only 32 or so pleasing random colors.
Any ideas would be great.
You could average the RGB values of random colors with those of a constant color:
(example in Java)
public Color generateRandomColor(Color mix) {
Random random = new Random();
int red = random.nextInt(256);
int green = random.nextInt(256);
int blue = random.nextInt(256);
// mix the color
if (mix != null) {
red = (red + mix.getRed()) / 2;
green = (green + mix.getGreen()) / 2;
blue = (blue + mix.getBlue()) / 2;
}
Color color = new Color(red, green, blue);
return color;
}
Mixing random colors with white (255, 255, 255) creates neutral pastels by increasing the lightness while keeping the hue of the original color. These randomly generated pastels usually go well together, especially in large numbers.
Here are some pastel colors generated using the above method:
You could also mix the random color with a constant pastel, which results in a tinted set of neutral colors. For example, using a light blue creates colors like these:
Going further, you could add heuristics to your generator that take into account complementary colors or levels of shading, but it all depends on the impression you want to achieve with your random colors.
Some additional resources:
http://en.wikipedia.org/wiki/Color_theory
http://en.wikipedia.org/wiki/Complementary_color
I would use a color wheel and given a random position you could add the golden angle (137,5 degrees)
http://en.wikipedia.org/wiki/Golden_angle
in order to get different colours each time that do not overlap.
Adjusting the brightness for the color wheel you could get also different bright/dark color combinations.
I've found this blog post that explains really well the problem and the solution using the golden ratio.
http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
UPDATE: I've just found this other approach:
It's called RYB(red, yellow, blue) method and it's described in this paper:
http://threekings.tk/mirror/ryb_TR.pdf
as "Paint Inspired Color Compositing".
The algorithm generates the colors and each new color is chosen to maximize its euclidian distance to the previously selected ones.
Here you can find a a good implementation in javascript:
http://afriggeri.github.com/RYB/
UPDATE 2:
The Sciences Po Medialb have just released a tool called "I want Hue" that generate color palettes for data scientists. Using different color spaces and generating the palettes by using k-means clustering or force vectors ( repulsion graphs) The results from those methods are very good, they show the theory and an implementation in their web page.
http://tools.medialab.sciences-po.fr/iwanthue/index.php
In javascript:
function pastelColors(){
var r = (Math.round(Math.random()* 127) + 127).toString(16);
var g = (Math.round(Math.random()* 127) + 127).toString(16);
var b = (Math.round(Math.random()* 127) + 127).toString(16);
return '#' + r + g + b;
}
Saw the idea here: http://blog.functionalfun.net/2008/07/random-pastel-colour-generator.html
Converting to another palette is a far superior way to do this. There's a reason they do that: other palettes are 'perceptual' - that is, they put similar seeming colors close together, and adjusting one variable changes the color in a predictable manner. None of that is true for RGB, where there's no obvious relationship between colors that "go well together".
I've had success using TriadMixing and CIE94 to avoid similar colors. The following image uses input colors red, yellow, and white. See here.
// http://devmag.org.za/2012/07/29/how-to-choose-colours-procedurally-algorithms/#:~:text=120%20and%20240.-,7.%20Triad%20Mixing,-This%20algorithm%20takes
public static Color RandomMix(Color color1, Color color2, Color color3,
float greyControl)
{
int randomIndex = random.NextByte() % 3;
float mixRatio1 =
(randomIndex == 0) ? random.NextFloat() * greyControl : random.NextFloat();
float mixRatio2 =
(randomIndex == 1) ? random.NextFloat() * greyControl : random.NextFloat();
float mixRatio3 =
(randomIndex == 2) ? random.NextFloat() * greyControl : random.NextFloat();
float sum = mixRatio1 + mixRatio2 + mixRatio3;
mixRatio1 /= sum;
mixRatio2 /= sum;
mixRatio3 /= sum;
return Color.FromArgb(
255,
(byte)(mixRatio1 * color1.R + mixRatio2 * color2.R + mixRatio3 * color3.R),
(byte)(mixRatio1 * color1.G + mixRatio2 * color2.G + mixRatio3 * color3.G),
(byte)(mixRatio1 * color1.B + mixRatio2 * color2.B + mixRatio3 * color3.B));
}
An answer that shouldn't be overlooked, because it's simple and presents advantages, is sampling of real life photos and paintings. sample as many random pixels as you want random colors on thumbnails of modern art pics, cezanne, van gogh, monnet, photos... the advantage is that you can get colors by theme and that they are organic colors. just put 20 - 30 pics in a folder and random sample a random pic every time.
Conversion to HSV values is a widespread code algorithm for psychologically based palette. hsv is easier to randomize.
In php:
function pastelColors() {
$r = dechex(round(((float) rand() / (float) getrandmax()) * 127) + 127);
$g = dechex(round(((float) rand() / (float) getrandmax()) * 127) + 127);
$b = dechex(round(((float) rand() / (float) getrandmax()) * 127) + 127);
return "#" . $r . $g . $b;
}
source: https://stackoverflow.com/a/12266311/2875783
Use distinct-colors.
Written in javascript.
It generates a palette of visually distinct colors.
distinct-colors is highly configurable:
Choose how many colors are in the palette
Restrict the hue to a specific range
Restrict the chroma (saturation) to a specific range
Restrict the lightness to a specific range
Configure general quality of the palette
Here is quick and dirty color generator in C# (using 'RYB approach' described in this article). It's a rewrite from JavaScript.
Use:
List<Color> ColorPalette = ColorGenerator.Generate(30).ToList();
First two colors tend to be white and a shade of black. I often skip them like this (using Linq):
List<Color> ColorsPalette = ColorGenerator
.Generate(30)
.Skip(2) // skip white and black
.ToList();
Implementation:
public static class ColorGenerator
{
// RYB color space
private static class RYB
{
private static readonly double[] White = { 1, 1, 1 };
private static readonly double[] Red = { 1, 0, 0 };
private static readonly double[] Yellow = { 1, 1, 0 };
private static readonly double[] Blue = { 0.163, 0.373, 0.6 };
private static readonly double[] Violet = { 0.5, 0, 0.5 };
private static readonly double[] Green = { 0, 0.66, 0.2 };
private static readonly double[] Orange = { 1, 0.5, 0 };
private static readonly double[] Black = { 0.2, 0.094, 0.0 };
public static double[] ToRgb(double r, double y, double b)
{
var rgb = new double[3];
for (int i = 0; i < 3; i++)
{
rgb[i] = White[i] * (1.0 - r) * (1.0 - b) * (1.0 - y) +
Red[i] * r * (1.0 - b) * (1.0 - y) +
Blue[i] * (1.0 - r) * b * (1.0 - y) +
Violet[i] * r * b * (1.0 - y) +
Yellow[i] * (1.0 - r) * (1.0 - b) * y +
Orange[i] * r * (1.0 - b) * y +
Green[i] * (1.0 - r) * b * y +
Black[i] * r * b * y;
}
return rgb;
}
}
private class Points : IEnumerable<double[]>
{
private readonly int pointsCount;
private double[] picked;
private int pickedCount;
private readonly List<double[]> points = new List<double[]>();
public Points(int count)
{
pointsCount = count;
}
private void Generate()
{
points.Clear();
var numBase = (int)Math.Ceiling(Math.Pow(pointsCount, 1.0 / 3.0));
var ceil = (int)Math.Pow(numBase, 3.0);
for (int i = 0; i < ceil; i++)
{
points.Add(new[]
{
Math.Floor(i/(double)(numBase*numBase))/ (numBase - 1.0),
Math.Floor((i/(double)numBase) % numBase)/ (numBase - 1.0),
Math.Floor((double)(i % numBase))/ (numBase - 1.0),
});
}
}
private double Distance(double[] p1)
{
double distance = 0;
for (int i = 0; i < 3; i++)
{
distance += Math.Pow(p1[i] - picked[i], 2.0);
}
return distance;
}
private double[] Pick()
{
if (picked == null)
{
picked = points[0];
points.RemoveAt(0);
pickedCount = 1;
return picked;
}
var d1 = Distance(points[0]);
int i1 = 0, i2 = 0;
foreach (var point in points)
{
var d2 = Distance(point);
if (d1 < d2)
{
i1 = i2;
d1 = d2;
}
i2 += 1;
}
var pick = points[i1];
points.RemoveAt(i1);
for (int i = 0; i < 3; i++)
{
picked[i] = (pickedCount * picked[i] + pick[i]) / (pickedCount + 1.0);
}
pickedCount += 1;
return pick;
}
public IEnumerator<double[]> GetEnumerator()
{
Generate();
for (int i = 0; i < pointsCount; i++)
{
yield return Pick();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public static IEnumerable<Color> Generate(int numOfColors)
{
var points = new Points(numOfColors);
foreach (var point in points)
{
var rgb = RYB.ToRgb(point[0], point[1], point[2]);
yield return Color.FromArgb(
(int)Math.Floor(255 * rgb[0]),
(int)Math.Floor(255 * rgb[1]),
(int)Math.Floor(255 * rgb[2]));
}
}
}
David Crow's method in an R two-liner:
GetRandomColours <- function(num.of.colours, color.to.mix=c(1,1,1)) {
return(rgb((matrix(runif(num.of.colours*3), nrow=num.of.colours)*color.to.mix)/2))
}
function fnGetRandomColour(iDarkLuma, iLightLuma)
{
for (var i=0;i<20;i++)
{
var sColour = ('ffffff' + Math.floor(Math.random() * 0xFFFFFF).toString(16)).substr(-6);
var rgb = parseInt(sColour, 16); // convert rrggbb to decimal
var r = (rgb >> 16) & 0xff; // extract red
var g = (rgb >> 8) & 0xff; // extract green
var b = (rgb >> 0) & 0xff; // extract blue
var iLuma = 0.2126 * r + 0.7152 * g + 0.0722 * b; // per ITU-R BT.709
if (iLuma > iDarkLuma && iLuma < iLightLuma) return sColour;
}
return sColour;
}
For pastel, pass in higher luma dark/light integers - ie fnGetRandomColour(120, 250)
Credits: all credits to
http://paulirish.com/2009/random-hex-color-code-snippets/
stackoverflow.com/questions/12043187/how-to-check-if-hex-color-is-too-black
JavaScript adaptation of David Crow's original answer, IE and Nodejs specific code included.
generateRandomComplementaryColor = function(r, g, b){
//--- JavaScript code
var red = Math.floor((Math.random() * 256));
var green = Math.floor((Math.random() * 256));
var blue = Math.floor((Math.random() * 256));
//---
//--- Extra check for Internet Explorers, its Math.random is not random enough.
if(!/MSIE 9/i.test(navigator.userAgent) && !/MSIE 10/i.test(navigator.userAgent) && !/rv:11.0/i.test(navigator.userAgent)){
red = Math.floor((('0.' + window.crypto.getRandomValues(new Uint32Array(1))[0]) * 256));
green = Math.floor((('0.' + window.crypto.getRandomValues(new Uint32Array(1))[0]) * 256));
blue = Math.floor((('0.' + window.crypto.getRandomValues(new Uint32Array(1))[0]) * 256));
};
//---
//--- nodejs code
/*
crypto = Npm.require('crypto');
red = Math.floor((parseInt(crypto.randomBytes(8).toString('hex'), 16)) * 1.0e-19 * 256);
green = Math.floor((parseInt(crypto.randomBytes(8).toString('hex'), 16)) * 1.0e-19 * 256);
blue = Math.floor((parseInt(crypto.randomBytes(8).toString('hex'), 16)) * 1.0e-19 * 256);
*/
//---
red = (red + r)/2;
green = (green + g)/2;
blue = (blue + b)/2;
return 'rgb(' + Math.floor(red) + ', ' + Math.floor(green) + ', ' + Math.floor(blue) + ')';
}
Run the function using:
generateRandomComplementaryColor(240, 240, 240);
you could have them be within a certain brightness. that would control the ammount of "neon" colors a bit. for instance, if the "brightness"
brightness = sqrt(R^2+G^2+B^2)
was within a certain high bound, it would have a washed out, light color to it. Conversely, if it was within a certain low bound, it would be darker. This would eliminate any crazy, standout colors and if you chose a bound really high or really low, they would all be fairly close to either white or black.
It's going to be hard to get what you want algorithmically - people have been studying color theory for a long time, and they don't even know all the rules.
However, there are some rules which you can use to cull bad color combinations (ie, there are rules for clashing colors, and choosing complementary colors).
I'd recommend you visit your library's art section and check out books on color theory to gain a better understanding of what is a good color before you try to make one - it appears you might not even know why certain combinations work and others don't.
-Adam
I'd strongly recommend using a CG HSVtoRGB shader function, they are awesome... it gives you natural color control like a painter instead of control like a crt monitor, which you arent presumably!
This is a way to make 1 float value. i.e. Grey, into 1000 ds of combinations of color and brightness and saturation etc:
int rand = a global color randomizer that you can control by script/ by a crossfader etc.
float h = perlin(grey,23.3*rand)
float s = perlin(grey,54,4*rand)
float v = perlin(grey,12.6*rand)
Return float4 HSVtoRGB(h,s,v);
result is AWESOME COLOR RANDOMIZATION! it's not natural but it uses natural color gradients and it looks organic and controlleably irridescent / pastel parameters.
For perlin, you can use this function, it is a fast zig zag version of perlin.
function zig ( xx : float ): float{ //lfo nz -1,1
xx= xx+32;
var x0 = Mathf.Floor(xx);
var x1 = x0+1;
var v0 = (Mathf.Sin (x0*.014686)*31718.927)%1;
var v1 = (Mathf.Sin (x1*.014686)*31718.927)%1;
return Mathf.Lerp( v0 , v1 , (xx)%1 )*2-1;
}
Here is something I wrote for a site I made. It will auto-generate a random flat background-color for any div with the class .flat-color-gen. Jquery is only required for the purposes of adding css to the page; it's not required for the main part of this, which is the generateFlatColorWithOrder() method.
JsFiddle Link
(function($) {
function generateFlatColorWithOrder(num, rr, rg, rb) {
var colorBase = 256;
var red = 0;
var green = 0;
var blue = 0;
num = Math.round(num);
num = num + 1;
if (num != null) {
red = (num*rr) % 256;
green = (num*rg) % 256;
blue = (num*rb) % 256;
}
var redString = Math.round((red + colorBase) / 2).toString();
var greenString = Math.round((green + colorBase) / 2).toString();
var blueString = Math.round((blue + colorBase) / 2).toString();
return "rgb("+redString+", "+greenString+", "+blueString+")";
//return '#' + redString + greenString + blueString;
}
function generateRandomFlatColor() {
return generateFlatColorWithOrder(Math.round(Math.random()*127));
}
var rr = Math.round(Math.random()*1000);
var rg = Math.round(Math.random()*1000);
var rb = Math.round(Math.random()*1000);
console.log("random red: "+ rr);
console.log("random green: "+ rg);
console.log("random blue: "+ rb);
console.log("----------------------------------------------------");
$('.flat-color-gen').each(function(i, obj) {
console.log(generateFlatColorWithOrder(i));
$(this).css("background-color",generateFlatColorWithOrder(i, rr, rg, rb).toString());
});
})(window.jQuery);

Resources