Get alpha-shape (concave hull) of a set of points using Julia - julia

I want to compute the alpha-shape (or even only the concave hull) of a set of points using Julia. In other questions they have solved this problem in python by using Delaunay tesselation Boundary enclosing a given set of points .
This package in Julia can get a Delaunay tesselation https://github.com/JuliaGeometry/VoronoiDelaunay.jl (though I am not sure if it is updated for julia v0.7).
I am wondering if there is an implementation already for julia v0.7 that can get eh alpha-shape, or even just the concave hull of a set of points.
Alternatively, is there a way to efficiently call python (scipy.spatial.Delaunay) to do the job?

VoronoiDelaunay.jl works with Julia 1.0 and 1.1. It should also work with Julia 0.7.
VoronoiDelaunay.jl has some numerical restrictions, i.e. (1.0+eps(), 2.0-eps()), on coordinates so you may need to re-scale your data points.
To create a DelaunayTesselation with your own point type, make sure your type is a subtype of AbstractPoint2D, that is <: AbstractPoint2D, and defines getx, and gety methods.
The following example code, I believe, finds what you call the Concave Hull of a set of points using DelaunayTesselation and plots the result. It basically uses the same algorithm in this answer. You may easily edit the code to get the alpha shape.
I did not wrap some code snippets into a function. If you need high performance, please do so. I used === while checking for equality of points which actually checks if two points are the same object (i.e. address in memory). If you somehow end up in a code which breaks this part, you can extend == and use it instead of ===.
using Random, VoronoiDelaunay, Plots
import Base.==
struct MyEdge{T<:AbstractPoint2D}
_a::T
_b::T
end
==(e1::MyEdge{T}, e2::MyEdge{T}) where {T<:AbstractPoint2D} = ((e1._a === e2._a) && (e1._b === e2._b)) || ((e1._b === e2._a) && (e2._b === e1._a))
###==(p1::T, p2::T) where {T<:AbstractPoint2D} = (getx(p1) == getx(p2)) && (gety(p1) == gety(p2))
### Create a Delaunay tesselation from random points
tess = DelaunayTessellation2D(46)
for _ in 1:23
push!(tess, Point2D(rand()+1, rand()+1))
end
edges = MyEdge[]
function add_edge!(edges, edge)
i = findfirst(e -> e == edge, edges)
if isnothing(i) # not found
push!(edges, edge)
else # found so not outer, remove this edge
deleteat!(edges, i)
end
end
for trig in tess
a, b, c = geta(trig), getb(trig), getc(trig)
add_edge!(edges, MyEdge(b, c))
add_edge!(edges, MyEdge(a, b))
add_edge!(edges, MyEdge(a, c))
end
### PLOT
x, y = Float64[], Float64[] # outer edges
for edge in edges
push!(x, getx(edge._a))
push!(x, getx(edge._b))
push!(x, NaN)
push!(y, gety(edge._a))
push!(y, gety(edge._b))
push!(y, NaN)
end
xall, yall = getplotxy(delaunayedges(tess)) # all the edges
plot(xall, yall, color=:blue, fmt=:svg, size=(400,400))
plot!(x, y, color=:red, linewidth=3, opacity=0.5)

Related

Change point position in Julia

