Reputation: 41
So what I want to do is something like: Define a type Point that is constructed from floats passed by the user. This is supposed to store the coordinates of a point. struct Point x::Float64 y::Float64 end This works well enough.
Now, I want to define a line segment that is defined as a vector of Point types which accepts two points from the user and creates a vector of 1000 points in between and assigns it to the line and constructs it. ''' s=Point(1,1) t=Point(8,9)
struct Line::Vector{Point}
s::Point
t::Point
Line(s,t)=new([Point(s.x+i*(t.x-s.x)/1000,s.y+i*(t.y-s.y)/1000) for i=0:1000])
end
''' I can't quite get it to work, trying variations of the code above. Could someone please guide me in the right direction?
Upvotes: 4
Views: 132
Reputation: 4370
The answer is actually simpler than you might think!
struct Point
x::Float64
y::Float64
end
struct Line
points::Vector{Point}
end
Line(s::Point, t::Point) = Line([Point(s.x+i*(t.x-s.x)/1000,s.y+i*(t.y-s.y)/1000) for i=0:1000]) # Aside: this is a function. You could also define it as a long-form function.
Then, for example
julia> s = Point(1,1)
Point(1.0, 1.0)
julia> t = Point(8,9)
Point(8.0, 9.0)
julia> Line(s,t)
Line(Point[Point(1.0, 1.0), Point(1.007, 1.008), Point(1.014, 1.016), Point(1.021, 1.024), Point(1.028, 1.032), Point(1.035, 1.04), Point(1.042, 1.048), Point(1.049, 1.056), Point(1.056, 1.064), Point(1.063, 1.072) … Point(7.937, 8.928), Point(7.944, 8.936), Point(7.951, 8.943999999999999), Point(7.958, 8.952), Point(7.965, 8.96), Point(7.972, 8.968), Point(7.979, 8.975999999999999), Point(7.986, 8.984), Point(7.993, 8.992), Point(8.0, 9.0)])
If you want to get the s and t back, then you can just grab them from the array of points. In fact, one "Julian" way to do this would be just to use multiple dispatch and add methods for the Line
type to first
and last
, e.g.
julia> import Base.first
julia> first(l::Line) = first(l.points)
first (generic function with 42 methods)
julia> import Base.last
julia> last(l::Line) = last(l.points)
last (generic function with 36 methods)
julia> first(l)
Point(1.0, 1.0)
julia> last(l)
Point(8.0, 9.0)
Now if you want to make this more general, to allow points of any type rather than just Float64
, then you could simply write
struct Point{T}
x::T
y::T
end
struct Line{T}
points::Vector{Point{T}}
end
instead, to make these structs parametric.
Upvotes: 1
Reputation: 42264
In some implementations you will want your Line
to behave as an actual Vector
in that case you need to inherit on the AbstractVector
type:
struct Point
x::Float64
y::Float64
end
struct Line <: AbstractVector{Point}
s::Point
t::Point
points::Vector{Point}
Line(s::Point,t::Point) =
new(s,t,Point.(range(s.x,t.x,length=100),range(s.y,t.y,length=100)))
end
Base.size(line::Line) = (length(line.points), )
Base.getindex(line::Line, ix) = line.points[ix]
I have slighlty shortened the code to generate points.
Moreover, you can see this required defining some basic methods (you might also want to add setindex
if you want to mutate the Vector
).
Now let us see how it works and how Line
behaves as a Vector
:
julia> line = Line(Point(0,1), Point(1,3));
100-element Line:
Point(0.0, 1.0)
Point(0.010101010101010102, 1.02020202020202)
Point(0.020202020202020204, 1.0404040404040404)
⋮
Point(0.9797979797979798, 2.95959595959596)
Point(0.98989898989899, 2.9797979797979797)
Point(1.0, 3.0)
julia> line[1], line[2], line[end]
(Point(0.0, 1.0), Point(0.010101010101010102, 1.02020202020202), Point(1.0, 3.0))
With this implementation first
and last
work as well (similarly to the other answer) so you might consider removing s
and t
from the struct (depends on your code):
julia> first(line), last(line)
(Point(0.0, 1.0), Point(1.0, 3.0))
Upvotes: 3