Public
Edited
Feb 15, 2022
1 star
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
{
const svg = div.append('svg')
.attr("viewBox", [0, 0, 1500, 1500])
.style("background-color", svgBackgroundColor),
container = svg.append("g")
.attr("id", "container")
.attr("transform", `translate(${400}, ${400})`),

lipsDrawing = svg.append("defs")
.append("g")
.attr("id","iconCustom")
.append("path")
.attr("id", "lipsPath")
.attr("d", lipsPath)
yield div.node();


let data = getLipsPointsData();

let puckerScale = d3.scaleLog()
.domain(d3.extent(data, d => d.centerDist))
.range([0,1])


let updateLines = (data2) =>{
let t = d3.transition()
.duration(1350)
//.delay((d,i) => i * 10);

let ddataJoin = d3.select("#container").selectAll('line')
.data(data2)


ddataJoin.join(
enter => enter
.append('line')
.attr("stroke", d => `#${colors[getRandomInteger(1,colors.length-1)]}`)
.attr("stroke-width", (d,i) => getRandomInteger(2,5))
.style("opacity", (d,i) => getRandomInteger(2,9)/10)
.attr('x1', (d,i) => {
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let x1 = d.x + ((puckerScale(dist)/4 * (dist)) * Math.cos(d.theta));
return x1

})
.attr('y1', (d,i) =>{
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let y1 = d.y + ((dist * noise.noise(i*count)) * Math.sin(d.theta));
return y1

})
.attr('x2', (d,i) =>{
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let x2 = (d.x - (((dist*.90)+ noise.noise(i*count)*20) * Math.cos(d.theta)));
return x2;

})
.attr('y2', (d,i) =>{
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let y2 = d.y - (((dist*.90)+ noise.noise(i*count)*20) * Math.sin(d.theta));
return y2;
}
),
update => update.call(e => e.transition(t)

.attr("stroke", d => `#${colors[getRandomInteger(1,colors.length-1)]}`)
.attr("stroke-width", (d,i) => {

return getRandomInteger(2,5)})
.style("opacity", (d,i) => getRandomInteger(5,9)/10)
.attrTween("x1", (d,i) => {
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let x1 = d.x + ((puckerScale(dist)/4 * (dist)) * Math.cos(d.theta));
//let x1 = d.x + (dist * Math.cos(d.theta));
let x11 = d.x + ((dist* noise.noise(i*count)) * Math.cos(d.theta));
let intp = d3.interpolate(x1,x11);
return t => {
return intp(t);
}
})
.attrTween("y1", (d,i) => {
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let y1 = d.y + ((dist * noise.noise(i*count)) * Math.sin(d.theta));
let y11 = d.y + (((dist)*(noise.noise(i*count))) * Math.sin(d.theta));
let intp = d3.interpolate(y1,y11);
return t => {
return intp(t);
}
})
.attrTween("x2", (d,i) => {
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let x2 = (d.x - (((dist*.90)+ noise.noise(i*count)*20) * Math.cos(d.theta)));
let x22 = d.x - (((dist/getRandomInteger(2,5) + noise.noise(i*count)*20)) * Math.cos(d.theta));
let intp = d3.interpolate(x2,x22);
return t => {
return intp(t);
}
})
.attrTween("y2", (d,i) => {
let dist = d.centerDist < idealDist ? d.centerDist : d.centerDist;
let y2 = (d.y - (((dist*.90)+ noise.noise(i*count)*20) * Math.sin(d.theta)));
let y22 = d.y - (((dist/getRandomInteger(2,5)) + noise.noise(i*count)*20)) * Math.sin(d.theta);
let intp = d3.interpolate(y2,y22);
return t => {
return intp(t);
}


})

)

)
.attr("stroke-linecap", "round")
}

let count = 1
d3.interval(function() {
updateLines(data,count);
count++;
}, 1400);
}
Insert cell
lipsPath = "m695.88 293.71c-100.43-120.44-182.21-154.64-242.43-154.66-55.621 0.10156-90.73 28.961-103.78 42-32.375-29.973-67.734-42.199-102.37-42.125-126.72 1.0078-242.47 154.82-243.28 155.05l-4.0234 5.2227 4.5352 4.7891c122.2 128.78 240.43 172.67 343.34 172.67h0.21484c205.98-0.39453 347.2-172.61 347.82-173.07l4.0781-4.9531zm-36.418 26.027c-48.258 47.316-163.72 141.6-311.38z"
Insert cell
idealDist = 150 //ideal shortest distance from lip points to center
Insert cell
getLipsPointsData = () => {
let lpLength = d3.select('#lipsPath').node();
let totalLength = lpLength.getTotalLength();
totalLength = totalLength

let dataPoints = d3.range(numPoints).map(point => {
let step = point * (totalLength/numPoints);
let pt = lpLength.getPointAtLength(step);
return {
x: pt.x,
y: pt.y
}
})
let xExtent = d3.extent(dataPoints, d => d.x);
let yExtent = d3.extent(dataPoints, d => d.y);

let xCenter = (xExtent[1] - xExtent[0])/2;
let yCenter = (yExtent[1] - yExtent[0]);

let centerPoint = {x: xCenter, y: yCenter};

let data = dataPoints.map(pt => {
let hyp = Math.sqrt(Math.pow(Math.abs(centerPoint.y - pt.y), 2) + Math.pow(Math.abs(centerPoint.x - pt.x), 2));

let theta = Math.atan2((pt.y - centerPoint.y), (pt.x - centerPoint.x));

return {
x: pt.x,
y: pt.y,
centerDist: hyp,
theta: theta
}
})
return data;
}
Insert cell
width = 400
Insert cell
height = 400
Insert cell
margin = ({top: 50, bottom: 50, left: 50, right: 50})
Insert cell
getRandomInteger = (min, max) => {
return Math.floor(Math.random() * (max - min) + min);
}
Insert cell
Insert cell
Insert cell
Insert cell
numPoints = 800
Insert cell
div = d3.create('div')
.attr("id", "viz")
.style("width", "400px")
.style("height", "400px")
Insert cell
p5 = require("p5@1.4.0")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more