using Plots
p_arr = Array{Plots.Plot{Plots.GRBackend}}(undef,10,10);
x=5;
y=10;
p_arr[1,1] = scatter!([x],[y],markersize=5,legend=false,markercolor = :green, showaxis =
false,size=(500,500));
p_arr[1,2] = scatter!([x],[y],markersize=5,legend=false,markercolor = :green, showaxis =
false,size=(500,500));
this is a very simple code of storing a point plot into an array. I just want to know how to change the x and y coordination for this point by the object it is stored in ?. in other words, I want to set x and y values by the object itself.
is there a better way to do this.
I am new to julia and I do not know where to search
While I'm not quite sure what you'll need for your end use case, storing an array of Plots.jl Plots.Plots has a lot of overhead and will not make it at all easy to modify the underlying points.
One approach that could be dramatically simpler would be to work directly with an array of Points instead. For example, let us first define:
struct Point{T}
x::T
y::T
end
then you have a type which can represent just an x-y point by itself. You can make an array of them:
p_arr = Array{Point{Int64}}(undef, 10, 10) # uninitialized, Int64
or to make this a little more interesting, an array of random points:
julia> p_arr = Point.(rand(10,10), rand(10,10))
10×10 Matrix{Point{Float64}}:
Point{Float64}(0.561232, 0.39038) … Point{Float64}(0.0564275, 0.851144)
⋮ ⋱
Point{Float64}(0.804435, 0.0717767) Point{Float64}(0.110489, 0.670536)
If you want to be able to plot these, then let's define some functions to let Plots.jl know how to plot our Point type:
using Plots
import Plots.scatter
scatter(p::Point, args...; kwargs...) = scatter([p.x], [p.y], args...; kwargs...)
scatter(p::AbstractArray{<:Point}, args...; kwargs...) = scatter(map(p->p.x, p), map(p->p.y, p), args...; kwargs...)
# You can do the same for `scatter!` if you want that version too
then you can write
scatter(p_arr, label="", framestyle=:box)
to obtain a plot that looks like:
(note that each column gets its own series in Plots.jl, hence the multiple colors; call vec on your matrix of Points first if you don't want that)
Now, say you want to move one of those points. Let's say the first one:
julia> p_arr[1,1] = Point(5.0, 10.0)
Point{Float64}(5.0, 10.0)
Then plotting again
scatter(p_arr, label="", framestyle=:box)
will yield

simulating the collision of particles in Julia

I would like to simulate the collision of particles inside a box.
To be more specific I want to create a function (lets call it collision!), that updates the particles velocities after each interaction, like shown in the image.
I defined the particles (with radius equal 1) as followed:
mutable struct Particle
pos :: Vector{Float64}
vel :: Vector{Float64}
end
p = Particle( rand(2) , rand(2) )
# example for the position
p.pos
> 2-element Vector{Float64}:
0.49339012018408135
0.11441734325871078
And for the collision
function collision!(p1::Particle, p2::Particle)
# ... #
return nothing
end
The main idea is that when two particles collide, they "exchange" their velocity vector that is parallel to the particles centers (vector n hat).
In order to do that, one would need to transform the velocity vectors to the orthonormal basis of the collision normal (n hat).
Then exchange the parallel component and rotate it in the original basis back.
I think I got the math right but I am not sure how to implement it in the code
With the caveat that I have not checked the math at all, one implementation for the 2d case you provide might be along the lines of:
struct Particle
pos :: Vector{Float64}
vel :: Vector{Float64}
end
p1 = Particle( rand(2) , rand(2) )
p2 = Particle( rand(2) , rand(2) )
function collision!(p1::Particle, p2::Particle)
# Find collision vector
n = p1.pos - p2.pos
# Normalize it, since you want an orthonormal basis
n ./= sqrt(n[1]^2 + n[2]^2)
# Construct M
M = [n[1] n[2]; -n[2] n[1]]
# Find transformed velocity vectors
v1ₙ = M*p1.vel
v2ₙ = M*p2.vel
# Swap first component (or should it be second? Depends on how M was constructed)
v1ₙ[1], v2ₙ[1] = v2ₙ[1], v1ₙ[1]
# Calculate and store new velocity vectors
p1.vel .= M'*v1ₙ
p2.vel .= M'*v2ₙ
return nothing
end
A few points:
You don't need a mutable struct; just a plain struct will work fine since the Vector itself is mutable
This implementation has a lot of excess allocations that you could avoid if you could work either in-place or perhaps more feasibly on the stack (for example, using StaticArrays of some sort instead of base Arrays as the basis for your position and velocity vectors). In-place actually might not be too hard either if you just make another struct (say "CollisionEvent") which holds preallocated buffers for M, n, v1n and v2n, and pass that to the collision! function as well.
While I have not dived in to see, one might be able to find useful reference implementations for this type of collision in a molecular dynamics package like https://github.com/JuliaMolSim/Molly.jl

How do I write a piecewise Differential Equation in Julia?

I am new to Julia, I would like to solve this system:
where k1 and k2 are constant parameters. However, I=0 when y,0 or Ky otherwise, where k is a constant value.
I followed the tutorial about ODE. The question is, how to solve this piecewise differential equation in DifferentialEquations.jl?
Answered on the OP's cross post on Julia Discourse; copied here for completeness.
Here is a (mildly) interesting example $x''+x'+x=\pm p_1$ where the sign of $p_1$ changes when a switching manifold is encountered at $x=p_2$. To make things more interesting, consider hysteresis in the switching manifold such that $p_2\mapsto -p_2$ whenever the switching manifold is crossed.
The code is relatively straightforward; the StaticArrays/SVector/MVector can be ignored, they are only for speed.
using OrdinaryDiffEq
using StaticArrays
f(x, p, t) = SVector(x[2], -x[2]-x[1]+p[1]) # x'' + x' + x = ±p₁
h(u, t, integrator) = u[1]-integrator.p[2] # switching surface x = ±p₂;
g(integrator) = (integrator.p .= -integrator.p) # impact map (p₁, p₂) = -(p₁, p₂)
prob = ODEProblem(f, # RHS
SVector(0.0, 1.0), # initial value
(0.0, 100.0), # time interval
MVector(1.0, 1.0)) # parameters
cb = ContinuousCallback(h, g)
sol = solve(prob, Vern6(), callback=cb, dtmax=0.1)
Then plot sol[2,:] against sol[1,:] to see the phase plane - a nice non-smooth limit cycle in this case.
Note that if you try to use interpolation of the resulting solution (i.e., sol(t)) you need to be very careful around the points that have a discontinuous derivative as the interpolant goes a little awry. That's why I've used dtmax=0.1 to get a smoother solution output in this case. (I'm probably not using the most appropriate integrator either but it's the one that I was using in a previous piece of code that I copied-and-pasted.)

How to draw graph of Gauss function?

Gauss function has an infinite number of jump discontinuities at x = 1/n, for positive integers.
I want to draw diagram of Gauss function.
Using Maxima cas I can draw it with simple command :
f(x):= 1/x - floor(1/x); plot2d(f(x),[x,0,1]);
but the result is not good ( near x=0 it should be like here)
Also Maxima claims:
plot2d: expression evaluates to non-numeric value somewhere in plotting
range.
I can define picewise function ( jump discontinuities at x = 1/n, for positive integers )
so I tried :
define( g(x), for i:2 thru 20 step 1 do if (x=i) then x else (1/x) - floor(1/x));
but it don't works.
I can also use chebyshew polynomials to aproximate function ( like in : A Graduate Introduction to Numerical Methods From the Viewpoint of Backward Error Analysis by Corless, Robert, Fillion, Nicolas)
How to do it properly ?
For plot2d you can set the adapt_depth and nticks parameters. The default values are 5 and 29, respectively. set_plot_option() (i.e. with no argument) returns the current list of option values. If you increase adapt_depth and/or nticks, then plot2d will use more points for plotting. Perhaps that makes the figure look good enough.
Another way is to use the draw2d function (in the draw package) and explicitly tell it to plot each segment. We know that there are discontinuities at 1/k, for k = 1, 2, 3, .... We have to decide how many segments to plot. Let's say 20.
(%i6) load (draw) $
(%i7) f(x):= 1/x - floor(1/x) $
(%i8) makelist (explicit (f, x, 1/(k + 1), 1/k), k, 1, 20);
(%o8) [explicit(f,x,1/2,1),explicit(f,x,1/3,1/2),
explicit(f,x,1/4,1/3),explicit(f,x,1/5,1/4),
explicit(f,x,1/6,1/5),explicit(f,x,1/7,1/6),
explicit(f,x,1/8,1/7),explicit(f,x,1/9,1/8),
explicit(f,x,1/10,1/9),explicit(f,x,1/11,1/10),
explicit(f,x,1/12,1/11),explicit(f,x,1/13,1/12),
explicit(f,x,1/14,1/13),explicit(f,x,1/15,1/14),
explicit(f,x,1/16,1/15),explicit(f,x,1/17,1/16),
explicit(f,x,1/18,1/17),explicit(f,x,1/19,1/18),
explicit(f,x,1/20,1/19),explicit(f,x,1/21,1/20)]
(%i9) apply (draw2d, %);
I have made a list of segments with ending points. The result is :
and full code is here
Edit: smaller size with shorter lists in case of almost straight lines,
if (n>20) then iMax:10 else iMax : 250,
in the GivePart function

Vector math, finding coördinates on a planar between 2 vectors

I am trying to generate a 3d tube along a spline. I have the coördinates of the spline (x1,y1,z1 - x2,y2,z2 - etc) which you can see in the illustration in yellow. At those points I need to generate circles, whose vertices are to be connected at a later stadium. The circles need to be perpendicular to the 'corners' of two line segments of the spline to form a correct tube. Note that the segments are kept low for illustration purpose.
[apparently I'm not allowed to post images so please view the image at this link]
http://img191.imageshack.us/img191/6863/18720019.jpg
I am as far as being able to calculate the vertices of each ring at each point of the spline, but they are all on the same planar ie same angled. I need them to be rotated according to their 'legs' (which A & B are to C for instance).
I've been thinking this over and thought of the following:
two line segments can be seen as 2 vectors (in illustration A & B)
the corner (in illustraton C) is where a ring of vertices need to be calculated
I need to find the planar on which all of the vertices will reside
I then can use this planar (=vector?) to calculate new vectors from the center point, which is C
and find their x,y,z using radius * sin and cos
However, I'm really confused on the math part of this. I read about the dot product but that returns a scalar which I don't know how to apply in this case.
Can someone point me into the right direction?
[edit]
To give a bit more info on the situation:
I need to construct a buffer of floats, which -in groups of 3- describe vertex positions and will be connected by OpenGL ES, given another buffer with indices to form polygons.
To give shape to the tube, I first created an array of floats, which -in groups of 3- describe control points in 3d space.
Then along with a variable for segment density, I pass these control points to a function that uses these control points to create a CatmullRom spline and returns this in the form of another array of floats which -again in groups of 3- describe vertices of the catmull rom spline.
On each of these vertices, I want to create a ring of vertices which also can differ in density (amount of smoothness / vertices per ring).
All former vertices (control points and those that describe the catmull rom spline) are discarded.
Only the vertices that form the tube rings will be passed to OpenGL, which in turn will connect those to form the final tube.
I am as far as being able to create the catmullrom spline, and create rings at the position of its vertices, however, they are all on a planars that are in the same angle, instead of following the splines path.
[/edit]
Thanks!
Suppose you have a parametric curve such as:
xx[t_] := Sin[t];
yy[t_] := Cos[t];
zz[t_] := t;
Which gives:
The tangent vector to our curve is formed by the derivatives in each direction. In our case
Tg[t_]:= {Cos[t], -Sin[t], 1}
The orthogonal plane to that vector comes solving the implicit equation:
Tg[t].{x - xx[t], y - yy[t], z - zz[t]} == 0
In our case this is:
-t + z + Cos[t] (x - Sin[t]) - (y - Cos[t]) Sin[t] == 0
Now we find a circle in that plane, centered at the curve. i.e:
c[{x_, y_, z_, t_}] := (x - xx[t])^2 + (y - yy[t])^2 + (z - zz[t])^2 == r^2
Solving both equations, you get the equation for the circles:
HTH!
Edit
And by drawing a lot of circles, you may get a (not efficient) tube:
Or with a good Graphics 3D library:
Edit
Since you insist :) here is a program to calculate the circle at junctions.
a = {1, 2, 3}; b = {3, 2, 1}; c = {2, 3, 4};
l1 = Line[{a, b}];
l2 = Line[{b, c}];
k = Cross[(b - a), (c - b)] + b; (*Cross Product*)
angle = -ArcCos[(a - b).(c - b)/(Norm[(a - b)] Norm[(c - b)])]/2;
q = RotationMatrix[angle, k - b].(a - b);
circle[t_] := (k - b)/Norm[k - b] Sin#t + (q)/Norm[q] Cos#t + b;
Show[{Graphics3D[{
Red, l1,
Blue, l2,
Black, Line[{b, k}],
Green, Line[{b, q + b}]}, Axes -> True],
ParametricPlot3D[circle[t], {t, 0, 2 Pi}]}]
Edit
Here you have the mesh constructed by this method. It is not pretty, IMHO:
I don't know what your language of choice is, but if you speak MatLab there are already a few implementations available. Even if you are using another language, some of the code might be clear enough to inspire a reimplementation.
The key point is that if you don't want your tube to twist when you connect the vertices, you cannot determine the basis locally, but need to propagate it along the curve. The Frenet frame, as proposed by jalexiou, is one option but simpler stuff works fine as well.
I did a simple MatLab implementation called tubeplot.m in my formative years (based on a simple non-Frenet propagation), and googling it, I can see that Anders Sandberg from kth.se has done a (re?)implementation with the same name, available at http://www.nada.kth.se/~asa/Ray/Tubeplot/tubeplot.html.
Edit:
The following is pseudocode for the simple implementation in tubeplot.m. I have found it to be quite robust.
The plan is to propagate two normals a and b along the curve, so
that at each point on the curve a, b and the tangent to the curve
will form an orthogonal basis which is "as close as possible" to the
basis used in the previous point.
Using this basis we can find points on the circumference of the tube.
// *** Input/output ***
// v[0]..v[N-1]: Points on your curve as vectors
// No neighbours should overlap
// nvert: Number of vertices around tube, integer.
// rtube: Radius of tube, float.
// xyz: (N, nvert)-array with vertices of the tube as vectors
// *** Initialization ***
// 1: Tangent vectors
for i=1 to N-2:
dv[i]=v[i+1]-v[i-1]
dv[0]=v[1]-v[0], dv[N-1]=v[N-1]-v[N-2]
// 2: An initial value for a (must not be pararllel to dv[0]):
idx=<index of smallest component of abs(dv[0])>
a=[0,0,0], a[idx]=1.0
// *** Loop ***
for i = 0 to N-1:
b=normalize(cross(a,dv[i]));
a=normalize(cross(dv[i],b));
for j = 0 to nvert-1:
th=j*2*pi/nvert
xyz[i,j]=v[i] + cos(th)*rtube*a + sin(th)*rtube*b
Implementation details: You can probably speed up things by precalculating the cos and sin. Also, to get a robust performance, you should fuse input points closer than, say, 0.1*rtube, or a least test that all the dv vectors are non-zero.
HTH
You need to look at Fenet formulas in Differential Geometry. See figure 2.1 for an example with a helix.
Surfaces & Curves
Taking the cross product of the line segment and the up vector will give you a vector at right-angles to them both (unless the line segment points exactly up or down) which I'll call horizontal. Taking the cross product of horizontal and the line segment with give you another vector that's at right angles to the line segment and the other one (let's call it vertical). You can then get the circle coords by lineStart + cos theta * horizontal + sin theta * vertical for theta in 0 - 2Pi.
Edit: To get the points for the mid-point between two segments, use the sum of the two line segment vectors to find the average.

Resources