mirror of
https://github.com/ryantimpe/brickr.git
synced 2026-05-22 20:49:04 -05:00
Final set of original 2D and 3D mosaic functions added to pkg.
This commit is contained in:
+15
@@ -15,3 +15,18 @@ WarholBea.png
|
||||
logomovie.mp4
|
||||
logoweb.mp4
|
||||
sets
|
||||
1_CreateLogo.R
|
||||
1_CreateSet.R
|
||||
1_CreateSet_Penguin.R
|
||||
1_CreateSet_Rocket.R
|
||||
1_CreateSet_Trex.R
|
||||
housemovie.mp4
|
||||
houseweb.mp4
|
||||
penguinmovie.mp4
|
||||
penguinweb.mp4
|
||||
rocketmovie.mp4
|
||||
rocketmovie2.mp4
|
||||
rocketweb.mp4
|
||||
rocketweb2.mp4
|
||||
trexmovie.mp4
|
||||
trexweb.mp4
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
# Generated by roxygen2: do not edit by hand
|
||||
|
||||
export("%>%")
|
||||
export(collect_3d)
|
||||
export(collect_bricks)
|
||||
export(convert_to_match_color)
|
||||
export(display_3d)
|
||||
export(display_pieces)
|
||||
export(display_set)
|
||||
export(generate_instructions)
|
||||
export(image_to_bricks)
|
||||
export(legoize)
|
||||
export(scale_image)
|
||||
export(table_pieces)
|
||||
importFrom(magrittr,"%>%")
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#' Convert image output from scale_image() to bricks
|
||||
#'
|
||||
#' @param image_list List output from collect_bricks() or image_to_bricks(). Contains an element \code{Img_lego}.
|
||||
#' @param mosaic_height Maximum height of 3D mosiacs in LEGO plates. 3 plates = 1 brick. This is also the maximum # of distinct elevations.
|
||||
#' @param highest_el Brick height is determined by brightness of color. Use \code{highest_el = 'dark'} for darkest bricks to have \code{mosaic_height}.
|
||||
#' @return A list with elements \code{threed_elevation} and \code{threed_hillshade} to created 3D mosiacs with the \code{rayshader} package.
|
||||
#' @export
|
||||
#'
|
||||
collect_3d <- function(image_list, mosaic_height = 6, highest_el = "light"){
|
||||
|
||||
#Get previous data
|
||||
in_list <- image_list
|
||||
|
||||
if(in_list$mosaic_type != "flat")stop("3D mosaics can only be generated with 'flat' mosaics. Set this input in the 'collect_bricks' function.")
|
||||
|
||||
BrickIDs <- in_list$ID_bricks
|
||||
img_lego <- in_list$Img_lego
|
||||
|
||||
#Number of 'pixels' on a side of a single-stud brick. I think this should be fixed for now
|
||||
ex_size <- 15
|
||||
|
||||
lego_expand <- img_lego %>%
|
||||
dplyr::select(x, y, Lego_name, Lego_color) %>%
|
||||
dplyr::mutate(stud_id = dplyr::row_number())
|
||||
|
||||
lego_expand2 <- expand.grid(x = (min(lego_expand$x)*ex_size):(max(lego_expand$x+1)*ex_size),
|
||||
y = (min(lego_expand$y)*ex_size):(max(lego_expand$y+1)*ex_size)) %>%
|
||||
dplyr::mutate(x_comp = x %/% ex_size,
|
||||
y_comp = y %/% ex_size) %>%
|
||||
dplyr::left_join(lego_expand %>% dplyr::rename(x_comp = x, y_comp = y),
|
||||
by = c("x_comp", "y_comp")) %>%
|
||||
dplyr::left_join(BrickIDs %>% dplyr::select(brick_id, x_comp = x, y_comp = y),
|
||||
by = c("x_comp", "y_comp")) %>%
|
||||
dplyr::select(-x_comp, -y_comp) %>%
|
||||
dplyr::left_join(lego_colors %>% dplyr::select(Lego_name = Color, R_lego, G_lego, B_lego),
|
||||
by = "Lego_name") %>%
|
||||
dplyr::do(
|
||||
if(highest_el == "dark"){
|
||||
dplyr::mutate(., elevation = (1-((R_lego + G_lego + B_lego )/3)) * 1000)
|
||||
} else {
|
||||
dplyr::mutate(., elevation = ( ((R_lego + G_lego + B_lego )/3)) * 1000)
|
||||
}
|
||||
) %>%
|
||||
#Round elevation to nearest 1/height
|
||||
dplyr::mutate(elevation = as.numeric(as.factor(cut(elevation, mosaic_height)))) %>%
|
||||
dplyr::mutate(y = max(y)-y) %>%
|
||||
dplyr::filter(!is.na(elevation)) %>%
|
||||
#Calculate stud placement... radius of 1/3 and height of 0.5 plate
|
||||
dplyr::group_by(stud_id) %>%
|
||||
dplyr::mutate(x_mid = median(x), y_mid = median(y),
|
||||
stud = ((x-x_mid)^2 + (y-y_mid)^2)^(1/2) < ex_size/3) %>%
|
||||
dplyr::ungroup() %>%
|
||||
dplyr::mutate(elevation = ifelse(stud, elevation+0.5, elevation)) %>%
|
||||
dplyr::mutate_at(dplyr::vars(R_lego, G_lego, B_lego), dplyr::funs(ifelse(stud, .-0.1, .))) %>%
|
||||
dplyr::mutate_at(dplyr::vars(R_lego, G_lego, B_lego), dplyr::funs(ifelse(. < 0, 0, .)))
|
||||
|
||||
edges <- dplyr::bind_rows(list(
|
||||
lego_expand2 %>% dplyr::filter(x == min(x)) %>% dplyr::mutate(x = x-1),
|
||||
lego_expand2 %>% dplyr::filter(x == max(x)) %>% dplyr::mutate(x = x+1),
|
||||
lego_expand2 %>% dplyr::filter(y == min(y)) %>% dplyr::mutate(y = y-1),
|
||||
lego_expand2 %>% dplyr::filter(y == max(y)) %>% dplyr::mutate(y = y+1)
|
||||
)) %>%
|
||||
dplyr:: mutate(R_lego = 1, G_lego = 1, B_lego = 1,
|
||||
elevation = 0,
|
||||
brick_id = NA)
|
||||
|
||||
#Elevation Matrix
|
||||
lego_elmat <- lego_expand2 %>%
|
||||
dplyr::bind_rows(edges) %>%
|
||||
dplyr::select(x, y, elevation) %>%
|
||||
tidyr::spread(y, elevation) %>%
|
||||
dplyr::select(-x) %>%
|
||||
as.matrix()
|
||||
|
||||
#Hillshade matrix
|
||||
lego_hillshade_m <- array(dim = c(length(unique(lego_expand2$y)),
|
||||
length(unique(lego_expand2$x)),
|
||||
3))
|
||||
|
||||
lego_expand_color <- lego_expand2 %>%
|
||||
dplyr::group_by(brick_id) %>%
|
||||
#This darkens the edge of each brick, to look like they are separated
|
||||
dplyr::mutate_at(dplyr::vars(R_lego, G_lego, B_lego),
|
||||
dplyr::funs(ifelse((x == min(x) | y == min(y) | x == max(x) | y == max(y)), .*0.75, .))) %>%
|
||||
dplyr::ungroup()
|
||||
|
||||
lego_hillshade_m[,,1] <- lego_expand_color %>%
|
||||
dplyr::select(x, y, R_lego) %>%
|
||||
tidyr::spread(x, R_lego) %>%
|
||||
dplyr::select(-y) %>%
|
||||
as.matrix()
|
||||
|
||||
lego_hillshade_m[,,2] <- lego_expand_color %>%
|
||||
dplyr::select(x, y, G_lego) %>%
|
||||
tidyr::spread(x, G_lego) %>%
|
||||
dplyr::select(-y) %>%
|
||||
as.matrix()
|
||||
|
||||
lego_hillshade_m[,,3] <- lego_expand_color %>%
|
||||
dplyr::select(x, y, B_lego) %>%
|
||||
tidyr::spread(x, B_lego) %>%
|
||||
dplyr::select(-y) %>%
|
||||
as.matrix()
|
||||
|
||||
#Return
|
||||
in_list[["threed_elevation"]] <- lego_elmat
|
||||
in_list[["threed_hillshade"]] <- lego_hillshade_m
|
||||
|
||||
return(in_list)
|
||||
|
||||
}
|
||||
|
||||
#' brickr wrapper for rayshader::plot_3d() to display 3D mosaics. Requires rayshader.
|
||||
#'
|
||||
#' @param image_list List output from collect_3d(). Contains element \code{threed_elevation} and \code{threed_hillshade}.
|
||||
#' @param solidcolor Hex color of mosaic base. Only renders on bottom.
|
||||
#' @param ... All other inputs from rayshader::plot_3d() EXCEPT \code{hillshade}, \code{soliddepth}, and \code{zscale}.
|
||||
#' @return 3D mosaic rendered in the 'rgl' package.
|
||||
#' @export
|
||||
|
||||
display_3d <- function(image_list, solidcolor = "#a3a2a4", ...){
|
||||
#Requires Rayshader
|
||||
if (!requireNamespace("rayshader", quietly = TRUE)) {
|
||||
stop("Package \"rayshader\" needed for this function to work. Please install it.",
|
||||
call. = FALSE)
|
||||
}
|
||||
|
||||
image_list$`threed_hillshade`%>%
|
||||
rayshader::plot_3d(image_list$`threed_elevation`, zscale=0.125,
|
||||
solidcolor=solidcolor, ...)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
#' Generate required bricks as a data frame.
|
||||
#'
|
||||
#' @param image_list List output from collect_bricks() or image_to_bricks(). Contains an element \code{Img_lego}.
|
||||
#' @return Data frame of piece counts by LEGO color name and size.
|
||||
#' @export
|
||||
#'
|
||||
|
||||
table_pieces <- function(image_list){
|
||||
pcs <- image_list$pieces
|
||||
|
||||
pcs %>%
|
||||
dplyr::select(-Lego_color) %>%
|
||||
tidyr::spread(Brick_size, n, fill = 0) %>%
|
||||
dplyr::rename(`LEGO Brick Color` = Lego_name)
|
||||
}
|
||||
|
||||
#' Graphically display required bricks.
|
||||
#'
|
||||
#' @param image_list List output from collect_bricks() or image_to_bricks(). Contains an element \code{Img_lego}.
|
||||
#' @return Plot object of required bricks by color and size.
|
||||
#' @export
|
||||
#'
|
||||
|
||||
display_pieces <- function(image_list){
|
||||
in_list <- image_list
|
||||
pcs <- in_list$pieces
|
||||
|
||||
if(in_list$mosaic_type == "flat"){
|
||||
pcs_coords <- dplyr::tibble(
|
||||
Brick_size = c("1 x 1", "2 x 1", "3 x 1", "4 x 1", "2 x 2", "4 x 2"),
|
||||
xmin = c(0, 0, 0, 0, 6, 6),
|
||||
xmax = c(1, 2, 3, 4, 8, 8),
|
||||
ymin = c(0, 2, 4, 6, 0, 3),
|
||||
ymax = c(1, 3, 5, 7, 2, 7)
|
||||
)
|
||||
} else {
|
||||
pcs_coords <- dplyr::tibble(
|
||||
Brick_size = c("1 x 2", "2 x 2", "3 x 2", "4 x 2"),
|
||||
xmin = c(0, 5, 5, 0),
|
||||
xmax = c(2, 7, 7, 2),
|
||||
ymin = c(0, 0, 3, 2),
|
||||
ymax = c(1, 2, 6, 6)
|
||||
)
|
||||
}
|
||||
#This function creates nodes in each brick for stud placement
|
||||
pcs_coords <- pcs_coords %>%
|
||||
dplyr::mutate(studs = purrr::pmap(list(xmin, xmax, ymin, ymax), function(a, b, c, d){
|
||||
expand.grid(x=seq(a+0.5, b-0.5, by=1),
|
||||
y=seq(c+0.5, d-0.5, by=1))
|
||||
}))
|
||||
|
||||
pcs2 <- pcs %>%
|
||||
dplyr::arrange(Lego_color) %>%
|
||||
dplyr::mutate(Lego_name = factor(Lego_name,
|
||||
levels = c("Black",
|
||||
unique(Lego_name)[!(unique(Lego_name) %in% c("Black", "White"))],
|
||||
"White"))) %>%
|
||||
dplyr::left_join(pcs_coords, by = "Brick_size")
|
||||
|
||||
if(in_list$mosaic_type == "flat"){
|
||||
coord_xlim <- c(-0.5, 10)
|
||||
facet_cols <- 5
|
||||
} else {
|
||||
coord_xlim <- c(-0.5, 9)
|
||||
facet_cols <- 6
|
||||
}
|
||||
|
||||
pcs2 %>%
|
||||
ggplot2::ggplot() +
|
||||
ggplot2::geom_rect(ggplot2::aes(xmin=xmin, xmax=xmax, ymin=-ymin, ymax=-ymax,
|
||||
fill = Lego_color), color = "#333333")+
|
||||
ggplot2::scale_fill_identity() +
|
||||
ggplot2::geom_point(data = pcs2 %>% tidyr::unnest(studs),
|
||||
ggplot2::aes(x=x, y=-y),
|
||||
color = "#cccccc", alpha = 0.25,
|
||||
shape = 1, size = 2) +
|
||||
ggplot2::geom_text(
|
||||
ggplot2::aes(x = xmax + 0.25, y = -(ymin+ymax)/2, label = paste0("x", n)),
|
||||
hjust = 0, vjust = 0.5, size = 3.5) +
|
||||
ggplot2::coord_fixed(xlim = coord_xlim) +
|
||||
ggplot2::labs(title = (if(in_list$mosaic_type == "stacked"){
|
||||
"Suggested LEGO Bricks"
|
||||
}else{"Suggested LEGO Plates"}),
|
||||
caption = (if(in_list$mosaic_type == "stacked"){
|
||||
"Mosaic is 2-bricks deep. Can substitute 2-stud bricks for 1-stud alternatives for a thinner mosaic."}else{""})
|
||||
) +
|
||||
ggplot2::facet_wrap(~Lego_name, ncol=facet_cols) +
|
||||
ggplot2::theme_minimal() +
|
||||
ggplot2::theme( panel.background = ggplot2::element_rect(fill = "#7EC0EE"),
|
||||
strip.background = ggplot2::element_rect(fill = "#F7F18D"),
|
||||
strip.text = ggplot2::element_text(color = "#333333", face = "bold"),
|
||||
axis.line = ggplot2::element_blank(),
|
||||
axis.title.x = ggplot2::element_blank(),
|
||||
axis.text.x = ggplot2::element_blank(),
|
||||
axis.title.y = ggplot2::element_blank(),
|
||||
axis.text.y = ggplot2::element_blank(),
|
||||
panel.grid = ggplot2::element_blank())
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/image_to_3d.R
|
||||
\name{collect_3d}
|
||||
\alias{collect_3d}
|
||||
\title{Convert image output from scale_image() to bricks}
|
||||
\usage{
|
||||
collect_3d(image_list, mosaic_height = 6, highest_el = "light")
|
||||
}
|
||||
\arguments{
|
||||
\item{image_list}{List output from collect_bricks() or image_to_bricks(). Contains an element \code{Img_lego}.}
|
||||
|
||||
\item{mosaic_height}{Maximum height of 3D mosiacs in LEGO plates. 3 plates = 1 brick. This is also the maximum # of distinct elevations.}
|
||||
|
||||
\item{highest_el}{Brick height is determined by brightness of color. Use \code{highest_el = 'dark'} for darkest bricks to have \code{mosaic_height}.}
|
||||
}
|
||||
\value{
|
||||
A list with elements \code{threed_elevation} and \code{threed_hillshade} to created 3D mosiacs with the \code{rayshader} package.
|
||||
}
|
||||
\description{
|
||||
Convert image output from scale_image() to bricks
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/image_to_3d.R
|
||||
\name{display_3d}
|
||||
\alias{display_3d}
|
||||
\title{brickr wrapper for rayshader::plot_3d() to display 3D mosaics. Requires rayshader.}
|
||||
\usage{
|
||||
display_3d(image_list, solidcolor = "#a3a2a4", ...)
|
||||
}
|
||||
\arguments{
|
||||
\item{image_list}{List output from collect_3d(). Contains element \code{threed_elevation} and \code{threed_hillshade}.}
|
||||
|
||||
\item{solidcolor}{Hex color of mosaic base. Only renders on bottom.}
|
||||
|
||||
\item{...}{All other inputs from rayshader::plot_3d() EXCEPT \code{hillshade}, \code{soliddepth}, and \code{zscale}.}
|
||||
}
|
||||
\value{
|
||||
3D mosaic rendered in the 'rgl' package.
|
||||
}
|
||||
\description{
|
||||
brickr wrapper for rayshader::plot_3d() to display 3D mosaics. Requires rayshader.
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/piece_count.R
|
||||
\name{display_pieces}
|
||||
\alias{display_pieces}
|
||||
\title{Graphically display required bricks.}
|
||||
\usage{
|
||||
display_pieces(image_list)
|
||||
}
|
||||
\arguments{
|
||||
\item{image_list}{List output from collect_bricks() or image_to_bricks(). Contains an element \code{Img_lego}.}
|
||||
}
|
||||
\value{
|
||||
Plot object of required bricks by color and size.
|
||||
}
|
||||
\description{
|
||||
Graphically display required bricks.
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/piece_count.R
|
||||
\name{table_pieces}
|
||||
\alias{table_pieces}
|
||||
\title{Generate required bricks as a data frame.}
|
||||
\usage{
|
||||
table_pieces(image_list)
|
||||
}
|
||||
\arguments{
|
||||
\item{image_list}{List output from collect_bricks() or image_to_bricks(). Contains an element \code{Img_lego}.}
|
||||
}
|
||||
\value{
|
||||
Data frame of piece counts by LEGO color name and size.
|
||||
}
|
||||
\description{
|
||||
Generate required bricks as a data frame.
|
||||
}
|
||||
Reference in New Issue
Block a user