A test of textures.js that shows one approach for creating a "texture scale" that combines individual scales for pattern, size, and color.
See also this discussion on GitHub: riccardoscalco/textures#7
A test of textures.js that shows one approach for creating a "texture scale" that combines individual scales for pattern, size, and color.
See also this discussion on GitHub: riccardoscalco/textures#7
| // Creates a texture scale that combines pattern, color, and size. | |
| function createTextureScale(){ | |
| var patternScale, | |
| colorScale, | |
| sizeScale, | |
| patternAccessor, | |
| colorAccessor, | |
| sizeAccessor, | |
| texturesCache = {}; | |
| function my(d){ | |
| // Extract data values using accessors. | |
| var patternValue = patternAccessor(d), | |
| colorValue = colorAccessor(d), | |
| sizeValue = sizeAccessor(d), | |
| // Use data values to look up the texture. | |
| key = [patternValue, colorValue, sizeValue].join(","), | |
| texture = texturesCache[key], | |
| pattern, color, size; | |
| // Create a new texture the first time each unique | |
| // (texture, color, size) combination is encountered. | |
| if(!texture){ | |
| // Evaluate scaled values for pattern, color, and size. | |
| pattern = patternScale(patternValue); | |
| color = colorScale(colorValue); | |
| size = sizeScale(sizeValue); | |
| // Create the base texture with the pattern generator. | |
| texture = pattern(); | |
| // Apply color. | |
| texture = colorizeTexture(texture, color); | |
| // Apply size. | |
| texture = texture.size(size); | |
| // Initialize the texture. | |
| svg.call(texture); | |
| // Store the texture for future reuse. | |
| texturesCache[key] = texture; | |
| } | |
| // Return the url so this function can be passed directly into the fill attribute. | |
| return texture.url(); | |
| } | |
| // Makes the given texture appear as the given color. | |
| function colorizeTexture(texture, color){ | |
| // Use stroke, present on all patterns. | |
| var texture = texture.stroke(color); | |
| // Use fill, present only on some textures (e.g. "circles", not "lines"). | |
| if(texture.fill){ | |
| texture.fill(color); | |
| } | |
| return texture; | |
| } | |
| // API design inspired by http://bost.ocks.org/mike/chart/ | |
| my.patternScale = function(value){ | |
| if (!arguments.length) return patternScale; | |
| patternScale = value; | |
| return my; | |
| }; | |
| my.colorScale = function(value){ | |
| if (!arguments.length) return colorScale; | |
| colorScale = value; | |
| return my; | |
| }; | |
| my.sizeScale = function(value){ | |
| if (!arguments.length) return sizeScale; | |
| sizeScale = value; | |
| return my; | |
| }; | |
| my.patternAccessor = function(value){ | |
| if (!arguments.length) return patternAccessor; | |
| patternAccessor = value; | |
| return my; | |
| }; | |
| my.colorAccessor = function(value){ | |
| if (!arguments.length) return colorAccessor; | |
| colorAccessor = value; | |
| return my; | |
| }; | |
| my.sizeAccessor = function(value){ | |
| if (!arguments.length) return sizeAccessor; | |
| sizeAccessor = value; | |
| return my; | |
| }; | |
| return my; | |
| } |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <script src="https://riccardoscalco.github.io/textures/textures.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min.js"></script> | |
| <script src="https://d3js.org/d3.v3.min.js"></script> | |
| <meta charset="utf-8"> | |
| <title>Texture Test</title> | |
| </head> | |
| <body> | |
| <div id="example"></div> | |
| <script src="createTextureScale.js"></script> | |
| <script src="main.js"></script> | |
| </body> | |
| </html> |
| // This program tests the script "createTextureScale", which | |
| // creates a texture scale by combining scales for pattern, color, and size. | |
| var width = 500, | |
| height = 500, | |
| // An n X n grid of circles will be created. | |
| n = 12, | |
| x = d3.scale.linear().domain([0, n]).range([0, width]), | |
| y = d3.scale.linear().domain([0, n]).range([0, height]), | |
| radius = 20, | |
| transitionDuration = 1000, | |
| svg = d3.select("#example").append("svg") | |
| .attr("width", width) | |
| .attr("height", height) | |
| // Center the SVG with respect to default width of bl.ocks. | |
| .style("position", "absolute") | |
| .style("left", 960 / 2 - width / 2), | |
| // Create a scale that encapsulates patterns. | |
| patternScale = d3.scale.ordinal() | |
| .domain(["A", "B", "C"]) | |
| // Patterns (base textures) need to be generated multiple times, | |
| // once for each (size, color) pair they are paired with. | |
| // Therefore these need to be functions, not direct textures. | |
| .range([ | |
| function(){ return textures.lines(); }, | |
| function(){ return textures.circles(); }, | |
| function(){ return textures.paths().d("squares"); } | |
| ]), | |
| // Create a scale that encapsulates colors. | |
| // Colors from http://colorbrewer2.org/ | |
| colorScale = d3.scale.ordinal() | |
| .domain(["X", "Y", "Z"]) | |
| .range(["#1b9e77", "#d95f02", "#7570b3"]), | |
| // Create a scale that encapsulates size. | |
| sizeScale = d3.scale.linear() | |
| .domain([0, 1]) | |
| .range([5, 20]) | |
| // Create a combined scale for pattern, size, and color. | |
| textureScale = createTextureScale() | |
| .patternScale(patternScale) | |
| .colorScale(colorScale) | |
| .sizeScale(sizeScale) | |
| .patternAccessor(function(d){ return d.pattern; }) | |
| .colorAccessor(function(d){ return d.color; }) | |
| .sizeAccessor(function(d){ return d.size; }); | |
| // Initialize the data grid. | |
| var data = []; | |
| for(var i = 0; i < n; i++){ | |
| for(var j = 0; j < n; j++){ | |
| data.push({ | |
| x: i, | |
| y: j, | |
| pattern: i < n / 3 ? "A" : i < (n * 2 / 3) ? "B" : "C", | |
| color: j < n / 3 ? "X" : j < (n * 2 / 3) ? "Y" : "Z", | |
| size: j / n | |
| }); | |
| } | |
| } | |
| // Create the marks. | |
| var marks = svg.selectAll(".mark").data(data) | |
| .enter().append("circle") | |
| // The "mark" class is necessary, because | |
| // selectAll("circle") conflicts with the circle texture. | |
| .attr("class", "mark") | |
| .attr("cx", function(d){ return x(d.x) + radius; }) | |
| .attr("cy", function(d){ return y(d.y) + radius; }) | |
| // Use the color scale for the stroke around each circle. | |
| .style("stroke", function(d){ return colorScale(d.color); }) | |
| // Use the combined texture & color scale to define the texture. | |
| .style("fill", textureScale); | |
| // Periodically set a random radius on each circle. | |
| function randomizeSize(){ | |
| marks.transition().duration(transitionDuration) | |
| .attr("r", function(){ return Math.random() * radius; }); | |
| } | |
| randomizeSize(); | |
| setInterval(randomizeSize, transitionDuration); |