Skip to content

Conversation

@jjgomezcadenas
Copy link
Owner

This PR completes the first version of raytracking functions, which for the time being include only the definition of a Ray, the definition of a Cylinder and the propagation of a ray to a cylinder. This functions are useful to correct the LORS in the NEMA5 test by the absorption in the phantom, and thus compute correctly the sensitivity.

The package Polynomials was added previously to compute roots of the
ray propagator, but it turns out that hand calculation is more
convenient here. Dependency not necesary now, thus removed.
Testing notebooks are added to the notebook respository. The testing
notebook exercise some of the functions in the package but are not
intended or guaranteed to be complete. All tests in the notebooks (and
some times more) are included in the automatic testing.
Copy link
Collaborator

@andLaing andLaing left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some questions about naming and functionality. There seem to be raytracing packages already written but it's true that they're maybe a bit more complex than what we need.

"""
function hist1d(x::Vector{T}, bins::Vector{S}, norm=false) where {T<:Number,S<:Number}
h = fit(Histogram, x, bins)
h = StatsBase.fit(Histogram, x, bins)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't seem necessary, especially since it's not used consistently even in histos.jl

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may be a fossile. At some stage there was a naming conflict.

p::Vector{Float64}
e::Vector{Float64}
d::Vector{Float64}
u::Vector{Float64}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why have you chosen to change to e rather than p? And without changing the docstring.

Also, A ray for me should be point and unit direction so it's not so clear what the additional "Direction vector" is for.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to follow the nomenclature here:
https://www.cl.cam.ac.uk/teaching/1999/AGraphHCI/SMAG/node2.html

If one takes d as specifying a direction (not a unit direction) then one can compute the unit vector internally.

function Ray(p , d)
new(p, d, unit_vector(d,p))
function Ray(e , d)
new(e, d, unit_vector(d,e))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of unit_vector here contradicts its meaning below.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow you here

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The unit vector for a ray should be along the direction vector (so basically d / mag(d)). Your unit_vector function calculates a unit vector along the difference of the two inputs, this means that here you're effectively using the origin as if it were a direction and forming a triangle with the direction vector.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, seems there is also a mistake there.

function propagate_ray(r::Ray)
function xray(t::Real)
return r.p + t * r.d
return r.e + t * r.d
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the propagation to be correct the function should use a unit vector so by the Ray definition above it should be r.e + t * r.u

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think you are right.

Return a unit vector in the direction defined by points p0 and p1
"""
unit_vector(p0::Vector{Real}, p1::Vector{Real}) = (p1 - p0) ./ mag(p1 - p0)
unit_vector(p0::Vector{Float64}, p1::Vector{Float64}) = (p1 - p0) ./ norm(p1 - p0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Broadcasting unnecessary.

Seems unusual that this type of function isn't in LinearAlgebra or similar

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be, I didn't look.



length(c::Cylinder) = abs.(c.zmax - c.zmin)
cylinder_length(c::Cylinder) = abs.(c.zmax - c.zmin)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c.zmax and c.zmin are Float64, there's no broadcasting to be done.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YES! good catch

xray = ATools.propagate_ray(r)

# If no real roots return nothing
arg = b^2 - 4*a*c
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should have a protection here after calculating the determinant. Otherwise a ray that doesn't intersect the barrel anywhere will error on the sqrt

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will check, thanks

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will check



"""Computes intersection between a ray and a cylinder"""
function propagate(r::Ray, cy::Cylinder)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name is misleading again. The function seems to look for intersections between the ray and cylinder. i think that might be a better thing to base the name on. intersection or find_intersections?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, right again


# belong-to-cylinder condition
zc1 = cy.zmin < z1 < cy.zmax
zc2 = cy.zmin < z2 < cy.zmax
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me exactly what the function is meant to do. You keep both t1 and t2 which made me think that you wanted intersections for back-to-back gammas but then the function only returns one position.

If you assume that the origin is inside the cylinder (should be true in our application), positive t is the only relevant intersection if we only want to follow the ray. If both are possible but only one returned the meaning of the function is ambiguous. If you only want the forward intersections then a lot of the if else below can be removed in favour of taking the positive t. If you want a function that gives you the two/one/or no intersection(s) of the back-to-back gammas then I'd return both with nothing or missing in the case of no valid intersection.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the calculations for where the intersections should be. My question is what you want to return. If you only want the forward going ray then you can check if one of the t is positive, if you only want to consider origins within the cylinder.

In the file you link they already talk about t > 0 being the only really relevant root for a ray my question about what you want is inspired by the back-to-back nature of the specific problem the code is written for.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me see if I follow you. The argument is that the solution with t<0 also works, given that there are two back-to-back gammas?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you assume back-to-back gammas both t > 0 and t < 0 can be valid (if you are inside the cylinder you can only have one positive and 1 negative or neither defined). In the case that you want both I'd check each intersect for validity (intersect within the limits of interest) and return them both with something like a missing or nothing for any of them that aren't valid. If you're only interested in the forward ray (t > 0) you can already ignore one of them from earlier in the function and save yourself some of the if else



"""Returns True if point in end-caps of cyinder"""
in_endcaps(c:: Cylinder, p::Vector{Float64}) = isapprox(p[3], c.zmin) || isapprox(p[3], c.zmax)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This alone doesn't test if the point is in an endcap. Only that the one-dimensional position is level with the endcaps

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is what was intended, name probably not very well chosen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants