mirror of
https://github.com/ryantimpe/brickr.git
synced 2026-01-06 05:39:34 -06:00
265 lines
8.6 KiB
Plaintext
265 lines
8.6 KiB
Plaintext
---
|
|
title: "Piece type in 3D Models"
|
|
output: rmarkdown::html_vignette
|
|
vignette: >
|
|
%\VignetteIndexEntry{models-piece-type}
|
|
%\VignetteEngine{knitr::rmarkdown}
|
|
%\VignetteEncoding{UTF-8}
|
|
---
|
|
|
|
```{r, include = FALSE}
|
|
knitr::opts_chunk$set(
|
|
collapse = TRUE,
|
|
comment = "#>"
|
|
)
|
|
rgl::setupKnitr()
|
|
```
|
|
|
|
```{r setup, include = FALSE}
|
|
library(brickr)
|
|
```
|
|
|
|
## Getting started
|
|
|
|
Models in brickr default to rendering rectangular LEGO bricks. Other LEGO shapes are possible in both `bricks_from_coords()` and `bricks_from_table()` or `bricks_from_excel()`.
|
|
|
|
Piece shapes in models are driven the `piece_type` ID.
|
|
|
|
## All piece options
|
|
|
|
Currently there are 5 unique piece shapes in brickr. Some shapes can have different orientations, which is also determined by the `piece_type` column.
|
|
|
|
```{r piece_table, results='asis', echo=FALSE, warning=FALSE, message=FALSE}
|
|
|
|
tibble::tribble(
|
|
~Piece, ~piece_type, ~Area, ~Height, ~Orientation,
|
|
"Brick (classic)", "'b'", "any", "1", "--",
|
|
"Plate (flat brick)", "'p'", "any", "1/3", "--",
|
|
"Round 1x1 (cylinder)" , "'c1'", "1", "1", "--",
|
|
"Round 1x1 (cone)", "'c2'", "1", "1", "--",
|
|
"Cheese slope", "'w1'", "1", "2/3", "north",
|
|
"Cheese slope", "'w2'", "1", "2/3", "east",
|
|
"Cheese slope", "'w3'", "1", "2/3", "south",
|
|
"Cheese slope", "'w4'", "1", "2/3", "west"
|
|
) %>%
|
|
knitr::kable()
|
|
|
|
```
|
|
|
|
```{r bricks_2a, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
|
|
expand.grid(
|
|
x = 1:4 * 2,
|
|
y = 1:2 * 2,
|
|
z = 1) %>%
|
|
dplyr::mutate(Color = head(lego_colors$Color, 8),
|
|
piece_type = c("b", "p", "c1", "c2", "w1", "w2", "w3", "w4")) %>%
|
|
bricks_from_coords() %>%
|
|
build_bricks(rgl_lit = FALSE, outline_bricks = TRUE)
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi, 0, 0 ,1))
|
|
```
|
|
|
|
## Bricks from coordinates
|
|
|
|
Changing the piece type in `bricks_from_coords()` is the most straight-forward, so let's begin there.
|
|
|
|
### Changing the default brick
|
|
|
|
Start with a simple programmed model to create a classic 2x4 red brick.
|
|
|
|
```{r bricks_1, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
|
|
expand.grid(
|
|
x = 1:4,
|
|
y = 1:2,
|
|
z = 1,
|
|
Color = "Bright red", stringsAsFactors = FALSE
|
|
) %>%
|
|
bricks_from_coords() %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|
|
|
|
Add a new column, `piece_type`, you can change this default shape. Set `piece_type = "p"` to create a 2x4 plate instead. A LEGO plate is 1/3 the height of a brick.
|
|
|
|
```{r bricks_2, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
|
|
expand.grid(
|
|
x = 1:4,
|
|
y = 1:2,
|
|
z = 1,
|
|
Color = "Bright red", stringsAsFactors = FALSE
|
|
) %>%
|
|
dplyr::mutate(piece_type = "p") %>%
|
|
bricks_from_coords() %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|
|
|
|
## Bricks from tables
|
|
|
|
Including piece options in `bricks_from_table()` or `bricks_from_excel()` is slightly more involved because the input tables are two-dimensional for easier drawing.
|
|
|
|
In these functions, use the input `piece_table` to supply an identically shaped table with optional `piece_type` ids instead of numbers.
|
|
|
|
Start with this data frame called tree_or_mushroom.
|
|
|
|
```{r bricks_5, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
tree_or_mushroom <- tibble::tribble(
|
|
~Level, ~X1, ~X2, ~X3, ~X4, ~X5, ~X6,
|
|
"A", 1, 1, 1, 1, 1, 1,
|
|
"A", 1, 1, 1, 1, 1, 1,
|
|
"A", 1, 1, 1, 1, 1, 1,
|
|
"A", 1, 1, 1, 1, 1, 1,
|
|
"B", 0, 0, 0, 0, 0, 0,
|
|
"B", 0, 0, 2, 2, 0, 0,
|
|
"B", 0, 0, 2, 2, 0, 0,
|
|
"B", 0, 0, 0, 0, 0, 0,
|
|
"C", 0, 0, 0, 0, 0, 0,
|
|
"C", 0, 0, 2, 2, 0, 0,
|
|
"C", 0, 0, 2, 2, 0, 0,
|
|
"C", 0, 0, 0, 0, 0, 0,
|
|
"D", 0, 3, 3, 3, 3, 0,
|
|
"D", 0, 3, 3, 3, 3, 0,
|
|
"D", 0, 3, 3, 3, 3, 0,
|
|
"D", 0, 3, 3, 3, 3, 0,
|
|
"E", 0, 0, 3, 3, 0, 0,
|
|
"E", 0, 3, 3, 3, 3, 0,
|
|
"E", 0, 3, 3, 3, 3, 0,
|
|
"E", 0, 0, 3, 3, 0, 0,
|
|
"F", 0, 0, 0, 0, 0, 0,
|
|
"F", 0, 0, 3, 3, 0, 0,
|
|
"F", 0, 0, 3, 3, 0, 0,
|
|
"F", 0, 0, 0, 0, 0, 0,
|
|
"G", 0, 0, 0, 0, 0, 0,
|
|
"G", 0, 0, 3, 0, 0, 0,
|
|
"G", 0, 0, 0, 3, 0, 0,
|
|
"G", 0, 0, 0, 0, 0, 0
|
|
)
|
|
|
|
brick_colors <- tibble::tribble(
|
|
~`.value`, ~Color,
|
|
1, "Bright green",
|
|
2, "Dark orange",
|
|
3, "Dark green"
|
|
)
|
|
|
|
tree_or_mushroom %>%
|
|
bricks_from_table(brick_colors) %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|
|
|
|
If you want more a tree with more texture, you can change all the "leaves" from rectangular bricks into cylinder bricks.
|
|
|
|
```{r bricks_6, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
tree_or_mushroom_pieces <- tree_or_mushroom %>%
|
|
dplyr::mutate_at(dplyr::vars(dplyr::starts_with("X")),
|
|
~ifelse(.==3, "c2", "b")) #If the color is the dark green, make a cone, else brick
|
|
|
|
tree_or_mushroom %>%
|
|
bricks_from_table(brick_colors,
|
|
piece_matrix = tree_or_mushroom_pieces) %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|
|
|
|
### Bricks from Excel
|
|
|
|
For `bricks_from_excel()`, you only have to supply `piece_type` ids when you do NOT want the classic brick. The best option here is to create your model in the Excel template in [the brickr toybox](https://github.com/ryantimpe/brickr_toybox) GitHub repo. Once complete, duplicate the sheet and change any color values to piece_type IDs.
|
|
|
|
## Mid-level bricks
|
|
|
|
Because different piece types have different heights, your models might have unintentional gaps when using pieces that are less than 1-unit tall.
|
|
|
|
Start with this LEGO cube.
|
|
|
|
```{r bricks_10, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
|
|
expand.grid(
|
|
x = 1:3,
|
|
y = 1:3,
|
|
z = 1:3,
|
|
color = "Bright blue"
|
|
) %>%
|
|
dplyr::mutate(color = ifelse(z==2, "Bright red", as.character(color))) %>%
|
|
bricks_from_coords() %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|
|
|
|
A problem arises when we change these bricks to plates. Each z (Level) continues to be drawn at the same height and the plates are no longer stacked together.
|
|
|
|
```{r bricks_11, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
|
|
expand.grid(
|
|
x = 1:3,
|
|
y = 1:3,
|
|
z = 1:3,
|
|
color = "Bright blue"
|
|
) %>%
|
|
dplyr::mutate(color = ifelse(z==2, "Bright red", as.character(color)),
|
|
piece_type = "p") %>%
|
|
bricks_from_coords() %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|
|
|
|
Each z (Level) is the height of a brick, but plates and cheese slopes have height less than a brick. To stack consecutive bricks that are now all full height, you'll need to use a new column, `mid_level`. The `mid_level` column can be added anywhere in a `bricks_from_coords()` input or as the 2nd column after `Level` in the `matrix_table` input in `bricks_from_table()` or `bricks_from_excel()`.
|
|
|
|
By default, all bricks and pieces are located at `mid_level = 0`, which is the base of the z (Level). All z (Level) values have 3 possible mid_level values: 0, 1, 2.
|
|
|
|
Using integer division and mods, you can convert all levels uniformly to Level + mid_levels. In future versions of brickr, there will be helper functions.
|
|
|
|
```{r bricks_12, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
|
|
expand.grid(
|
|
x = 1:3,
|
|
y = 1:3,
|
|
z = 1:3,
|
|
color = "Bright blue"
|
|
) %>%
|
|
dplyr::mutate(color = ifelse(z==2, "Bright red", as.character(color)),
|
|
piece_type = "p") %>%
|
|
dplyr::mutate(mid_level = (z-1) %% 3,
|
|
z = (z-1) %/% 3 +1) %>%
|
|
bricks_from_coords() %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|
|
|
|
It's also possible to mix and match different z (Level) and mid_level combinations to stack plates with bricks. There are various ways to do this, depending on the complexity and your comfort with tidyverse data manipulation.
|
|
|
|
```{r bricks_13, rgl=TRUE, dev='png', echo=TRUE, warning=FALSE, message=FALSE, fig.width=4, fig.height=4}
|
|
|
|
expand.grid(
|
|
x = 1:3,
|
|
y = 1:3,
|
|
z = 1:3,
|
|
color = "Bright blue"
|
|
) %>%
|
|
dplyr::mutate(color = ifelse(z==2, "Bright red", as.character(color)),
|
|
piece_type = ifelse(z==2, "p", "b")) %>%
|
|
dplyr::mutate(
|
|
mid_level = dplyr::case_when(
|
|
z %in% 1:2 ~ 0,
|
|
z == 3 ~ 1
|
|
),
|
|
z = dplyr::case_when(
|
|
z %in% 2:3 ~ 2,
|
|
TRUE ~ 1
|
|
)) %>%
|
|
bricks_from_coords() %>%
|
|
build_bricks()
|
|
|
|
rgl::par3d(userMatrix = rgl::rotate3d(rgl::par3d("userMatrix"), 1.1*pi/4, 0, 0 ,1))
|
|
```
|