From 23857705b67f3bc451995e6c4751a1b8dd5972c2 Mon Sep 17 00:00:00 2001 From: Joaquim Date: Tue, 26 Nov 2024 20:34:59 +0000 Subject: [PATCH] Rework sort_visible_faces() to account for the FV recent changes. (#1597) * Add new member to GMTfv * Pass also the 'i_dz', which fundamental for color interpolation. * Fixes/improvements in the flatfv() fun. * Rework sort_visible_faces() to account for the FV recent changes. --- src/gmt_main.jl | 4 ++-- src/gmt_types.jl | 28 ++++++++++++++------------- src/psxy.jl | 47 +++++++++++++++++++++++++++------------------- src/solids.jl | 15 ++++++++++----- src/utils_types.jl | 10 +++++----- 5 files changed, 60 insertions(+), 44 deletions(-) diff --git a/src/gmt_main.jl b/src/gmt_main.jl index f457198045..7311f90a1f 100644 --- a/src/gmt_main.jl +++ b/src/gmt_main.jl @@ -1228,10 +1228,10 @@ function palette_init(API::Ptr{Nothing}, cpt::GMTcpt)::Ptr{GMT_PALETTE} rgb_diff = (cpt.cpt[j,4]-cpt.cpt[j,1], cpt.cpt[j,5]-cpt.cpt[j,2], cpt.cpt[j,6]-cpt.cpt[j,3], 0.0) z_low = cpt.range[j,1] z_high = cpt.range[j,2] - # GMT6.1 bug does not free "key" but frees "label" and does not see if memory is external. Hence crash or mem leaks + i_dz = 1. / (z_high - z_low) annot = (j == Pb.n_colors) ? 3 : 1 # Annotations L for all but last which is B(oth) - lut = GMT_LUT(z_low, z_high, glut.i_dz, rgb_low, rgb_high, rgb_diff, glut.hsv_low, glut.hsv_high, + lut = GMT_LUT(z_low, z_high, i_dz, rgb_low, rgb_high, rgb_diff, glut.hsv_low, glut.hsv_high, glut.hsv_diff, annot, glut.skip, glut.fill, C_NULL, C_NULL) unsafe_store!(Pb.data, lut, j) diff --git a/src/gmt_types.jl b/src/gmt_types.jl index 7ca7ed6f85..c44bce931c 100644 --- a/src/gmt_types.jl +++ b/src/gmt_types.jl @@ -298,24 +298,26 @@ GMTdataset(data::Array{Float32,2}) = The GMTfv struct is used to store a (mostly) triangulated mesh. The fields of this struct are: -- `verts::AbstractMatrix{T}`: Mx3 Matrix with the data vertices -- `faces`::Vector{<:AbstractMatrix{<:Integer}} A vector of matrices with the faces. Each row is a face -- `faces_view`::Vector{Matrix{Int}} A subset of `faces` with only the visible faces from a certain perspective -- `color`::Vector{Vector{String}} A vector with G option colors for each face -- `bbox`::Vector{Float64} The vertices BoundingBox -- `zscale`::Float64 A multiplicative factor to scale the z values -- `bfculling`::Bool If culling of invisible faces is wished. Default is true -- `isflat`::Vector{Bool} If this is a flat mesh. Default is false -- `proj4::String` Projection string in PROJ4 syntax (Optional) -- `wkt::String` Projection string in WKT syntax (Optional) -- `epsg::Int` EPSG projection code (Optional) +- `verts::AbstractMatrix{T}`: A Mx3 Matrix with the data vertices +- `faces::Vector{<:AbstractMatrix{<:Integer}}`: A vector of matrices with the faces. Each row is a face +- `faces_view::Vector{Matrix{Int}}`: A subset of `faces` with only the visible faces from a certain perspective +- `bbox::Vector{Float64}`: The vertices BoundingBox +- `color::Vector{Vector{String}}`: A vector with G option colors for each face +- `color_vwall::String`: A string for makecpt cmap option argument (e.g. "darkgreen,lightgreen") +- `zscale::Float64`: A multiplicative factor to scale the z values. Default is 1. +- `bfculling::Bool`: If culling of invisible faces is wished. Default is true +- `isflat::Vector{Bool}`: If this is a flat mesh. Default is false +- `proj4::String`: Projection string in PROJ4 syntax (Optional) +- `wkt::String`: Projection string in WKT syntax (Optional) +- `epsg::Int`: EPSG projection code (Optional) """ Base.@kwdef mutable struct GMTfv{T<:AbstractFloat} <: AbstractArray{T,2} verts::AbstractMatrix{T}=Matrix{Float64}(undef,0,0) faces::Vector{<:AbstractMatrix{<:Integer}}=Vector{Matrix{Int}}(undef,0) faces_view::Vector{Matrix{Int}}=Vector{Matrix{Int}}(undef,0) - color::Vector{Vector{String}}=[String[]] bbox::Vector{Float64}=zeros(6) + color::Vector{Vector{String}}=[String[]] + color_vwall::String="" zscale::Float64=1.0 bfculling::Bool=true isflat::Vector{Bool}=[false] @@ -330,7 +332,7 @@ Base.BroadcastStyle(::Type{<:GMTfv}) = Broadcast.ArrayStyle{GMTfv}() function Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{GMTfv}}, ::Type{ElType}) where ElType FV = find4similar(bc.args) # Scan the inputs for the FV: #GMTfv(similar(Array{ElType}, axes(bc)), FV.faces, FV.faces_view, FV.color, FV.bbox, FV.zscale, FV.bfculling, FV.proj4, FV.wkt, FV.epsg) - GMTfv(FV.verts, FV.faces, FV.faces_view, FV.color, FV.bbox, FV.zscale, FV.bfculling, FV.proj4, FV.wkt, FV.epsg) + GMTfv(FV.verts, FV.faces, FV.faces_view, FV.bbox, FV.color, FV.color_vwall, FV.zscale, FV.bfculling, FV.proj4, FV.wkt, FV.epsg) end find4similar(FV::GMTfv, rest) = FV diff --git a/src/psxy.jl b/src/psxy.jl index b5fd6abc8e..b956b4b11e 100644 --- a/src/psxy.jl +++ b/src/psxy.jl @@ -1561,20 +1561,20 @@ function sort_visible_faces(FV::GMTfv, azim, elev; del::Bool=true)::Tuple{GMTfv, view_vec = [sin_az * cos_el, cos_az * cos_el, sin_el] projs = Float64[] - #!FV.bfculling && (del = false) # Do not delete if bfculling is set to false (for example if FV is not closed) - #isPlane = (FV.isflat || FV.bbox[1] == FV.bbox[2]) || (FV.bbox[3] == FV.bbox[4]) || (FV.bbox[5] == FV.bbox[6]) # Is this FV a plane? - #isPlane && (del = false) # Planes have no invisibles - #needNormals = isempty(FV.color[1]) # If we have no color, we need to compute the normals - - #isPlane && !needNormals && return FV, projs # Nothing to do here in this case. - + if (!isempty(FV.color_vwall)) + P::Ptr{GMT_PALETTE} = palette_init(G_API[1], gmt("makecpt -T0/1 -C" * FV.color_vwall)); # A pointer to a GMT CPT + rgb = [0.0, 0.0, 0.0, 0.0] + end + + FV.faces_view = Vector{Matrix{Int}}(undef,numel(FV.faces)) first_face_vis = true for k = 1:numel(FV.faces) # Loop over number of face groups (we can have triangles, quads, etc) isPlane = FV.isflat[k] - needNormals = isempty(FV.color[1]) # If we have no color, we need to compute the normals - isPlane && !needNormals && continue # Nothing to do here in this case. + have_colorwall = length(FV.color) >= k && !isassigned(FV.color[k], 1) && !isempty(FV.color_vwall) + needNormals = have_colorwall || (length(FV.color) >= k && isempty(FV.color[k])) # No color, no normals + (isPlane && !needNormals) && (FV.faces_view[k] = FV.faces[k]; continue) # Nothing more to do in this case. del = !isPlane && FV.bfculling # bfculling should become a vector too? - have_colors = !isempty(FV.color[1]) # Does this FV have a color for each polygon? + have_colors = length(FV.color) >= k && !isempty(FV.color[k]) # Does this FV has a color for each polygon? n_faces::Int = size(FV.faces[k], 1) # Number of faces (polygons) this_face_nverts::Int = size(FV.faces[k], 2) @@ -1593,24 +1593,33 @@ function sort_visible_faces(FV::GMTfv, azim, elev; del::Bool=true)::Tuple{GMTfv, push!(dists, (cx * sin_az + cy * cos_az, cz * sin_el)) end push!(_projs, this_proj) # But need the normals as stated at the begining of this function + if (have_colorwall) + gmt_get_rgb_from_z(G_API[1], P, this_proj, rgb) + FV.color[k][face] = @sprintf("-G#%.2x%.2x%.2x", round(Int, rgb[1]*255), round(Int, rgb[2]*255), round(Int, rgb[3]*255)) + end end end - - if (isPlane) # Here, FV being a plane we only care about storing the normals - projs = (first_face_vis) ? _projs[ind] : append!(projs, _projs[ind]) - first_face_vis = false - continue - end + #if (isPlane) # Here, FV being a plane we only care about storing the normals + #projs = (first_face_vis) ? _projs[ind] : append!(projs, _projs[ind]) # 'ind' may be first time use => error? + #first_face_vis = false + #continue + #end + + (have_colorwall) && (FV.color[k] = FV.color[k][isVisible]) # SO FUNCIONA A PRIMEIRA VEZ data::Matrix{Integer} = del ? FV.faces[k][isVisible, :] : FV.faces[k] - isempty(data) && continue + isempty(data) && continue # These 'continues' leave out #undefs in FV.faces_view that need to be deleted ind = sortperm(dists) data = data[ind, :] - (first_face_vis) ? (FV.faces_view = [data]) : append!(FV.faces_view, [data]) + FV.faces_view[k] = data projs = (first_face_vis) ? _projs[ind] : append!(projs, _projs[ind]) - have_colors && (FV.color[k] = FV.color[k][ind]) + (have_colors || have_colorwall) && (FV.color[k] = FV.color[k][ind]) first_face_vis = false end + + c = [isassigned(FV.faces_view, k) for k = 1:numel(FV.faces_view)] + !all(c) && (FV.faces_view = FV.faces_view[c]) # Delete eventual #undefs + vis = sum(size.(FV.faces_view, 1)) # If = 0, it must have been a plane. vis > 0 && vis < sum(size.(FV.faces, 1) / 3) && @warn("More than 2/3 of the faces found invisible (actually: $(100 - sum(size.(FV.faces_view, 1)) / sum(size.(FV.faces, 1))*100)%). This often indicates that the Z and X,Y units are not the same. Consider setting `bfculling` to false or use the `nocull=true` option, or using the `zscale` field of the `FV` input.") diff --git a/src/solids.jl b/src/solids.jl index 5383b2a3a1..b21d90c453 100644 --- a/src/solids.jl +++ b/src/solids.jl @@ -761,14 +761,18 @@ function flatfv(I::Union{GMTimage, AbstractString}; shape=:n, level=0.0, thickne end isnoref && return I # A plain image with no coords - x = extrema(view(shape, :, 1)) # xx minmax - y = extrema(view(shape, :, 2)) + if (isa(shape, GDtype)) + x, y = isa(shape, Vector) ? (shape[1].ds_bbox[1:2], shape[1].ds_bbox[3:4]) : (shape.ds_bbox[1:2], shape.ds_bbox[3:4]) + else + x = extrema(view(shape, :, 1)) # xx minmax + y = extrema(view(shape, :, 2)) + end isa(I, AbstractString) && (x[1] < D[1] || x[2] > D[2] || y[1] < D[3] || y[2] > D[4]) && error("The 'shape' is outside the image.") isa(I, GMTimage) && (x[1] < I.range[1] || x[2] > I.range[2] || y[1] < I.range[3] || y[2] > I.range[4]) && error("The 'shape' is outside the image.") - return isa(I, AbstractString) ? gmtread(I, R=(x[1], x[2], y[1], y[2]), V=:q) : crop(I, R=(x[1], x[2], y[1], y[2])) + return isa(I, AbstractString) ? gmtread(I, R=(x[1], x[2], y[1], y[2]), V=:q) : crop(I, R=(x[1], x[2], y[1], y[2]))[1] end function forceRGB(I)::GMTimage{UInt8, 3} @@ -868,9 +872,10 @@ function flatfv(I::Union{GMTimage, AbstractString}; shape=:n, level=0.0, thickne FV.color, FV.isflat = [cor], [true] else cor_wall = Vector{String}(undef, n_wall) - for k = 1:n_wall cor_wall[k] = "-G180" end + #for k = 1:n_wall cor_wall[k] = "-G180" end FV.color = [cor_wall, cor] - FV.isflat = [true, false] + FV.isflat = [false, true] + FV.color_vwall = "140,220" end return FV end diff --git a/src/utils_types.jl b/src/utils_types.jl index 34077ac6a7..47294c0b9b 100644 --- a/src/utils_types.jl +++ b/src/utils_types.jl @@ -700,21 +700,21 @@ Create a FacesVertices object from a matrix of faces indices and another matrix surfaces (cylinders for example). - `V`: A Mx3 matrix of vertices. -### Keyword args +### Kargs - `proj` or `proj4`: A proj4 string for setting the Coordinate Referencing System - `wkt`: A WKT SRS. - `epsg`: Same as `proj` but using an EPSG code """ -function fv2fv(F::Vector{<:AbstractMatrix{<:Integer}}, V; zscale=1.0, bfculling=true, proj="", proj4="", wkt="", epsg=0)::GMTfv +function fv2fv(F::Vector{<:AbstractMatrix{<:Integer}}, V; color_vwall::String="", zscale=1.0, bfculling=true, proj="", proj4="", wkt="", epsg=0)::GMTfv (isempty(proj4) && !isempty(proj)) && (proj4 = proj) # Allow both proj4 or proj keywords bbox = extrema(V, dims=1) isflat = zeros(Bool, length(F)) # Needs thinking GMTfv(verts=V, faces=F, bbox=[bbox[1][1], bbox[1][2], bbox[2][1], bbox[2][2], bbox[3][1], bbox[3][2]], - zscale=zscale, bfculling=bfculling, isflat=isflat, proj4=proj4, wkt=wkt, epsg=epsg) + color_vwall=color_vwall, zscale=zscale, bfculling=bfculling, isflat=isflat, proj4=proj4, wkt=wkt, epsg=epsg) end -fv2fv(F::Matrix{<:Integer}, V; zscale=1.0, bfculling=true, proj="", proj4="", wkt="", epsg=0) = - fv2fv([F], V; zscale=zscale, bfculling=bfculling, proj=proj, proj4=proj4, wkt=wkt, epsg=epsg) +fv2fv(F::Matrix{<:Integer}, V; color_vwall::String="", zscale=1.0, bfculling=true, proj="", proj4="", wkt="", epsg=0) = + fv2fv([F], V; color_vwall=color_vwall, zscale=zscale, bfculling=bfculling, proj=proj, proj4=proj4, wkt=wkt, epsg=epsg) """ When using Meshing.jl we can use the output of the ``isosurface`` function, "verts, faces" as input to this function.