Skip to content

Commit

Permalink
Added RBF interpolation
Browse files Browse the repository at this point in the history
  • Loading branch information
flohorovicic committed Jan 8, 2024
1 parent 6856814 commit 5d3abdf
Show file tree
Hide file tree
Showing 7 changed files with 444 additions and 2 deletions.
141 changes: 141 additions & 0 deletions d3j/dist/rbf_interpolation.bundle.js

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion d3j/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion d3j/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"license": "MIT",
"dependencies": {
"@rainij/polynomial-regression-js": "^1.3.1",
"cubic-spline": "^3.0.3"
"cubic-spline": "^3.0.3",
"rbf": "^1.1.5"
},
"devDependencies": {
"webpack": "^5.89.0",
Expand Down
59 changes: 59 additions & 0 deletions d3j/rbf_interpolation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html>
<head>
<title>RBF Interpolation Visualization</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
<link rel="stylesheet" type="text/css" href="style_dark.css">
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
</head>
<body>
<div class="content">
<h1>RBF Interpolation Visualization</h1>
<p>Drag the points to see how the RBF interpolation changes.</p>

<div class="flex-container">
<div class="buttons-container">
<button id="homeBtn">Home</button>
<button id="infoBtn">Info</button>
<button id="CG3Btn">
<!-- <img src="./images/SA_schematics_fig_a_Zeichenfläche 1.png" alt="Icon" style="height: 20px; margin-right: 5px;"> -->
CG3
</button>
</div>

<div class="plot-container">
<svg id="rbfPlot" width="800" height="400" viewBox="0 0 1600 800"></svg>
</div>
</div>
</div>

<script type="module" src="./dist/rbf_interpolation.bundle.js"></script>

<!-- Info Modal -->
<div id="infoModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2>Radial Basis Function (RBF) Interpolation - Theory</h2>
<p>
RBF interpolation is a method of multivariate interpolation in which the interpolated surface is a sum of radial basis functions, each associated with a different point.
</p>
<p>
The general form of an RBF is:
</p>
<p>
\( f(x) = \sum_{i=1}^{n} \lambda_i \phi(\| x - x_i \|) \)
</p>
<p>
where \( \lambda_i \) are the weights, \( \phi \) is the radial basis function, \( x \) is the interpolation point, and \( x_i \) are the data points.
</p>
<p>
Common choices for \( \phi \) include Gaussian, Multiquadric, and Inverse Multiquadric functions.
</p>
</div>
</div>

<script src="./dist/modal.bundle.js"></script>

</body>
</html>
215 changes: 215 additions & 0 deletions d3j/rbf_interpolation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
// import { PolynomialRegressor } from '@rainij/polynomial-regression-js';


import Spline from 'cubic-spline';

const RBF = require('rbf');

const points = [
[0, 0],
[0, 100]
];

// values could be vectors of any dimensionality.
// The computed interpolant function will return values or vectors accordingly.
const values = [
0.0,
1.0
]

// RBF accepts a distance function as a third parameter :
// either one of the following strings or a custom distance function (defaults to 'linear').
//
// - linear: r
// - cubic: r**3
// - quintic: r**5
// - thin-plate: r**2 * log(r)
// - gaussian: exp(-(r/epsilon) ** 2)
// - multiquadric: sqrt((r/epsilon) ** 2 + 1)
// - inverse-multiquadric: 1 / sqrt((r/epsilon) ** 2 + 1)
//
// epsilon can be provided as a 4th parameter. Defaults to the average
// euclidean distance between points.
//
var rbf = RBF(points, values /*, distanceFunction, epsilon */);

console.log(rbf([0, 50])); // => 0.5

// Sample Data
// const data = [{x: 10, y: 20}, {x: 20, y: 40}]; //, ...]; // Replace with your data

// ************************************
// Create random points
// ************************************

function seededPRNG(seed) {
return function() {
seed = seed * 16807 % 2147483647;
return (seed - 1) / 2147483646;
};
}

// Set the seed for reproducibility
const seed = 12315; // Change this value for different results
const random = seededPRNG(seed);

// Generate 10 random x,y-pairs
const data = Array.from({ length: 10 }, () => ({
x: random() * 8 + 1, // Assuming you want x and y values in the range [10, 10]
y: random() * 2 + 4
}));

console.log(data);

// ************************************
// Create (x,y)-plot
// ************************************

const svg = d3.select("#rbfPlot");

const margin = { top: 40, right: 40, bottom: 60, left: 100 };
const viewBoxWidth = 1600;
const viewBoxHeight = 800;
const width = viewBoxWidth - margin.left - margin.right;
const height = viewBoxHeight - margin.top - margin.bottom;

const x = d3.scaleLinear().rangeRound([0, width]);
const y = d3.scaleLinear().rangeRound([height, 0]);



const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
// Set domain limits

// from data
// x.domain(d3.extent(data, d => d.x));
// y.domain(d3.extent(data, d => d.y));

// manually
x.domain([0, 10]);
y.domain([0, 10]);

// Create the X and Y axes with class names for future reference
const xAxis = g.append("g")
.attr("class", "x axis") // Assign class name
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x));

