Public
Edited
Jan 11, 2023
Fork of Mousetrap
1 fork
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
interactionBoundary = ({xMin: -10, xMax: 10, yMin: 0, yMax: 6})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
html`<style>

text, p {
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 16px;

}

.headerTitle {
font-size: 21px;
}

.headerSubtitle {
}

.footer {
}

g.x > .tick:first-of-type {
color: #92278f;
}

g.x > .tick:first-of-type + .tick, g.x > .tick:first-of-type + .tick + .tick {
color: grey;
}

g.y > .tick {
color: #d12244; // supplyRed
// font-weight: bold;
}

.axisTitle {
font-size: 16px;
text-align: right;
}

line.given {
}

line.vDrop {
}

line.hDrop {
}

text.label {
}

circle.intersection {
}

</style>`
Insert cell
Insert cell
drag = {
function dragstarted(event, d) {

d.originalP1 = d.point1;
d.originalP2 = d.point2;

// First time
if (d.drawOriginLine && !d.hasOwnProperty('isAwayFromOrigin')) {
svg.selectAll(`line.ghost${d.label}`).remove();
svg.append('line')
.classed(`ghost${d.label}`, true)
.attr('x1', xScale(d.originalP1[0]))
.attr('y1', yScale(d.originalP1[1]))
.attr('x2', xScale(d.originalP2[0]))
.attr('y2', yScale(d.originalP2[1]))
.attr('stroke-width', d.width)
.attr('stroke', getColor(d))
.attr('filter', 'brightness(2)')

svg.selectAll(`text.ghost${d.label}`).remove();
svg.append('text')
.classed(`ghost${d.label}`, true)
.attr('x', xScale(d.originalP2[0]))
.attr('y', yScale(d.originalP2[1]))
.style('fill', getColor(d))
.style("font-size", d.labelSize)
.style("font-weight", 'bold')
.style('text-shadow', textShadowDark)
.attr('filter', 'brightness(2)')
.text(() => `Old ${d.label}`);
d.isAwayFromOrigin = true;
}
// following times, add color back
else if (d.drawOriginLine && !d.isAwayFromOrigin) {
svg.select(`line.ghost${d.label}`)
.attr('opacity', '1')
d3.select(`text.ghost${d.label}`)
.attr('opacity', '1')
d.isAwayFromOrigin = true;
}
if (d.moveX || d.moveY) {
d3.select(this).raise();
d3.select(this).attr("stroke-width", d.width*1.5).attr("filter", "drop-shadow(0 0 .5rem yellow)");
}

appendIntersectionObjects();

}
function dragged(event, d) {

svg.selectAll("line.vDrop").remove();
svg.selectAll("line.hDrop").remove();
appendLabels();

var dx=event.dx;
var dy=event.dy;

if (d.moveX) {
d.point1[0] = xScale.invert(xScale(d.point1[0])+dx);
d.point2[0] = xScale.invert(xScale(d.point2[0])+dx);
}
if (d.moveY) {
d.point1[1] = yScale.invert(yScale(d.point1[1])+dy);
d.point2[1] = yScale.invert(yScale(d.point2[1])+dy);
}

d.x1 = xScale(d.point1[0]);
d.y1 = yScale(d.point1[1]);
d.x2 = xScale(d.point2[0]);
d.y2 = yScale(d.point2[1]);
d.il = false;
d.ib = false;
d.it = false;
d.ir = false;

// handle line crossing interaction boundaries
if (intersectLines(d, yAxisLine)) {
// console.log("intersecting Y at", intersectLines(d, yAxisLine));
d.il = true;
if (xScale(d.point1[0]) < xScale(interactionBoundary.xMin)) {
d.x1 = xScale(interactionBoundary.xMin);
d.y1 = yScale(intersectLines(d, yAxisLine).y);
}
else if (xScale(d.point2[0]) < xScale(interactionBoundary.xMin)) {
d.x2 = xScale(interactionBoundary.xMin);
d.y2 = yScale(intersectLines(d, yAxisLine).y);
}
}

if(intersectLines(d, xAxisLine)) {
// console.log("intersecting X at", intersectLines(d, xAxisLine));
// remember that yScale is inverted, so this is checking for being less than min.
d.ib = true;
if (yScale(d.point1[1]) > yScale(interactionBoundary.yMin)) {
d.x1 = xScale(intersectLines(d, xAxisLine).x);
d.y1 = yScale(interactionBoundary.yMin);
}
else if (yScale(d.point2[1]) > yScale(interactionBoundary.yMin)) {
d.x2 = xScale(intersectLines(d, xAxisLine).x);
d.y2 = yScale(interactionBoundary.yMin);
}
}

if(intersectLines(d, topLine)) {
d.it = true;
if (yScale(d.point1[1]) < yScale(interactionBoundary.yMax)) {
d.x1 = xScale(intersectLines(d, topLine).x);
d.y1 = yScale(interactionBoundary.yMax);
}
else if (yScale(d.point2[1]) < yScale(interactionBoundary.yMax)) {
d.x2 = xScale(intersectLines(d, topLine).x);
d.y2 = yScale(interactionBoundary.yMax);
}
}

if (intersectLines(d, rightLine)) {
d.ir = true;
if (xScale(d.point1[0]) > xScale(xAxisScale.domain[1])) {
d.x1 = xScale(rightLine.point1[0]);
d.y1 = yScale(intersectLines(d, rightLine).y);
}
else if (xScale(d.point2[0]) > xScale(0)) {
d.x2 = xScale(rightLine.point1[0]);
d.y2 = yScale(intersectLines(d, rightLine).y);
}
}

// if the edit would make the line too short, bounce it.
if (distance(xyScale(d.point1), xyScale(d.point2)) * LINE_MIN_LENGTH > distance([d.x1, d.y1], [d.x2, d.y2])) {
console.log("too short")
if (d.il) {
d.point1[0] += (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
d.point2[0] += (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
}
if (d.ib) {
d.point1[1] += (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
d.point2[1] += (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
}
if (d.it) {
d.point1[1] -= (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
d.point2[1] -= (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
}
if (d.ir) {
d.point1[0] -= (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
d.point2[0] -= (xAxisScale.domain[1] - xAxisScale.domain[0]) / 100;
}
}
else {
d3.select(this)
.attr('x1', d.x1)
.attr('y1', d.y1)
.attr('x2', d.x2)
.attr('y2', d.y2)
}

svg.selectAll("text.label")
.data(lines)
.enter().append('text')
.classed('label', true)
.style("font-size", d => d.labelSize)
.style("font-family", 'Helvetica Neue, sans-serif')
.style('fill', d => getColor(d))
.style("font-weight", 'bold')
.style('text-shadow', textShadow)
.attr("x", d => {
return ('x2' in d) ? d.x2 : d.point2[0]
})
.attr("y", d => {
return ('y2' in d) ? d.y2 : d.point2[1]
})
.text(d => d.label);

appendIntersectionObjects();


}

function dragended(event, d) {


// if (d.drawOriginLine) {

// const ghostLineX1 = d3.select(`line.ghost${d.label}`).attr("x1")
// const draggedLineX1 = xScale(d.point1[0])

// const ghostLineY1 = d3.select(`line.ghost${d.label}`).attr("y1")
// const draggedLineY1 = d.y1

// if (
// (ghostLineX1 < draggedLineX1 + GHOST_MIN_DISTANCE && ghostLineX1 > draggedLineX1 - GHOST_MIN_DISTANCE) &&
// (ghostLineY1 < draggedLineY1 + GHOST_MIN_DISTANCE && ghostLineY1 > draggedLineY1 - GHOST_MIN_DISTANCE))
// {
// d3.select(`line.ghost${d.label}`)
// .attr('opacity', '0')
// d3.select(`text.ghost${d.label}`)
// .attr('opacity', '0')

// d.isAwayFromOrigin = false;
// }
// }

console.log(this)
d3.select(this)
.attr("cursor", getCursor(d))
.transition().duration(200).attr("stroke-width", d.width).attr("filter", "drop-shadow(0)")

}

return d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
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