Make it d3
This commit is contained in:
166
app.qmd
166
app.qmd
@@ -10,13 +10,15 @@ execute:
|
||||
highlight-style: github
|
||||
---
|
||||
|
||||
#### B-spline Basis Functions
|
||||
## B-spline Basis Functions
|
||||
|
||||
```{ojs}
|
||||
bsplineData = FileAttachment("basis_functions.csv").csv({ typed: true })
|
||||
d3 = require("d3@7")
|
||||
```
|
||||
|
||||
```{ojs}
|
||||
bsplineData = FileAttachment("basis_functions.csv").csv({ typed: true })
|
||||
|
||||
knotValues = Array.from(new Set(bsplineData.map(d => d.knots))).sort((a, b) => a - b)
|
||||
minKnots = Math.min(...knotValues)
|
||||
maxKnots = Math.max(...knotValues)
|
||||
@@ -24,9 +26,7 @@ maxKnots = Math.max(...knotValues)
|
||||
muValues = Array.from(new Set(bsplineData.map(d => d.mu))).sort((a, b) => a - b)
|
||||
minMu = Math.min(...muValues)
|
||||
maxMu = Math.max(...muValues)
|
||||
```
|
||||
|
||||
```{ojs}
|
||||
// Create a more compact layout for controls
|
||||
viewof controls = Inputs.form({
|
||||
knots: Inputs.range([minKnots, maxKnots], {value: minKnots, step: 1, label: "Knots:", width: 200}),
|
||||
@@ -39,39 +39,141 @@ viewof controls = Inputs.form({
|
||||
|
||||
selectedKnots = controls.knots
|
||||
selectedMu = controls.mu
|
||||
```
|
||||
|
||||
```{ojs}
|
||||
filteredBspline = bsplineData.filter(function(row) {
|
||||
return selectedKnots == row.knots && Math.abs(selectedMu - row.mu) < 0.001;
|
||||
})
|
||||
```
|
||||
|
||||
```{ojs}
|
||||
Plot.plot({
|
||||
grid: true,
|
||||
y: {domain: [0, 0.7]},
|
||||
x: {label: "x", domain: [0, 1]},
|
||||
marks: [
|
||||
Plot.line(filteredBspline, {
|
||||
x: "x",
|
||||
y: "y",
|
||||
stroke: "b",
|
||||
strokeWidth: 5,
|
||||
}),
|
||||
Plot.ruleY([0])
|
||||
],
|
||||
color: {
|
||||
legend: false,
|
||||
label: "Basis Function"
|
||||
},
|
||||
marginRight: 80,
|
||||
width: 800,
|
||||
height: 400,
|
||||
// title: `B-spline Basis Functions (${selectedKnots} knots, μ = ${selectedMu})`
|
||||
})
|
||||
```
|
||||
// D3-based visualization for B-spline basis functions
|
||||
chart = {
|
||||
// Create chart dimensions
|
||||
const width = 800;
|
||||
const height = 400;
|
||||
const margin = {top: 40, right: 20, bottom: 40, left: 40};
|
||||
const innerWidth = width - margin.left - margin.right;
|
||||
const innerHeight = height - margin.top - margin.bottom;
|
||||
|
||||
::: {.callout-note}
|
||||
TODO
|
||||
:::
|
||||
// Create scales
|
||||
const x = d3.scaleLinear()
|
||||
.domain([0, 1])
|
||||
.range([0, innerWidth]);
|
||||
|
||||
const y = d3.scaleLinear()
|
||||
.domain([0, 0.7])
|
||||
.range([innerHeight, 0]);
|
||||
|
||||
// Create a color scale for the basis functions
|
||||
const color = d3.scaleOrdinal(d3.schemeCategory10);
|
||||
|
||||
// Create SVG
|
||||
const svg = d3.create("svg")
|
||||
.attr("width", width)
|
||||
.attr("height", height)
|
||||
.attr("viewBox", [0, 0, width, height])
|
||||
.attr("style", "max-width: 100%; height: auto;");
|
||||
|
||||
// Add chart title
|
||||
svg.append("text")
|
||||
.attr("class", "chart-title")
|
||||
.attr("x", width / 2)
|
||||
.attr("y", 20)
|
||||
.attr("text-anchor", "middle")
|
||||
.attr("font-size", "16px")
|
||||
.attr("font-weight", "bold");
|
||||
|
||||
// Create the chart group
|
||||
const g = svg.append("g")
|
||||
.attr("transform", `translate(${margin.left},${margin.top})`);
|
||||
|
||||
// Add axes
|
||||
const xAxis = g.append("g")
|
||||
.attr("transform", `translate(0,${innerHeight})`)
|
||||
.call(d3.axisBottom(x).ticks(10));
|
||||
|
||||
const yAxis = g.append("g")
|
||||
.call(d3.axisLeft(y).ticks(5));
|
||||
|
||||
// Add axis labels
|
||||
g.append("text")
|
||||
.attr("x", innerWidth / 2)
|
||||
.attr("y", innerHeight + 35)
|
||||
.attr("text-anchor", "middle")
|
||||
.text("x");
|
||||
|
||||
g.append("text")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("x", -innerHeight / 2)
|
||||
.attr("y", -30)
|
||||
.attr("text-anchor", "middle")
|
||||
.text("y");
|
||||
|
||||
// Add a horizontal line at y = 0
|
||||
g.append("line")
|
||||
.attr("x1", 0)
|
||||
.attr("x2", innerWidth)
|
||||
.attr("y1", y(0))
|
||||
.attr("y2", y(0))
|
||||
.attr("stroke", "#000")
|
||||
.attr("stroke-opacity", 0.2);
|
||||
|
||||
// Add gridlines
|
||||
g.append("g")
|
||||
.attr("class", "grid-lines")
|
||||
.selectAll("line")
|
||||
.data(y.ticks(5))
|
||||
.join("line")
|
||||
.attr("x1", 0)
|
||||
.attr("x2", innerWidth)
|
||||
.attr("y1", d => y(d))
|
||||
.attr("y2", d => y(d))
|
||||
.attr("stroke", "#ccc")
|
||||
.attr("stroke-opacity", 0.5);
|
||||
|
||||
// Create a line generator
|
||||
const line = d3.line()
|
||||
.x(d => x(d.x))
|
||||
.y(d => y(d.y));
|
||||
|
||||
// Group to contain the basis function lines
|
||||
const linesGroup = g.append("g")
|
||||
.attr("class", "basis-functions");
|
||||
|
||||
// Function to update the chart with new data
|
||||
function updateChart(data) {
|
||||
// Group data by basis function
|
||||
const groupedData = d3.group(data, d => d.b);
|
||||
|
||||
// Update the chart title
|
||||
svg.select(".chart-title")
|
||||
.text(`B-spline Basis Functions (${selectedKnots} knots, μ = ${selectedMu})`);
|
||||
|
||||
// Update or create paths
|
||||
const paths = linesGroup.selectAll("path")
|
||||
.data(Array.from(groupedData.values()));
|
||||
|
||||
// Remove paths that are no longer needed
|
||||
paths.exit().remove();
|
||||
|
||||
// Add new paths
|
||||
paths.enter()
|
||||
.append("path")
|
||||
.attr("fill", "none")
|
||||
.attr("stroke-width", 3)
|
||||
.attr("stroke", (_, i) => color(i))
|
||||
.merge(paths) // Merge with existing paths for transition
|
||||
.transition()
|
||||
.duration(750)
|
||||
.attr("d", line);
|
||||
}
|
||||
|
||||
// Store the update function
|
||||
svg.node().update = updateChart;
|
||||
|
||||
// Initial render
|
||||
updateChart(filteredBspline);
|
||||
|
||||
return svg.node();
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user