如何将自定义数据类型转换为数组数组

时间:2019-08-04 19:48:15

标签: julia

我是Julia的新手,我在使用类型方法进行编程时遇到了一些困难。

我想从文件中加载3D网格进行练习,并且我做了一些自定义类型来存储它。

这是我的类型:

struct Vertex
    x::Number
    y::Number
    z::Number
    Vertex(x::Number, y::Number, z::Number) = new(x, y, z)
    Vertex(t::Tuple{Number, Number, Number}) = new(t[1], t[2], t[3])

    Vertex(x::Number, y::Number) = new(x, y, 0)
    Vertex(t::Tuple{Number, Number}) = new(t[1], t[2], 0)

    Vertex(x::Number) = new(x, 0, 0)
    Vertex(t::Tuple{Number}) = new(t[1], 0, 0)

    Vertex() = new(0, 0, 0)
    Vertex(t::Tuple{}) = new(0, 0, 0)
end

struct Mesh
    t::Vector{Vertex} # List of triangles
    f::Vector{Vertex} # List of faces
    n::Vector{Vertex} # List of normals
    Mesh(t::Vertex, f::Vertex) = new([t], [f], [])
    Mesh(t::Vector{Vertex}, f::Vector{Vertex}, n::Vector{Vertex}) = new(t, f, n)
    Mesh(t::Vector{Vertex}, f::Vector{Vertex}, n::Vector) = new(t, f, n)

    Mesh(t::Vector, f::Vector, n::Vector) = new(t, f, n)

    #Mesh(t::Triangle) = new([t], [])
    #Mesh(t::Vector{Triangle}) = new(t, [])
end

我可以有效地以我的 Mesh 类型加载网格。

现在,我想使用 PyPlot 中的 plot_trisurf 方法对其进行绘制。但是,此方法需要一个数组数组,我不确定我的处理方法是否正确:

function plotMesh(M)
    Xv = map(e -> e.x, M.t[:])
    Yv = map(e -> e.x, M.t[:])
    Zv = map(e -> e.x, M.t[:])
    Fv = map(e -> (e.x, e.y, e.z), M.f[:])

    plot_trisurf(Xv, Yv, Zv, triangles=Fv, alpha=1)
    gca()[:projection] = "3d"
end

Q

  • Xv,Yv,Zv目前感觉不正确,
  • 和Fv根本不起作用。 [更正->参见编辑]
  • 什么是最好的方法?
  • 我的字体设计正确吗?还是应该将其更改为更合适的内容?

谢谢


[编辑] 经过更多测试之后,我终于设法使它工作了,但是我仍然不确定这是否是在Julia中做事的最佳方法,还是我的类型系统是否是一个好的系统。

function plotMesh(M::Mesh)
    Xv = map(e -> e.x, M.t[:])
    Yv = map(e -> e.y, M.t[:])
    Zv = map(e -> e.z, M.t[:])
    Fv = map(e -> [Int(e.x)-1, Int(e.y)-1, Int(e.z)-1], M.f[:])
    print(size(Xv))
    print(size(Fv))

    plot_trisurf(Xv, Yv, Zv, triangles=Fv)
    gca()[:projection] = "3d"
end

First 3D plot in Julia


[编辑] 顶点和法线通常是浮点数,而面是整数。

我正在使用的对象是bunny.obj 我在结构中加载对象的代码是:

function read_obj(filename::String)
    v = []
    f = []
    n = []

    tof(x) = parse(Float64, x)

    open(filename) do file
       for line in eachline(file)
            l = split(line, ' ')
            if l[1] ∈ ["v", "f", "n"]
                values = (tof(l[2]), tof(l[3]), tof(l[4]))
                if l[1] == "v"
                    push!(v, Vertex(values))
                elseif l[1] == "f"
                    faces = (Int(values[1]), Int(values[2]), Int(values[3]))
                    push!(f, Vertex(faces))
                elseif l[1] == "n"
                    push!(n, Vertex(values))
                end
            end
        end
    end

    return Mesh(v, f, n)
end

我加载对象的方式肯定不是最好的方式。如果您有任何提高我的技能的材料,请随时分享:)

1 个答案:

答案 0 :(得分:2)

首先,我将像这样更改Vertex的定义(似乎在下面,您要求条目为整数,如果不是,则可以将Integer更改为Number

struct Vertex{T<:Integer}
    x::T
    y::T
    z::T
end

Vertex(x::T=0, y::T=zero(T)) where {T<:Integer} = Vertex(x,y,zero(T))
Vertex(t::Tuple) = Vertex(t...)

接下来在Mesh中,您可以使用StructArrays.jl程序包(这样,您可以轻松地将Vertex的字段作为矢量访问):

using StructArrays

struct Mesh{S<:StructArray, T}
    t::S
    f::S
    n::S
    function Mesh(t::Vector{T}, f::Vector{T}, n::Vector{T}) where {T<:Vertex}
        st, sf, sn = StructArray(t), StructArray(f), StructArray(n)
        new{typeof(st), T}(st, sf, sn)
    end
end

Mesh(t::T, f::T) where {T<:Vertex} = Mesh([t], [f], T[])

现在您可以将绘图功能定义为:

function plotMesh(M::Mesh{S, T}) where {S,T}
    Fv = eachrow([M.f.x M.f.y M.f.z] .- one(T))
    print(size(M.t.x))
    print(size(Fv))
    plot_trisurf(M.t.x, M.t.y, M.t.z, triangles=Fv)
    gca()[:projection] = "3d"
end

注1:所有代码均确保所有结构都对具体类型进行操作,因此该代码将比使用抽象类型(如Number)更快。另外,我还要确保所有条目都具有相同的类型。

注2:由于您没有提供用于测试代码的数据(因此请告诉我,如果此代码中有任何失败),我从脑海写了这封信。严格来说,您不必使用StructArrays.jl即可达到目标,但我希望您同意使用它们可以使您的代码更具可读性。

相关问题