A more formal attempt at creating a utility to extend d3.js, lichen.js.
This block shows some of the methods of some of the patterns in the library. In some cases the user interface limits setting more complex method parameters (such as multiple widths for plaid and stripe patterns).
xxxxxxxxxx
<head>
<script src="https://d3js.org/d3.v5.js"></script>
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Lato" />
<style>
.chevrons {
cursor: pointer;
}
text {
font-family: "Lato";
}
.menu {
cursor: pointer;
}
.grids {
cursor: pointer;
}
</style>
<script src="lichen.js"></script>
<script src="chevronArc.js"></script>
</head>
<body>
<script>
var ps = ln.circles().spacings([1,2,3]).get();
console.log(ps);
var width = 960;
var height = 500;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var topBackground = svg.append("g");
topBackground.append("rect")
.attr("width",width)
.attr("height",50)
.attr("fill",ln.hexagon().stroke("#ddd").strokeWidth(1).radius(40).angle(20).spacing(0).fill("none").use())
var r = 200;
var arc = d3.arc()
.innerRadius(r)
.outerRadius(r+20)
.startAngle(0)
.endAngle(2*Math.PI);
var chevronData = [{a:40,currentA:40,f:"#eaeaea",o:0.12},{a:30,currentA:30,f:"#eee",o:0.15},{a:20,f:"#ddd",o:0.20},{a:10,f:"#cacaca",o:0.25}]
var chevrons = d3.chevronArc()
.outerRadius(r)
.innerRadius(r-60)
.startAngle(function(d) { return -d.a/180*Math.PI; })
.endAngle(function(d) { return d.a/180*Math.PI; })
.startIndent(-12)
.endIndent(12);
var control = svg.append("g")
.attr("transform","translate(480,250)")
.call(d3.drag().on("drag",dragged)
.on("start",mouseover)
.on("end",mouseout)
)
control.selectAll(".chevrons")
.data(chevronData)
.enter()
.append("path")
.attr("d",chevrons)
.attr("class","chevrons")
.attr("fill","#555")
.attr("opacity",function(d) { return d.o; })
.on("mouseover", mouseover)
.on("mouseout",mouseout)
var patterns = [
{type:"circle",fill:ln.circle(),options:["spacing","radius","strokeWidth"],grid:["hex","square"]},
{type:"square",fill:ln.square(),options:["spacing","length","strokeWidth"],grid:["hex","square"]},
{type:"hexagon",fill:ln.hexagon(),options:["spacing","radius","strokeWidth"],grid:["hex","square"]},
{type:"symbol",fill:ln.symbol(),options:["spacing","strokeWidth"],grid:["hex","square"]},
{type:"checker",fill:ln.checker(),options:["width"]},
{type:"letter",fill:ln.text(),options:["spacing","strokeWidth","fontSize"],grid:["hex","square"]},
{type:"sine",fill:ln.sine(),options:["amplitude","period","strokeWidth","sampling"]},
{type:"stripe",fill:ln.stripe(),options:["width"] },
{type:"octagon",fill:ln.octagon(),options:["strokeWidth","length"] },
{type:"plaid",fill:ln.plaid(),options:["width"]},
{type:"cairo",fill:ln.cairo(),options:["length","strokeWidth"]},
{type:"fish",fill:ln.fish(),options:["spacing","scale"],grid:["hex","square"]}
];
var currentPattern = patterns[Math.floor(Math.random()*patterns.length)];
svg.append("text")
.attr("y", 80)
.attr("x",150)
.style("font-size",24)
.style("text-anchor","middle")
.text("Selected Base Patterns");
svg.append("text")
.attr("y", 80)
.attr("x",760)
.style("font-size",24)
.style("text-anchor","middle")
.text("Selected Pattern Methods");
svg.append("text")
.attr("y", 30)
.attr("x",480)
.style("font-size",24)
.style("text-anchor","middle")
.text("lichen.js Pattern Explorer");
var pattern = svg.append("circle")
.attr("r", 140)
.attr("fill", currentPattern.fill.use())
.attr("stroke","black")
.attr("stroke-width",8)
.attr("transform","translate("+width/2+","+height/2+")");
var showcase = svg.selectAll()
.data(patterns)
.enter()
.append("circle")
.attr("cx", function(d,i) { return i%3 * 100 + 50; })
.attr("cy", function(d,i) { return Math.floor(i/3) * 100 + 150 })
.attr("r", 40)
.attr("fill", function(d) { return d.fill.use() })
.attr("stroke","black")
.attr("stroke-width",3)
.attr("class","menu")
.on("click", function(d) {
currentPattern = d;
pattern.attr("fill", currentPattern.fill.use());
showOptions(d);
})
var scales = {
spacing: d3.scaleLinear().range([0,200]).domain([-5,40]),
radius: d3.scaleLinear().range([0,200]).domain([0,40]),
length: d3.scaleLinear().range([0,200]).domain([0,40]),
strokeWidth: d3.scaleLinear().range([0,200]).domain([0,10]),
width: d3.scaleLinear().range([0,200]).domain([0,80]),
amplitude: d3.scaleLinear().range([0,200]).domain([0,80]),
period: d3.scaleLinear().range([0,200]).domain([0,200]),
sampling: d3.scaleLinear().range([0,200]).domain([0.01,5]),
scale: d3.scaleLinear().range([0,200]).domain([0.1,5]),
fontSize: d3.scaleLinear().range([0,200]).domain([8,32])
}
showOptions(currentPattern);
function showOptions(p) {
svg.selectAll(".currentName").attr("opacity",1).transition().attr("opacity",0).duration(200).remove();
svg.append("text")
.attr("class","currentName")
.attr("x", 750)
.attr("y", 110)
.attr("opacity",0)
.text("ln."+p.type+"()")
.transition()
.attr("opacity",1)
.duration(200)
.delay(200);
var options = svg.selectAll(".options")
.data(p.options);
options.exit().transition().attr("opacity",0).duration(500).remove();
var options = options.enter()
.append("g")
.merge(options)
.attr("transform",function(d,i) {
return "translate("+700+","+(i*80+180)+")";
})
.attr("class","options")
options.selectAll("*").transition().attr("opacity",0).duration(200).remove();
options.append("line")
.attr("x1",100)
.attr("x2",100)
.attr("stroke-width",3)
.attr("stroke","grey")
.transition()
.delay(200)
.attr("x1",0)
.attr("x2",200)
.duration(400);
options.append("text")
.attr("x",100)
.attr("y",-20)
.style("text-anchor","middle")
.text(function(d) { return d; });
options.append("circle")
.attr("cy",0)
.attr("r", 8)
.attr("cx", function(d) {
var current = p.fill[d]();
return scales[d](current);
})
.attr("opacity",0)
.call(d3.drag().on("drag",dragValue))
.style("cursor","pointer")
.transition()
.attr("opacity",1)
.delay(250)
.duration(300);
if (p.grid != undefined) {
var grids = svg.selectAll(".grids")
.data(p.grid);
var offset = p.grid.length*50/2;
grids.exit().remove();
grids.enter()
.append("text")
.merge(grids)
.text(function(d) { return d; })
.attr("x", function(d,i) { return i * 50 + 480 - offset; })
.attr("y", 480)
.attr("class","grids")
.on("click", function(d) {
if (d == "hex") currentPattern.fill.hex(true).add();
else currentPattern.fill.hex(false).add();
});
svg.append("text")
.attr("x",480)
.attr("y",450)
.style("text-anchor","middle")
.text("grid options")
.attr("class","grids");
}
else {
svg.selectAll(".grids")
.remove();
}
}
function mouseout(d) {
d3.selectAll(".chevrons")
.filter(function(d) { return d.o < 0.2; })
.transition()
.attr("fill","#555")
.attrTween("d", function(d) {
var node = this;
var interpolate = d3.interpolate(d.currentA,d.a);
return function(t) {
var v = interpolate(t);
d.currentA = v;
return chevrons({a:v});
}
})
.duration(2000);
}
function mouseover(d) {
d3.selectAll(".chevrons")
.filter(function(d) { return d.o < 0.2; })
.transition()
.attr("fill","orange")
.attrTween("d", function(d) {
var node = this;
var interpolate = d3.interpolate(d.currentA,90);
return function(t) {
var v = interpolate(t);
d.currentA = v;
return chevrons({a:v});
}
})
.duration(2000);
}
function dragValue(d) {
var circle = d3.select(this);
var x = d3.event.x;
if (x < 0) x = 0;
if (x > 200) x = 200;
circle.attr("cx",x);
var f = d3.format(".2f");
var text = d3.select(this.parentNode).select("text").text(d+"("+f(scales[d].invert(x))+")");
currentPattern.fill[d](scales[d].invert(x)).add();
}
function dragged(d) {
var x = d3.event.x;
var y = d3.event.y;
var r = 170;
x = x - width/2;
y = y - height/2;
var a = Math.atan(y/x);
var x1 = Math.cos(a) * r;
var y1 = Math.sin(a) * r;
if(a<0) a = -a + Math.PI*2;
if (x<0) y1 = -y1, x1 = -x1;
var theta = getAngle([x1,y1],[0,0],[100,0]);
if(y<0) theta = 2*Math.PI-theta;
currentPattern.fill.angle(theta*180/Math.PI+Math.PI/2).add();
control.attr("transform","translate("+ (width/2) + "," + (height/2) +")rotate("+(90)+")");
d3.select(this)
.attr("transform","translate("+ (width/2) + "," + (height/2) +")rotate("+(theta*180/Math.PI+90)+")");
}
function getAngle(A,B,C) {
var AB = Math.sqrt(Math.pow(B[0]-A[0],2)+ Math.pow(B[1]-A[1],2));
var BC = Math.sqrt(Math.pow(B[0]-C[0],2)+ Math.pow(B[1]-C[1],2));
var AC = Math.sqrt(Math.pow(C[0]-A[0],2)+ Math.pow(C[1]-A[1],2));
return Math.acos((BC*BC+AB*AB-AC*AC)/(2*BC*AB));
}
function tweenArc(a) {
return function(d) {
var interpolate = d3.interpolate(d.a,Math.PI/2);
return function(t) {
var dd = {};
dd.a = interpolate(t);
return chevron(dd);
}
}
}
</script>
https://d3js.org/d3.v5.js