Published
Edited
Nov 24, 2019
Insert cell
Insert cell
Insert cell
Insert cell
config = {
var cfg = {
//width: 1000,
//height: 600,
width: 2000,
height: 1200,
//straight: 400,
straight: 1133.3333333333,
//radius: 250,
radius: 283.3333333333,
//base_x: 500,
//base_y: 550,
//person_x: 200,
//person_y: 400,
base_x: 433.3333435059,
base_y: 883.3333129883,
person_x: 1000.0000000000,
person_y: 1072.2221679688,
sound_speed: 340, // m/sec
//car_speed: 100, // 100m/sec = 360km/s
car_speed: 113.3333333333, // 100m/sec = 360km/s
sampling_rate: 8000
};

cfg.circle_r = cfg.width / 100;
cfg.font_size = cfg.width / 50;

//cfg.wave_distance_base = 200;
cfg.wave_distance_base = 113.3333333333;
cfg.wave_radius_base = cfg.circle_r;
cfg.corners = {
left: (cfg.width - cfg.straight) / 2,
right: (cfg.width + cfg.straight) / 2,
top: cfg.height / 2 - cfg.radius,
bottom: cfg.height / 2 + cfg.radius,
center_y: cfg.height / 2
};
cfg.track_length = 2 * (cfg.radius * Math.PI + cfg.straight);
cfg.rev_time = cfg.track_length / cfg.car_speed;
// 왼쪽, 위 코너 입구를 기준으로 반시계 방향
cfg.time2where = [
{until: Math.PI * cfg.radius / cfg.car_speed, where: "left"},
{until: (Math.PI * cfg.radius + cfg.straight) / cfg.car_speed, where: "bottom"},
{until: (2 * Math.PI * cfg.radius + cfg.straight) / cfg.car_speed, where: "right"},
{until: 2 * (Math.PI * cfg.radius + cfg.straight) / cfg.car_speed, where: "top"}
];
return cfg;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const stadium = svg`<svg viewBox="0 0 ${config.width} ${config.height}" class="chart">
<path id="track" style="stroke: cyan; fill: none; stroke-width: 10" />
<g id="car_legend" transform="translate(600, 500)" fill="steelblue" style="font-size: ${config.font_size}px">
<circle r="${config.circle_r}"></circle>
<text x="${config.circle_r + 8}" alignment-baseline="middle">car</text>
</g>
<g id="shadow_legend" transform="translate(600, 600)" fill="gray" style="font-size: ${config.font_size}px">
<circle r="${config.circle_r}"></circle>
<text x="${config.circle_r + 8}" alignment-baseline="middle">sound amplitude on distance ${config.wave_distance_base}m</text>
<line x1="${config.circle_r + 316}" y1="${config.circle_r + 10}" x2="${config.circle_r + 316}" y2="${config.circle_r + 30}" style="stroke: gray; stroke-width: 6"></line>
<line x1="${config.circle_r + 316 + config.wave_distance_base}" y1="${config.circle_r + 10}" x2="${config.circle_r + 316 + config.wave_distance_base}" y2="${config.circle_r + 30}" style="stroke: gray; stroke-width: 6"></line>
<line x1="${config.circle_r + 316}" y1="${config.circle_r + 20}" x2="${config.circle_r + 316 + config.wave_distance_base}" y2="${config.circle_r + 20}" style="stroke: gray; stroke-width: 6"></line>
</g>
<g id="person" transform="translate(${config.person_x}, ${config.person_y})" style="font-size: ${config.font_size}px">
<circle r="${config.circle_r}" fill="firebrick"></circle>
<text y="${config.circle_r * 3}" fill="firebrick" style="text-anchor: middle">person</text>
</g>
</svg>`;
const corners = config.corners;

const draw = () => {
const path = d3.path();
path.moveTo(corners.left, corners.top);
path.arc(corners.left, corners.center_y, config.radius, Math.PI * 3 / 2, Math.PI / 2, true);
path.lineTo(corners.right, corners.bottom);
path.arc(corners.right, corners.center_y, config.radius, Math.PI / 2, Math.PI * 3 / 2, true);
path.lineTo(corners.left, corners.top);
return path;
};
const track = d3.select(stadium).select("#track");
track.attr("d", draw());

const shadow_position = prepare_shadow_position(config.rev_time, config.sampling_rate);
const max_sample = Math.ceil(config.rev_time * config.sampling_rate);

for (let t = 0; t < config.rev_time; t+=4) {
const car_xy = drive(config.base_x, config.base_y, t);
const car = d3.select(stadium).append("g")
.style("text-anchor", "middle")
.style("font-size", config.font_size + "px")
.attr("fill", "steelblue")
.attr("transform", `translate(${car_xy[0]}, ${car_xy[1]})`);
car.append("circle")
.attr("r", config.circle_r);
car.append("text")
.attr("y", config.circle_r * 3)
.text(`${t}s`);
const shadow_xy = shadow_position[t * config.sampling_rate];
const d = Math.max(dd(shadow_xy[0], shadow_xy[1]), 1);
const ratio = config.wave_distance_base / d;
const shadow = d3.select(stadium).append("g")
.style("text-anchor", "middle")
.style("font-size", config.font_size + "px")
.attr("fill", "gray")
.attr("transform", `translate(${shadow_xy[0]}, ${shadow_xy[1]})`);
shadow.append("circle")
.attr("r", ratio * config.wave_radius_base);
shadow.append("text")
.attr("y", -2 * config.circle_r)
.text(`${t}s`);
}

return stadium;
}
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