From f9b2204bb95fc9034b821d67f624dc075542ef58 Mon Sep 17 00:00:00 2001 From: tylermorganwall Date: Thu, 28 Mar 2024 00:08:53 -0400 Subject: [PATCH] rayshader v0.38.0: Add variable point and line width support to `render_points()` and `render_lines()` --- DESCRIPTION | 17 ++++----- R/convert_rgl_to_raymesh.R | 4 +- R/generate_scalebar_overlay.R | 2 +- R/render_highquality.R | 12 ++++-- R/render_path.R | 69 ++++++++++++++++++++++++++++------- R/render_points.R | 18 +++++++-- R/render_snapshot.R | 7 ++-- R/render_snapshot_software.R | 10 +++-- man/render_highquality.Rd | 3 +- man/render_points.Rd | 5 ++- man/render_snapshot.Rd | 5 ++- 11 files changed, 107 insertions(+), 45 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index b9a44a1e..fcd433a0 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,9 +1,11 @@ Package: rayshader Type: Package Title: Create Maps and Visualize Data in 2D and 3D -Version: 0.37.3 -Date: 2024-02-19 -Author: Tyler Morgan-Wall +Version: 0.38.0 +Date: 2024-03-28 +Authors@R: + person("Tyler", "Morgan-Wall", email = "tylermw@gmail.com",role = c("aut", "cph", "cre"), + comment = c(ORCID = "0000-0002-3131-3814")) Maintainer: Tyler Morgan-Wall Description: Uses a combination of raytracing and multiple hill shading methods to produce 2D and 3D data visualizations and maps. Includes water detection and layering functions, programmable color palette generation, several built-in textures for hill shading, 2D and 3D plotting options, a built-in path tracer, 'Wavefront' OBJ file export, and the ability to save 3D visualizations to a 3D printable format. License: GPL-3 @@ -47,14 +49,9 @@ Suggests: osmdata, raybevel LinkingTo: Rcpp, progress, RcppArmadillo -RoxygenNote: 7.3.0 +RoxygenNote: 7.3.1 URL: https://www.rayshader.com, https://github.com/tylermorganwall/rayshader BugReports: https://github.com/tylermorganwall/rayshader/issues -Remotes: tylermorganwall/rayimage, - tylermorganwall/rayrender, - tylermorganwall/rayvertex, - tylermorganwall/raybevel, - dmurdoch/rgl Config/testthat/edition: 3 -Additional_Repositories: https://github.com/tylermorganwall/raybevel +Additional_repositories: https://tylermorganwall.r-universe.dev/ diff --git a/R/convert_rgl_to_raymesh.R b/R/convert_rgl_to_raymesh.R index df187cc8..3fc6f8f4 100644 --- a/R/convert_rgl_to_raymesh.R +++ b/R/convert_rgl_to_raymesh.R @@ -97,7 +97,7 @@ convert_rgl_to_raymesh = function(save_shadow = TRUE) { dissolve = alpha, specular = specular))) } - for(row in 1:nrow(vertex_info)) { + for(row in seq_len(nrow(vertex_info))) { if(!is.na(vertex_info$lit[row])) { lit_val = unlist(vertex_info$lit[row]) } else { @@ -130,7 +130,7 @@ convert_rgl_to_raymesh = function(save_shadow = TRUE) { indices = indices - 1 indices = matrix(indices, ncol=3, byrow=TRUE) - indices = indices[1:(nrow(indices)-na_counter),] + indices = indices[seq_len(nrow(indices)-na_counter),] texture_loc = vertex_info$texture_file[[row]] texture_loc = ifelse(!is.na(texture_loc), texture_loc, "") final_scene[[num_elems]] = rayvertex::construct_mesh(indices = indices, diff --git a/R/generate_scalebar_overlay.R b/R/generate_scalebar_overlay.R index 4d39771b..f3508221 100644 --- a/R/generate_scalebar_overlay.R +++ b/R/generate_scalebar_overlay.R @@ -148,7 +148,7 @@ #' latlong=TRUE)) %>% #' plot_map() #'} -##'if(run_documentation()) { +#'if(run_documentation()) { #'#64373.8 meters in 40 miles #'#Create custom labels, change font and text size, remove the border/ticks, and change the color #'#Here, we specify a width and height to double the resolution of the image (for sharper text) diff --git a/R/render_highquality.R b/R/render_highquality.R index c2b0f643..b22d1908 100644 --- a/R/render_highquality.R +++ b/R/render_highquality.R @@ -44,7 +44,8 @@ #'than straight segments. #'@param use_extruded_paths Default `TRUE`. If `FALSE`, paths will be generated with the `rayrender::path()` object, instead #'of `rayrender::extruded_path()`. -#'@param point_radius Default `0.5`. Radius of 3D points (rendered with `render_points()`. +#'@param point_radius Default `1`. Radius of 3D points (rendered with `render_points()`). This scales the existing +#'value of size specified in `render_points()`. #'@param scale_text_angle Default `NULL`. Same as `text_angle`, but for the scale bar. #'@param scale_text_size Default `6`. Height of the scale bar text. #'@param scale_text_offset Default `c(0,0,0)`. Offset to be applied to all scale bar text labels. @@ -451,6 +452,8 @@ render_highquality = function(filename = NA, samples = 128, for(i in seq_len(length(pathids))) { temp_verts = rgl.attrib(pathids[i], "vertices") temp_color = rgl.attrib(pathids[i], "colors") + temp_lwd = material3d("lwd", id = pathids[i]) * line_radius + if(nrow(temp_color) == 1) { temp_color = matrix(temp_color[1:3], byrow = TRUE, ncol = 3, nrow = nrow(temp_verts)) } @@ -458,13 +461,13 @@ render_highquality = function(filename = NA, samples = 128, path_material_args$color = temp_color[1,1:3] if(use_extruded_paths) { pathline[[counter]] = rayrender::extruded_path(points = temp_verts - matrix_center , - width = line_radius * 2, + width = temp_lwd * 2, smooth_normals = TRUE, straight = !smooth_line, material = do.call("path_material", args = path_material_args)) } else { pathline[[counter]] = rayrender::path(points = temp_verts - matrix_center, - width = line_radius * 2, + width = temp_lwd * 2, straight = !smooth_line, material = do.call("path_material", args = path_material_args)) } @@ -476,6 +479,7 @@ render_highquality = function(filename = NA, samples = 128, for(i in seq_len(length(pointids))) { temp_verts = rgl.attrib(pointids[i], "vertices") temp_color = rgl.attrib(pointids[i], "colors") + temp_size = material3d("size", id = pointids[i]) * point_radius if(nrow(temp_color) == 1) { temp_color = matrix(temp_color[1:3], byrow = TRUE, ncol = 3, nrow = nrow(temp_verts)) } @@ -485,7 +489,7 @@ render_highquality = function(filename = NA, samples = 128, pointlist[[counter]] = rayrender::sphere(x = temp_verts[j,1] - bbox_center[1], y = temp_verts[j,2] - bbox_center[2], z = temp_verts[j,3] - bbox_center[3], - radius = point_radius, + radius = temp_size, material = do.call("point_material", args = point_material_args)) counter = counter + 1 } diff --git a/R/render_path.R b/R/render_path.R index fa53455a..8980708c 100644 --- a/R/render_path.R +++ b/R/render_path.R @@ -166,20 +166,36 @@ render_path = function(lat, long = NULL, altitude = NULL, groups = NULL, } if(resample_evenly) { stopifnot(resample_n > 1) - xyz = render_path(extent = extent, lat = lat, long = long, altitude = altitude, - zscale=zscale, heightmap = heightmap, offset = offset, resample_evenly = FALSE, + xyz = render_path(extent = extent, lat = lat, long = long, + altitude = altitude, + zscale=zscale, heightmap = heightmap, + offset = offset, resample_evenly = FALSE, reorder = reorder, reorder_first_index = reorder_first_index, reorder_duplicate_tolerance = reorder_duplicate_tolerance, reorder_merge_tolerance = reorder_merge_tolerance, simplify_tolerance = simplify_tolerance, clear_previous = FALSE, return_coords = TRUE) xyz = lapply(xyz, get_interpolated_points_path, n = resample_n) - xyz = do.call("rbind",lapply(xyz, - \(x) rbind(x,matrix(NA,ncol=3,nrow=1)))) + xyz = do.call("rbind",lapply(xyz, \(x) rbind(x,matrix(NA,ncol=3,nrow=1)))) if(!return_coords) { - rgl::lines3d(xyz, - color = color, tag = tag, lwd = linewidth, line_antialias = antialias) - return(invisible()) + if(length(linewidth) > 1) { + if(length(linewidth) == nrow(xyz)) { + linewidth = (linewidth[seq_len(length(linewidth))[-1]] + + linewidth[seq_len(length(linewidth)-1)])/2 + } + color_length = length(color) + for(i in seq_len(nrow(xyz)-1)) { + rgl::lines3d(xyz[i:(i+1),], + color = color[((i-1) %% color_length) + 1], + tag = tag, lwd = linewidth[i], + line_antialias = antialias) + } + return(invisible()) + } else { + rgl::lines3d(xyz, + color = color, tag = tag, lwd = linewidth, line_antialias = antialias) + return(invisible()) + } } else { return(xyz) } @@ -272,12 +288,39 @@ render_path = function(lat, long = NULL, altitude = NULL, groups = NULL, altitude, offset, zscale, filter_bounds = FALSE) } if(!return_coords) { - xyz = do.call("rbind",lapply(coord_list, - \(x) rbind(x,matrix(NA,ncol=3,nrow=1)))) - xyz = xyz[-nrow(xyz),] - rgl::lines3d(xyz, - color = color, tag = tag, - lwd = linewidth, line_antialias = antialias) + if(length(linewidth) > 1) { + if(length(coord_list) == 1) { + xyz = do.call("rbind",coord_list) + if(length(linewidth) == nrow(xyz)) { + linewidth = (linewidth[seq_len(length(linewidth))[-1]] + + linewidth[seq_len(length(linewidth)-1)])/2 + } + stopifnot(length(linewidth) == (nrow(xyz)-1)) + color_length = length(color) + for(i in seq_len(nrow(xyz)-1)) { + rgl::lines3d(xyz[i:(i+1),], + color = color[((i-1) %% color_length) + 1], + tag = tag, lwd = linewidth[i], + line_antialias = antialias) + } + } else { + stopifnot(length(coord_list) == length(linewidth)) + color_length = length(color) + for(i in seq_len(length(coord_list))) { + rgl::lines3d(coord_list[[i]], + color = color[((i-1) %% color_length) + 1], + tag = tag, lwd = linewidth[i], + line_antialias = antialias) + } + } + } else { + xyz = do.call("rbind",lapply(coord_list, + \(x) rbind(x,matrix(NA,ncol=3,nrow=1)))) + xyz = xyz[-nrow(xyz),] + rgl::lines3d(xyz, + color = color, tag = tag, + lwd = linewidth, line_antialias = antialias) + } } else { return(coord_list) } diff --git a/R/render_points.R b/R/render_points.R index 73e96af3..2f5dbd08 100644 --- a/R/render_points.R +++ b/R/render_points.R @@ -4,8 +4,8 @@ #'system defined by the extent object. If no altitude is provided, the points will be elevated a constant offset #'above the heightmap. If the points goes off the edge, the nearest height on the heightmap will be used. #' -#'@param long Vector of longitudes (or other coordinate in the same coordinate reference system as extent). #'@param lat Vector of latitudes (or other coordinate in the same coordinate reference system as extent). +#'@param long Vector of longitudes (or other coordinate in the same coordinate reference system as extent). #'@param altitude Default `NULL`. Elevation of each point, in units of the elevation matrix (scaled by zscale). If a single value, #'all data will be rendered at that altitude. #'@param extent Either an object representing the spatial extent of the 3D scene @@ -16,8 +16,9 @@ #'@param heightmap Default `NULL`. Automatically extracted from the rgl window--only use if auto-extraction #'of matrix extent isn't working. A two-dimensional matrix, where each entry in the matrix is the elevation at that point. #' All points are assumed to be evenly spaced. -#'@param size Default `3`. The point size. -#'@param color Default `black`. Color of the point. +#'@param size Default `3`. The point size. This can be a vector (the same length as `lat` and `long`) specifying +#'a size for each point. +#'@param color Default `black`. Color of the point. This can also be a vector specifying the color of each point. #'@param offset Default `5`. Offset of the track from the surface, if `altitude = NULL`. #'@param clear_previous Default `FALSE`. If `TRUE`, it will clear all existing points. #'@export @@ -110,5 +111,14 @@ render_points = function(lat = NULL, long = NULL, altitude = NULL, extent = NULL } xyz = transform_into_heightmap_coords(extent, heightmap, lat, long, altitude, offset, zscale) - rgl::points3d(xyz[,1], xyz[,2], xyz[,3], color = color, tag = "points3d", size = size) + if(length(size) > 1) { + stopifnot(length(size) == nrow(xyz)) + color_length = length(color) + for(i in seq_len(nrow(xyz))) { + rgl::points3d(xyz[i,1], xyz[i,2], xyz[i,3], color = color[((i-1) %% color_length) + 1], + tag = "points3d", size = size[i]) + } + } else { + rgl::points3d(xyz[,1], xyz[,2], xyz[,3], color = color, tag = "points3d", size = size) + } } diff --git a/R/render_snapshot.R b/R/render_snapshot.R index 2e465190..16f18236 100644 --- a/R/render_snapshot.R +++ b/R/render_snapshot.R @@ -41,7 +41,8 @@ #'will specify the absolute angle all the labels are facing. If three angles, this will specify all three orientations #'(relative to the x,y, and z axes) of the text labels. #'@param text_size Default `30`. Height of the text. -#'@param point_radius Default `2`. Radius of 3D points (rendered with `render_points()`. +#'@param point_radius Default `1`. Radius of 3D points (rendered with `render_points()`). This scales the existing +#'value of size specified in `render_points()`. #'@param line_offset Default `1e-7`. Small number indicating the offset in the scene to apply to lines if using software rendering. Increase this if your lines #'aren't showing up, or decrease it if lines are appearing through solid objects. #'@param thick_lines Default `TRUE`. If `software_render = TRUE`, this will render path segments as thick cylinders. Otherwise, it will @@ -117,8 +118,8 @@ render_snapshot = function(filename, clear=FALSE, software_render = FALSE, camera_location = NULL, camera_lookat = c(0,0,0), background = NULL, text_angle = NULL, text_size = 30, text_offset = c(0,0,0), - point_radius = 2, - line_offset = 1e-7, thick_lines = TRUE, line_radius = 0.5, + point_radius = 1, + line_offset = 1e-7, thick_lines = TRUE, line_radius = 1, cache_scene = FALSE, reset_scene_cache = FALSE, new_page = TRUE, print_scene_info = FALSE, fsaa = 1, rayvertex_lighting = FALSE, rayvertex_lights = NULL, diff --git a/R/render_snapshot_software.R b/R/render_snapshot_software.R index 8773908a..624cb510 100644 --- a/R/render_snapshot_software.R +++ b/R/render_snapshot_software.R @@ -209,6 +209,8 @@ render_snapshot_software = function(filename, cache_scene = FALSE, camera_locati for(i in seq_len(length(pathids))) { temp_verts = rgl.attrib(pathids[i], "vertices") temp_color = rgl.attrib(pathids[i], "colors") + temp_lwd = material3d("lwd", id = pathids[i]) * line_radius + if(nrow(temp_color) == 1) { temp_color = matrix(temp_color[1:3], byrow = TRUE, ncol = 3, nrow = nrow(temp_verts)) } @@ -220,7 +222,7 @@ render_snapshot_software = function(filename, cache_scene = FALSE, camera_locati if(!all(temp_verts[j+1,] == temp_verts[j,])) { line_scene[[line_counter]] = rayvertex::segment_mesh(start = temp_verts[j,] - bbox_center, end = temp_verts[j+1,] - bbox_center, - radius = line_radius, + radius = temp_lwd, material = line_mat) line_counter = line_counter + 1 } @@ -239,14 +241,16 @@ render_snapshot_software = function(filename, cache_scene = FALSE, camera_locati for(i in seq_len(length(pointids))) { temp_verts = rgl.attrib(pointids[i], "vertices") temp_color = rgl.attrib(pointids[i], "colors") + temp_size = material3d("size", id = pointids[i]) * point_radius if(nrow(temp_color) == 1) { temp_color = matrix(temp_color[1:3], byrow = TRUE, ncol = 3, nrow = nrow(temp_verts)) } for(j in seq_len(nrow(temp_verts))) { - pointlist = rayvertex::add_shape(pointlist, rayvertex::sphere_mesh(position = c(temp_verts[j,1] - bbox_center[1], + pointlist = rayvertex::add_shape(pointlist, + rayvertex::sphere_mesh(position = c(temp_verts[j,1] - bbox_center[1], temp_verts[j,2] - bbox_center[2], temp_verts[j,3] - bbox_center[3]), - radius = point_radius, + radius = temp_size, material = rayvertex::material_list(diffuse = temp_color[j,1:3]))) } } diff --git a/man/render_highquality.Rd b/man/render_highquality.Rd index fd9bfbd3..73a1fcc4 100644 --- a/man/render_highquality.Rd +++ b/man/render_highquality.Rd @@ -112,7 +112,8 @@ will specify the absolute angle all the labels are facing. If three angles, this \item{line_radius}{Default `0.5`. Radius of line/path segments.} -\item{point_radius}{Default `0.5`. Radius of 3D points (rendered with `render_points()`.} +\item{point_radius}{Default `1`. Radius of 3D points (rendered with `render_points()`). This scales the existing +value of size specified in `render_points()`.} \item{smooth_line}{Default `FALSE`. If `TRUE`, the line will be rendered with a continuous smooth line, rather than straight segments.} diff --git a/man/render_points.Rd b/man/render_points.Rd index f75a0857..6a645c0f 100644 --- a/man/render_points.Rd +++ b/man/render_points.Rd @@ -36,9 +36,10 @@ the previously aforementioned packages) which will be automatically converted to of matrix extent isn't working. A two-dimensional matrix, where each entry in the matrix is the elevation at that point. All points are assumed to be evenly spaced.} -\item{size}{Default `3`. The point size.} +\item{size}{Default `3`. The point size. This can be a vector (the same length as `lat` and `long`) specifying +a size for each point.} -\item{color}{Default `black`. Color of the point.} +\item{color}{Default `black`. Color of the point. This can also be a vector specifying the color of each point.} \item{offset}{Default `5`. Offset of the track from the surface, if `altitude = NULL`.} diff --git a/man/render_snapshot.Rd b/man/render_snapshot.Rd index 6bcd4a6e..893bb12f 100644 --- a/man/render_snapshot.Rd +++ b/man/render_snapshot.Rd @@ -31,7 +31,7 @@ render_snapshot( text_angle = NULL, text_size = 30, text_offset = c(0, 0, 0), - point_radius = 2, + point_radius = 1, line_offset = 1e-07, thick_lines = TRUE, line_radius = 0.5, @@ -114,7 +114,8 @@ will specify the absolute angle all the labels are facing. If three angles, this \item{text_offset}{Default `c(0,0,0)`. Offset to be applied to all text labels.} -\item{point_radius}{Default `2`. Radius of 3D points (rendered with `render_points()`.} +\item{point_radius}{Default `1`. Radius of 3D points (rendered with `render_points()`). This scales the existing +value of size specified in `render_points()`.} \item{line_offset}{Default `1e-7`. Small number indicating the offset in the scene to apply to lines if using software rendering. Increase this if your lines aren't showing up, or decrease it if lines are appearing through solid objects.}