-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
2D irregular interpolation #118
Comments
@vchuravy: Nice, thanks for locating that so quickly for me! Seems like it should be able to do what we need. |
This doesn't have to be limited to 2d: the same strategy works for polyhedra in N dimensions. |
@timholy I was hoping you'd jump onto this thread with info like that :) Do you know if this method has a name, so I can read up on it? If not, could you outline for me how to generalize the calculation given a set of n corners? Such a generalization would also require a general implementation for finding a hyper-tetrahedron mesh, which is definitiely out-of-scope for this package (and, from what I can find with a few quick searches, doesn't seem to exist in Julia yet)... |
I don't know if it has a name. I would search for terms like "piecewise linear polyhedra voronoi" or something. I'm attaching an old (>10 years) document I once wrote up before I realized this stuff must be well known. It was really targeted at defining PDEs on irregular grids, so it may not be entirely relevant, but for what it's worth... As far as finding the mesh goes, perhaps https://github.com/davidavdav/CHull.jl? (Matlab uses the QHull library for its |
OOO, how's this going? Can I help? I need it.. See here. |
I'm pretty sure no one is working on this. Would be great to have someone contribute it! |
+1 to please add this - highly useful! |
I don't really have any knowledge of this (except what you can read on Wikipedia) or the internals of It's not particularly fast but it seems to work for my problems. If this is something that could fit into Here is the code. Example usage would be:
|
Is there any update on this? |
Thanks to the excellent explanation and breakdown by @tomasaschan at the beginning of this issue, this ~6 years-old issue seems astonishingly simple to solve for linear interpolations. I must be missing something or does this look good as a starting point for a PR? using Statistics, LinearAlgebra
using Delaunay, GeometryBasics # we need a Delaunay library and something that can detect if a point is in a triangle
import GeometryBasics.Ngon # just for type parameters' sake
# this struct keeps both the 2D and 3D versions of the triangles
struct Ungridded2D{T<:Real}
triangles::Vector{Pair{Ngon{2, T, 3, Point2{T}}, Ngon{3, T, 3, Point3{T}}}}
function Ungridded2D{T}(xy, z) where T <: Real
mat = hcat(first.(xy), last.(xy))
m = delaunay(mat)
triangles = [Triangle(xy[i]...) => Triangle((Point3{T}(xy[j]..., z[j]) for j in i)...) for i in eachrow(m.simplices)]
return new(triangles)
end
end
Ungridded2D(xy::Vector{Point2{T}}, z) where {T<:Real} = Ungridded2D{T}(xy, z)
function (ug::Ungridded2D)(xy)
for (tri2, tri3) in ug.triangles
if xy in tri2 # check if the point is inside the 2D triangle
P, Q, R = tri3 # use the 3D triangle in @tomasaschan's algorithm
A = (Q-P) × (R-P)
d = A ⋅ P
x, y = xy
z = (d - (x * A[1] + y * A[2])) / A[3]
return z
end
end
return NaN # defaulting to NaNs for extrapolation, for now
end
fun(xy) = 3*xy[1] - 2*xy[2] + 10 # a plane
n = 10
xy = rand(Point{2, Float64}, n)
append!(xy, [Point2(0,0), Point2(0,1), Point2(1,0), Point2(1,1)]) # required to avoid the extrapolated NaNs, just for this MWE
z = fun.(xy)
itp = Ungridded2D(xy, z)
all(1:1000) do _
xy = rand(Point2{Float64})
z = fun(xy)
ẑ = itp(xy)
isapprox(z, ẑ)
end # is true
using BenchmarkTools
@benchmark Ungridded2D($xy, $z)
# BenchmarkTools.Trial: 4895 samples with 1 evaluation.
# Range (min … max): 974.792 μs … 16.887 ms ┊ GC (min … max): 0.00% … 40.20%
# Time (median): 995.583 μs ┊ GC (median): 0.00%
# Time (mean ± σ): 1.020 ms ± 498.802 μs ┊ GC (mean ± σ): 0.66% ± 1.28%
#
# ▂▅▆▇███▇▆▅▅▄▃▃▂▁▁▁▁ ▂▂▃▃▃▂▁▁ ▂
# ▃▄▆█████████████████████████████▇████▇▇▇▆▆▇▇▆▇▆▇▇▇▅▆▅▅▅▅▁▅▅▄▅ █
# 975 μs Histogram: log(frequency) by time 1.11 ms <
#
# Memory estimate: 47.78 KiB, allocs estimate: 1064.
@benchmark itp($(rand(Point2{Float64})))
# BenchmarkTools.Trial: 10000 samples with 976 evaluations.
# Range (min … max): 69.117 ns … 1.763 μs ┊ GC (min … max): 0.00% … 94.65%
# Time (median): 69.758 ns ┊ GC (median): 0.00%
# Time (mean ± σ): 72.308 ns ± 52.368 ns ┊ GC (mean ± σ): 2.26% ± 2.99%
#
# ▆▆█▇▄▄▂▁ ▃▆▆▃▂ ▃▃▁ ▁▂▁ ▁▁▁ ▄▅▁ ▂
# █████████████████████▇███████████████▇▆▆▆▅▄▆▅▄▄▃▄▅▅▅▄▅▅▆▄▄▂ █
# 69.1 ns Histogram: log(frequency) by time 77.3 ns <
#
# Memory estimate: 48 bytes, allocs estimate: 2. |
The origin of this precedes my time as maintainer. What I am leaning towards here is creating subpackages to avoid the dependencies of Interpolations.jl incrementally increasing over time. Another thought is whether this better fits ScatteredInterpolations.jl. Overall the idea for expanding Interpolations.jl would be to maintain a unified interface for multiple interpolation schemes. In particular, we would the new interpolation type to derive from AbstractInterpolation and be able to use the other BSpline schemes in the future. If that sounds fine to you, open up a pull request and then we can discuss the details. |
For the subpackage, the idea would be to create a package in a |
Hi @mkitti! As to the original issue here, it seems like the following packages:
implement some kind of interpolation for un-gridded data. If |
I explained the lib folder and subpackages in this post: https://discourse.julialang.org/t/can-someone-please-explain-the-lib-directory/84238?u=mkitti Essentially this would be another package hosted in this repository to aid co-development. Because it would be a separate package, it would have fully separate dependencies. It would also have independent precompilation times. However, we could fully integrate CI and the documentation. |
I explained the lib folder and subpackages in this post: https://discourse.julialang.org/t/can-someone-please-explain-the-lib-directory/84238?u=mkitti Essentially this would be another package hosted in this repository to aid co-development. Because it would be a separate package, it would have fully separate dependencies. However, we could fully integrate CI and the documentation. |
A future direction of Interpolations.jl might be to break it up into smaller packages, and provide a meta package that depends on all of them. Perhaps Interpolations.jl becomes the meta package in the future. |
We recently ported this one to Julia https://github.com/fatteneder/CloughTocher2DInterpolation.jl and will soon be using it in https://github.com/MakieOrg/TopoPlots.jl. |
I have worked out a scheme for linear interpolation of functions
f(x,y)
that I think might be fast enough to be useful (and might be possible to optimize really well with the same metaprogramming tricks we use on B-splines). It's probably not novel, but I don't know what it's called, so I'm just going to describe it.Given lists of points
(x,y,z)
(maybe on the form of equal-length listsxs
,ys
andzs
) representing a discretization of a functionz = f(x,y)
, we can find the value ofz
at an arbitrary point inside the domain using the following steps:Find the three corners of the triangle that contains the sought point
(x,y)
. Let's call themP
,Q
andR
.Evaluate
z
as follows:(These relations follow from finding the equation of the plane through the three points, and then solving for
z
.)I would love to have this functionality in the package, and I could take a stab at an implementation (at least as a POC) but I have no idea how to build the triangular mesh. Maybe a Delaunay triangulation is what we want? Is there a package that does that?
The text was updated successfully, but these errors were encountered: