#' Get front triangle dimensions
#'
#' The front triangle is a misnomer, because it's a quadrilateral made up
#' by four tubes: the seat tube ST, top tube TT, head tube HT and down tube DT.
#' Between these tubes there are four angles, and they must add up to 360 degrees.
#' A bike frame designer aims for a top tube length (`tt_length`) and
#' seat tube length (`st_length`) that fit a rider of a given height and
#' torso length, and also for a seat tube angle (`st_angle`) head
#' tube angle (`ht_angle`) and top tube angle (`tt_angle`) that give
#' the bike its desired riding characteristics.
#'
#' The angle between the seat tube and the downtube (`st_dt_angle`) is
#' pre-determined in lugged frame construction by how the bottom bracket
#' shell was cast. With these six dimensions in hand we can get the full
#' set of dimensions that describe the front triangle. This function
#' returns them as a tibble of five columns and three rows. Each tube is
#' described as a right triangle made up of the tube length as the hypotenuse
#' and the tube's horizontal and vertical projections as the legs. The elements
#' on each row are named accordingly. Why five columns for four tubes?
#'
#' The fifth column, ett_triangle, helps calculate the effective top tube length
#' when the top tube is slanted. For slanted top tubes the effective length is
#' the horizontal distance between the front of the top tube and an imaginary
#' extension of the seat tube equal to the vertical projection of the top tube
#' times the sine of the seat tube angle in radians. In other words, this
#' horizontal distance is equal to the horizontal projection of the top tube
#' plus the vertical projection of the top tube times the tangent of the seat
#' tube angle in radians. The second term of this addition is the third
#' element of the ett_triangle vector.
#'
#' @inheritParams get_bb_tt_diagonal
#' @param ht_angle Head tube angle with the horizontal, in degrees.
#' @param st_dt_angle Angle between the seat tube and the down tube, in degrees.
#'
#' @return A 3 x 5 tibble.
#' @export
#'
#' @examples
#' get_front_triangle_dims()
get_front_triangle_dims <- function(st_length = 500,
tt_length = 500,
st_angle = 71,
tt_angle = 0,
ht_angle = 71,
st_dt_angle = 60) {
stopifnot(st_length > 0 && tt_length > 0)
stopifnot(st_angle > 0 && st_angle < 90)
stopifnot(tt_angle >= 0 && tt_angle < 90)
stopifnot(ht_angle > 0 && ht_angle < 90)
stopifnot(st_dt_angle > 0 && st_dt_angle < 90)
tt_ht_angle <- 180 - (ht_angle + tt_angle)
dt_angle <- (90 - st_angle) + (90 - st_dt_angle) # 1
ht_dt_angle <- dt_angle + ht_angle # 2
# neither 1 nor 2 may feel intuitively true to you.
# if so, sketch the frame out on paper. they can be
# both proved with some basic Euclidean geometry.
# it's easier if you can look at the drawing.
bb_tt_diagonal <- get_bb_tt_diagonal(st_length, tt_length, st_angle, tt_angle)
tt_diagonal_angle <- acos(-(st_length^2 - (tt_length^2 + bb_tt_diagonal^2))/(2*tt_length*bb_tt_diagonal))*180/pi
st_diagonal_angle <- acos(-(tt_length^2 - (st_length^2 + bb_tt_diagonal^2))/(2*st_length*bb_tt_diagonal))*180/pi
diagonal_dt_angle <- st_dt_angle - st_diagonal_angle
diagonal_ht_angle <- tt_ht_angle - tt_diagonal_angle
dt_length <- sin(diagonal_ht_angle * pi / 180) * bb_tt_diagonal / sin(ht_dt_angle * pi / 180)
dt_triangle <- c(length = dt_length,
vertical_projection = sin(dt_angle * pi / 180) * dt_length,
horizontal_projection = cos(dt_angle * pi / 180) * dt_length)
ht_length <- sin(diagonal_dt_angle * pi / 180) * bb_tt_diagonal / sin(ht_dt_angle * pi / 180)
ht_triangle <- c(length = ht_length,
vertical_projection = sin(ht_angle * pi / 180) * ht_length,
horizontal_projection = cos(ht_angle * pi / 180) * ht_length)
st_triangle <- c(length = st_length,
vertical_projection = sin(st_angle * pi / 180) * st_length,
horizontal_projection = cos(st_angle * pi / 180) * st_length)
tt_triangle <- c(length = tt_length,
vertical_projection = sin(tt_angle * pi / 180) * tt_length,
horizontal_projection = cos(tt_angle * pi / 180) * tt_length)
# if the top tube has a slope, then its length is different
# from the effective top tube (ett) length. add ett_triangle:
tt_vp <- tt_triangle[['vertical_projection']]
ett_triangle <- c(length = tt_vp / sin(st_angle * pi / 180),
vertical_projection = tt_vp,
horizontal_projection = tt_vp / tan(st_angle * pi / 180))
tibble::tibble(st_triangle,
dt_triangle,
ht_triangle,
tt_triangle,
ett_triangle)
}
#' Add extension to top of the head or seat tube
#'
#' The frame reach is the difference between the x coordinate
#' of the top of the head tube and the x coordinate of the
#' bottom bracket. This is measured at the true top of the head
#' tube, which is somewhere above the point where the top tube
#' and the head tube meet. The true length of the head tube is
#' a derived dimension, but the distance between the top of the
#' head tube and the point where the top tube and the head tube
#' meet, set here as `t_extension`, can be an arbitrary input if
#' you have a welded frame. In a lugged frame the top of the head
#' tube is the same as the top of the HT-TT lug ring.
#'
#' The same goes for the top of the seat tube extension. It too
#' is arbitrary for a welded frame, and the trigonometric
#' calculations for deriving its vertical and horizontal projections
#' are identical to those of the head tube extension so we might
#' as well use the same function for getting at them.
#'
#' @param frame_dims A tibble with frame dimensions from the front
#' and rear triangle, returned by [bicycle::wrap_frame_dims()].
#' @param tube The name of the tube to be extended: `ht_triangle`
#' for the head tube or `st_triangle` for the seat tube.
#' @param t_extension The length of the tube extension in millimeters
#' @seealso [bicycle::get_front_triangle_dims()]
#'
#' @return A tibble.
#' @export
add_tube_extension <- function(frame_dims,
tube = 'ht_triangle',
t_extension = 20) {
stopifnot(t_extension >= 0)
stopifnot(tube %in% c('ht_triangle', 'st_triangle'))
# recover the tube angle:
hp <- frame_dims[[tube]][['horizontal_projection']]
vp <- frame_dims[[tube]][['vertical_projection']]
t_angle <- 90 - atan(hp/vp)*180/pi
# get the t_extension triangle:
t_triangle = c(length = t_extension,
vertical_projection = t_extension * sin(t_angle * pi / 180),
horizontal_projection = t_extension * cos(t_angle * pi / 180))
# add it as a new column:
col_name <- paste0('t', tube)
frame_dims %>%
add_column(!!col_name := t_triangle)
}
#' Get rear triangle dimensions
#'
#' The rear triangle is made up of the projection in the horizontal plane
#' of the chain stay, the seat stay, and the seat tube. The seat tube is
#' the same as its projection in the horizontal plane, but the other two
#' tubes stick out at an angle, ans they must accommodate the width of
#' the rear wheel hub, so there's some trigonometry involved.
#'
#' A bike frame designer aims for a seat tube length (`st_length`) that
#' fits a rider of a given height, and also for a seat tube angle (`st_angle`)
#' and chain stay length (`cs_length`) that give the bike its desired riding
#' characteristics, though `cs_length` is subject to some other constraints
#' described in the documentation for [bicycle::get_cs_length()].
#'
#' The angle between the chain stay and the seat tube (`cs_st_angle`) is
#' pre-determined in lugged frame construction by how the bottom bracket
#' shell was cast.
#'
#' This function returns the dimensions of the rear triangle using the
#' convention described in the documentation for [bicycle::get_front_triangle_dims()].
#'
#' @inheritParams get_front_triangle_dims
#' @inheritParams get_old_spacing
#' @param cs_st_angle Angle between chain stay and seat tube, in degrees.
#' @seealso [bicycle::get_cs_length()]
#' @seealso [bicycle::get_front_triangle_dims()]
#'
#' @return A 3 x 3 tibble.
#' @export
#'
#' @examples
#' get_rear_triangle_dims()
get_rear_triangle_dims <- function(st_length = 500,
cs_length = 450,
st_angle = 71,
cs_st_angle = 61,
angle_btw_css = 14) {
# remember, this is a 2d projection of the bike frame: its
# shadow as if it were lying on the ground with the sun at noon.
# so the chain stay sticks out from the ground starting at the
# BB toward the DO, at half the angle between the chains stays.
# we want the length of its shadow:
cs_shadow <- cs_length * cos((angle_btw_css / 2) * pi / 180)
# derived from here (sketch it out on paper)
# cs_angle <- 90 - (cs_st_angle + (90 - st_angle))
cs_angle <- st_angle - cs_st_angle
cs_triangle <- c(length = cs_shadow,
vertical_projection = cs_shadow * sin(cs_angle * pi / 180),
horizontal_projection = cs_shadow * cos(cs_angle * pi / 180))
st_triangle <- c(length = st_length,
vertical_projection = st_length * sin(st_angle * pi / 180),
horizontal_projection = st_length * cos(st_angle * pi / 180))
ss_hp <- cs_triangle['horizontal_projection'] - st_triangle['horizontal_projection']
ss_vp <- st_triangle['vertical_projection'] - cs_triangle['vertical_projection']
ss_shadow <- sqrt(ss_vp^2 + ss_hp^2)
ss_triangle <- c(length = ss_shadow,
vertical_projection = ss_vp,
horizontal_projection = ss_hp)
tibble::tibble(cs_triangle, st_triangle, ss_triangle)
}
#' Get the dimensions of the bicycle frame
#'
#' This is a convenience wrapper that puts together the
#' output of [bicycle::get_front_triangle_dims()] and
#' [bicycle::get_rear_triangle_dims()].
#'
#' @inheritParams get_front_triangle_dims
#' @inheritParams get_rear_triangle_dims
#' @seealso [bicycle::get_front_triangle_dims()]
#' @seealso [bicycle::get_rear_triangle_dims()]
#'
#' @return A 7 x 3 tibble.
#' @export
#'
#' @examples
#' wrap_frame_dims()
wrap_frame_dims <- function(st_length = 500,
tt_length = 500,
st_angle = 71,
tt_angle = 0,
ht_angle = 71,
st_dt_angle = 60,
cs_length = 450,
cs_st_angle = 59,
angle_btw_css = 14) {
front_triangle <- get_front_triangle_dims(st_length = st_length,
tt_length = tt_length,
st_angle = st_angle,
tt_angle = tt_angle,
ht_angle = ht_angle,
st_dt_angle = st_dt_angle)
rear_triangle <- get_rear_triangle_dims(st_length = st_length,
cs_length = cs_length,
st_angle = st_angle,
cs_st_angle = cs_st_angle,
angle_btw_css = angle_btw_css)
df <- front_triangle %>%
dplyr::inner_join(rear_triangle)
for (i in colnames(rear_triangle)) {
names(df[[i]]) <- names(df$cs_triangle)
}
df
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.