|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<meta name="viewport" content="width=device-width"> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<title>Figure 5.12 - Popout</title> |
|
|
|
<style> |
|
.frame { |
|
stroke: #A0A0A0; |
|
stroke-width: 3; |
|
fill: none; |
|
} |
|
.label { |
|
text-anchor: middle; |
|
} |
|
.rectmark { |
|
fill: #0070C0; |
|
} |
|
.circlemark { |
|
fill: #0070C0; |
|
} |
|
|
|
</style> |
|
|
|
</head> |
|
<body> |
|
|
|
<svg width="960" height="500"> |
|
<!-- |
|
Modified from example of SVG drop shadow at |
|
https://www.w3schools.com/graphics/svg_feoffset.asp |
|
--> |
|
<defs> |
|
<filter id="dropLR" x="0" y="0" width="200%" height="200%"> |
|
<feOffset result="offOut" in="SourceAlpha" dx="8" dy="8"/> |
|
<feGaussianBlur result="blurOut" in="offOut" stdDeviation="4"/> |
|
<feBlend in="SourceGraphic" in2="blurOut" mode="normal"/> |
|
</filter> |
|
<filter id="dropUL" x="-1" y="-1" width="200%" height="200%"> |
|
<feOffset result="offOut2" in="SourceAlpha" dx="-8" dy="-8"/> |
|
<feGaussianBlur result="blurOut2" in="offOut2" stdDeviation="4"/> |
|
<feBlend in="SourceGraphic" in2="blurOut2" mode="normal"/> |
|
</filter> |
|
</defs> |
|
|
|
</svg> |
|
<script> |
|
|
|
const svg = d3.select('svg'); |
|
|
|
const margin = { left: 30, right: 30, top:10, bottom: 30 }; |
|
const width = svg.attr('width'); |
|
const height = svg.attr('height'); |
|
const innerWidth = width - margin.left - margin.right; |
|
const innerHeight = height - margin.top - margin.bottom; |
|
|
|
const g = svg.append('g') |
|
.attr('transform', `translate(${margin.left},${margin.top})`); |
|
|
|
const fPadX = 35; |
|
const fPadY = 30; |
|
|
|
const frameWidth = (innerWidth - 2*fPadX) / 3; |
|
const frameHeight = (innerHeight - fPadY) / 2; |
|
|
|
const frameInnerPad = 25; |
|
const frameInnerWidth = frameWidth - 2*frameInnerPad; |
|
const frameInnerHeight = frameHeight - 2*frameInnerPad; |
|
|
|
function makeFrame(i, j, label) { |
|
const left = (i-1) * (frameWidth + fPadX); |
|
const top = (j-1) * (frameHeight + fPadY); |
|
|
|
const frame = g.append('g') |
|
.attr('transform', `translate(${left},${top})`); |
|
|
|
frame.append('rect') |
|
.attr('x', 0) |
|
.attr('y', 0) |
|
.attr('width', frameWidth) |
|
.attr('height', frameHeight) |
|
.attr('class', 'frame'); |
|
|
|
frame.append('text') |
|
.attr('x', frameWidth/2) |
|
.attr('y', frameHeight + 20) |
|
.attr('class', 'label') |
|
.text(label); |
|
|
|
const innerFrame = frame.append('g') |
|
.attr('transform', `translate(${frameInnerPad},${frameInnerPad})`); |
|
|
|
return innerFrame; |
|
} |
|
|
|
function addRectMark(selection, cx, cy) { |
|
const width = 40; |
|
const height = 8; |
|
const x = cx - width/2; |
|
const y = cy - height/2; |
|
return selection.append('rect') |
|
.attr('x', x) |
|
.attr('y', y) |
|
.attr('width', width) |
|
.attr('height', height) |
|
.attr('class', 'rectmark'); |
|
} |
|
|
|
function addCircleMark(selection, cx, cy) { |
|
const r = 7; |
|
return selection.append('circle') |
|
.attr('cx', cx) |
|
.attr('cy', cy) |
|
.attr('r', r) |
|
.attr('class', 'circlemark'); |
|
} |
|
|
|
const aF = makeFrame(1, 1, "(a)"); |
|
const bF = makeFrame(2, 1, "(b)"); |
|
const cF = makeFrame(3, 1, "(c)"); |
|
|
|
const dF = makeFrame(1, 2, "(d)"); |
|
const eF = makeFrame(2, 2, "(e)"); |
|
const fF = makeFrame(3, 2, "(f)"); |
|
|
|
// (a) frame |
|
const I = 4; |
|
const J = 6; |
|
const x0 = 25; |
|
const y0 = 0; |
|
const dx = ((frameInnerWidth - 200) / 3) + 50; |
|
const dy = frameInnerHeight / (J-1); |
|
for(var i = 0; i < I; i++) { |
|
for(var j = 0; j < J; j++) { |
|
if(i == 2 && j == 2) { |
|
addRectMark(aF, x0 + i*dx + 15, y0 + j*dy - 15).attr('width', 8).attr('height', 40); |
|
} else { |
|
addRectMark(aF, x0 + i*dx, y0 + j*dy); |
|
} |
|
} |
|
} |
|
|
|
// (b) frame |
|
for(var i = 0; i < I; i++) { |
|
for(var j = 0; j < J; j++) { |
|
if(i == 2 && j == 2) { |
|
addRectMark(bF, x0 + i*dx, y0 + j*dy - 5).attr('height', 20); |
|
} else { |
|
addRectMark(bF, x0 + i*dx, y0 + j*dy); |
|
} |
|
} |
|
} |
|
|
|
// (c) frame |
|
const xydots = [ |
|
{x:5, y:0}, |
|
{x:10.5, y:0.5}, |
|
{x:1.5, y:5}, |
|
{x:2.5, y:6}, |
|
{x:7, y:5.5}, |
|
{x:8, y:4.8}, |
|
{x:8, y:6}, |
|
{x:6, y:9.5}, // special mark, index=7 |
|
{x:3, y:12}, |
|
{x:10, y:12} |
|
]; |
|
for(var i in xydots) { |
|
var item = xydots[i]; |
|
var cx = item.x/12 * frameInnerWidth; |
|
var cy = item.y/12 * frameInnerHeight; |
|
if(i == 7) { |
|
cF.append("rect").attr("width", 8).attr("height", 24).attr("x", cx-4).attr("y", cy-12).attr("class", "rectmark"); |
|
cF.append("rect").attr("width", 24).attr("height", 8).attr("x", cx-12).attr("y", cy-4).attr("class", "rectmark"); |
|
} else { |
|
addCircleMark(cF, cx, cy); |
|
} |
|
} |
|
|
|
// (d) frame |
|
const xybars = [ |
|
{x: 5, y:0}, |
|
{x: 9, y:0.5}, |
|
{x: 2, y:1.0}, |
|
{x: 8, y:1.25}, |
|
{x: 10.25, y:1.5}, |
|
{x: 8.75, y:2.25}, |
|
{x: 7.5, y:3}, |
|
{x: 10.5, y:3}, |
|
{x: 0.5, y:2.25}, |
|
{x: 4.5, y:2.0}, |
|
{x: 1.5, y:3.5}, |
|
{x: 1.0, y:5}, |
|
{x: 1.0, y:7}, |
|
{x: 0.9, y:9}, |
|
{x: 3.75, y:5.75}, |
|
{x: 3.75, y:7.5}, |
|
{x: 3.75, y:10}, |
|
{x: 6.5, y:5.25}, |
|
{x: 6.75, y:7.5}, |
|
{x: 6.5, y:9.25}, |
|
{x: 9.5, y:6}, |
|
{x: 8, y:6.75}, |
|
{x: 9.5, y:8} |
|
]; |
|
for(var i in xybars) { |
|
var item = xybars[i]; |
|
addRectMark(dF, (item.x/11) * frameInnerWidth, (item.y/10) * frameInnerHeight); |
|
} |
|
|
|
// (e) frame |
|
for(var i in xydots) { |
|
var item = xydots[i]; |
|
var cx = item.x/12 * frameInnerWidth; |
|
var cy = item.y/12 * frameInnerHeight; |
|
if(i == 7) { |
|
addCircleMark(eF, cx, cy).attr('filter', 'url(#dropUL)'); |
|
} else { |
|
addCircleMark(eF, cx, cy).attr('filter', 'url(#dropLR)'); |
|
} |
|
} |
|
|
|
// (f) frame |
|
const anglebars = [ |
|
{x: 5, y: 3, angle: 15, rot: -10}, |
|
{x: 8, y: 4, angle: 35, rot: -19}, |
|
{x: 1, y: 8, angle: -30, rot: 19}, |
|
{x: 5.5, y: 7, angle: -12, rot: 6}, |
|
{x: 11, y: 8, angle: -12, rot: 10}, |
|
{x: 8.5, y: 9.5, angle: 0, rot: -15}, |
|
{x: 6, y: 11, angle: -10, rot: 8} |
|
]; |
|
for(var i in anglebars) { |
|
var item = anglebars[i]; |
|
var cx = (item.x / 12) * frameInnerWidth; |
|
var cy = (item.y / 12) * frameInnerHeight; |
|
var offset = fF.append('g').attr('transform', `translate(${cx}, ${cy})`); |
|
var rotate = offset.append('g').attr('transform', `rotate(${item.rot})`); |
|
var off1 = rotate.append('g').attr('transform', 'translate(0, -10)'); |
|
var off1r = off1.append('g').attr('transform', `rotate(${-item.angle/2})`); |
|
addRectMark(off1r, 0, 0); |
|
var off2 = rotate.append('g').attr('transform', 'translate(0, 10)'); |
|
var off2r = off2.append('g').attr('transform', `rotate(${item.angle/2})`); |
|
addRectMark(off2r, 0, 0); |
|
} |
|
|
|
</script> |
|
|
|
</body> |
|
</html> |