const yAxis = g.append("g")
.attr("class", "y axis") // Assign class name
.call(d3.axisLeft(y));

g.selectAll(".y.axis text")
.style("font-size", "30px"); // Update font size


g.selectAll(".x.axis text")
.style("font-size", "30px"); // Update font size


// Gridlines
g.append("g")
.attr("class", "grid")
.attr("transform", `translate(0,${height})`)
.call(d3.axisBottom(x)
.tickSize(-height)
.tickFormat(""));

g.append("g")
.attr("class", "grid")
.call(d3.axisLeft(y)
.tickSize(-width)
.tickFormat(""));



// **************************
// RBF function
// **************************

// Function to update spline curve
function updateRBF() {
// Sort the data by x-values
data.sort((a, b) => a.x - b.x);

// Extract sorted xs and ys
const xs = data.map(d => d.x);
const ys = data.map(d => d.y);

// RBF accepts a distance function as a third parameter :
// either one of the following strings or a custom distance function (defaults to 'linear').
//
// - linear: r
// - cubic: r**3
// - quintic: r**5
// - thin-plate: r**2 * log(r)
// - gaussian: exp(-(r/epsilon) ** 2)
// - multiquadric: sqrt((r/epsilon) ** 2 + 1)
// - inverse-multiquadric: 1 / sqrt((r/epsilon) ** 2 + 1)
//
// epsilon can be provided as a 4th parameter. Defaults to the average
// euclidean distance between points.

// create RBF function
var rbf = RBF(xs, ys, 'multiquadric', .5 /*, distanceFunction, epsilon */);

// Generate points for the curve at a higher resolution
const curvePoints = [];
const start = x.domain()[0];
const end = x.domain()[1];
const step = (end - start) / 500; // Adjust the number of points for smoothness

for (let i = start; i <= end; i += step) {
curvePoints.push({ x: i, y: rbf(i) });
}
// Draw the curve (or update if already drawn)
const line = d3.line()
.x(d => x(d.x))
.y(d => y(d.y));

const path = g.selectAll('.spline-curve')
.data([curvePoints]); // Bind the new curve data

path.enter().append('path')
.attr('class', 'spline-curve')
.merge(path)
.attr('fill', 'none')
.attr('stroke', 'green')
.attr('stroke-width', 3)
.attr('d', line);
}


// Initial drawing of the curve
updateRBF();

// ************************************
// Drag points
// ************************************

// Drag event handler
function dragged(event, d) {
const [newX, newY] = d3.pointer(event, this); // Get the correct coordinates

d.x = x.invert(newX); // Convert from screen to data coordinates
d.y = y.invert(newY); // Convert from screen to data coordinates

d3.select(this)
.attr('cx', x(d.x)) // Update the position using the data scale
.attr('cy', y(d.y));

// Update the spline curve after dragging a point
updateRBF();
}

// Apply drag behavior to the points
g.selectAll(".dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("cx", d => x(d.x))
.attr("cy", d => y(d.y))
.attr("r", 10)
.call(d3.drag().on("drag", dragged));


1 change: 1 addition & 0 deletions d3j/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
entry: {
script_interpolations: './script_interpolations.js',
spline_interpolation: './spline_interpolation.js',
rbf_interpolation: './rbf_interpolation.js',
bezier_curve: './bezier_curve.js',
bezier_spline: './bezier_spline.js',
modal: './modal.js'
Expand Down
11 changes: 11 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ <h1>Structural Geological Modeling: Algorithms and Implementations</h1>
</div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front">
RBF curve visualisation
</div>
<div class="tile-back">
<a href="./d3j/rbf_interpolation.html" class="tile">
<img width=200 src="./d3j/images/cubic_bezier.png" alt="Polynomial Regression"></a>
</div>
</div>
</div>
<div class="tile">
<div class="tile-inner">
<div class="tile-front">
Expand Down

0 comments on commit 5d3abdf

Please sign in to comment.