knitr::opts_chunk$set(echo = TRUE)

Preamble

In Momocs, a shape is a matrix of (x, y) coordinates. When they are plenty, they are usually gathered in a Coo object which is, essentially, a list with at least a $coo component.

library(Momocs)
# a single shape
bot[1] %>% is_shp() # tests if it is a 2-col matrix without NAs
bot[1] %>% head()

# a collection of shapes
bot %>% class()     # Out for outlines, but also a Coo. See ?Coo 
bot %>% is.list()   # essentially a list
bot$coo %>% class() # with a $coo component
bot$coo[1:2] %>% lapply(head) # where shapes are gathered

Most operations on shapes can also be done on Coo objects. For example, getting the area or centering:

coo_area(bot[1]) # in pixels^2
coo_area(bot) %>% head()

In other words most coo_ functions are actually methods that can actually be passed with a single matrix or a Coo object.

Below, we will distinguish coo_* operations based on whether they return a shape (eg if it is a geometric operation) or a scalar (a single number that can be used as a shape descriptor).

Not all are described but you can obtain a full list of coo_ operations with:

cool <- apropos("coo_") # get the full list
head(cool)              # the firt 6
length(cool)            # how many are defined

Scalar descriptors of shape

coo_scalar calculate all scalar descriptors included in Momocs. coo_rectilinearity is not included by default since it takes a looot of time to compute.

df <- shapes %>% coo_scalars()
df

Each of the function listed below has its own method that you can used by prefixing it with coo_; see the example below with coo_area.

colnames(df) %>% cat(sep="\n")

coo_area(shapes[4]) # in pixels^2
coo_area(shapes)

# a subset
dplyr::tibble(area=coo_area(shapes),
                  perim=coo_perim(shapes)) %>% head()

Geometric operations

The functions below return shapes, so we exemplify them with graphs. For the sake of clarity/speed we illustrate them on single shape but, again, this can be done on Coo objects.

# A single example on Coo
bot %>% stack
bot %>% coo_center %>% stack()

Now we go with a cat and we will extensively use coo_plot. We define a function that will help display side by side the original and transformed cat; when I use p(coo_align), it is equivalent to coo_align(your_shp) or shp %>% coo_align

shp <- shapes[4]
shp %>% coo_plot()
# from a shape (shp) in the global environment (do not tell anyone !)
# apply a transformation (fun) and plot the two, side by side
p <- function(fun){
  title <- as.character(match.call()$fun) # capture the function name
  par(mfrow=c(1, 2))                      # side by side plots
  shp %>% coo_plot()                      # original shape
  shp %>% fun %>% coo_plot(main=title)    # transform and plot
  par(mfrow=c(1, 1))                      # back to original layout
}

Size

Isotropic

shp %>% coo_plot(main="original cat")
# scaling
shp %>% coo_scale %>% coo_plot(main="coo_scale")
# templating 
shp %>% coo_template(1) %>% coo_plot(ylim=c(-1, 1), main="coo_template"); rect(-0.5, -0.5, 0.5, 0.5)

Anisotropic

shp %>% coo_scalex(2) %>% coo_plot(main="coo_scalex")
shp %>% coo_scaley(3) %>% coo_plot(main="coo_scaley")
shp %>% coo_shearx(1) %>% coo_plot(main="coo_shearx")
shp %>% coo_sheary(1) %>% coo_plot(main="coo_sheary")

Alignment

Based on geometry

shp %>% coo_align          %>% coo_plot(main="coo_align")
shp %>% coo_aligncalliper  %>% coo_plot(main="coo_aligncalliper")
shp %>% coo_alignminradius %>% coo_plot(main="coo_alignminradius")
shp %>% coo_alignxax       %>% coo_plot(main="coo_alignxax")

Based on landmarks

# we use points with id 93 and 516 as landmarks
coo_bookstein(shp, 93, 516) %>% coo_plot(main="coo_bookstein")
# see coo_baseline for a more generic version

Translation

shp %>% coo_center %>% coo_plot(main="coo_center") # or coo_centre if you come from UK
shp %>% coo_center %>% coo_trans(x=  5) %>% coo_plot(main="coo_trans")
shp %>% coo_center %>% coo_trans(y= -2) %>% coo_plot(main="coo_trans")

Rotation

shp %>% coo_rotate(pi/2) %>% coo_plot(main="coo_rotate(pi/2)")
shp %>% coo_rotatecenter(-pi/6, center=c(250, 195)) %>% coo_plot(main="coo_rotatecenter") # with center ~on cat's nose
# mirrorring
shp %>% coo_flipx %>% coo_plot(main="coo_flipx")
shp %>% coo_flipy %>% coo_plot(main="coo_flipy")

Sampling

shp %>% coo_nb()
shp %>% coo_sample(36) %>% coo_plot(main="coo_sample")
shp %>% coo_sample_prop(1/20) %>%  coo_plot(main="coo_sample")
shp %>% coo_samplerr(36) %>% coo_plot(main="coo_samplerr") # regular radius
shp %>% coo_sample(36) %>% coo_interpolate(144) %>% coo_plot(points=TRUE, main="coo_interpolate")

Smoothing

shp %>% coo_sample(24) %>% coo_plot(main="a cubist cat")
shp %>% coo_sample(24) %>% coo_smooth(2) %>% coo_plot(main="coo_smooth")

If you have curves, you may prefer not to change first and last points:

olea[1] %>% coo_plot()
olea[1] %>% coo_smooth(12) %>% coo_plot(main="coo_smooth")
olea[1] %>% coo_smoothcurve(12) %>% coo_plot(main="coo_smoothcurve")

Slicing

Based on geometry

shp %>% coo_center %>% coo_plot(main="centered cat")
shp %>% coo_center %>% coo_up() %>% coo_plot(main="coo_up")
shp %>% coo_center %>% coo_down() %>% coo_plot(main="coo_down")
shp %>% coo_center %>% coo_left() %>% coo_plot(main="coo_left")
shp %>% coo_center %>% coo_right() %>% coo_plot(main="coo_right")

If you want to slice but want the first point to be on one end, then coo_slidegap is your friend :

shp %>% coo_center %>% coo_right() %>% coo_slidegap %>% coo_plot(main="coo_right")

Based on landmarks

coo_slice results in at least two shapes. Should be more useful with the argument ldk and within a Coo.

shp %>% coo_center %>% coo_slice(ids=c(93, 516)) %>% Opn %>% panel()

Indexing

Sliding

shp %>% coo_plot(main="original cat", cex.first.point = 2)
shp %>% coo_slide(id=93) %>% coo_plot(main="coo_slide", cex.first.point = 2) # same comment for ldk/Coo as for coo_slice
shp %>% coo_slidedirection("right") %>%  coo_plot(main="coo_slidedirection", cex.first.point = 2)

Closing/opening

shp12 <- shp %>% coo_sample(12)
shp12 %>% coo_plot(main="a cubist cat")
shp12 %>% coo_close %>% coo_plot(main="coo_close")
shp12 %>% coo_close %>% coo_unclose %>% coo_plot(main="coo_unclose")

Trimming/extracting

shp12 %>% coo_trimtop(3) %>% coo_plot(main="coo_trimtop")
shp12 %>% coo_trimbottom(3) %>% coo_plot(main="coo_trimtop")
shp12 %>% coo_trim(3) %>% coo_plot(main="coo_trim")
shp %>% coo_extract(93:516) %>% coo_plot(main="coo_extract")

Reversing

shp12 %>% coo_rev %>% coo_plot(main="coo_rev", cex.first.point = 2)

Misc

A side effect of coo_dxy is to set the first point on (0, 0)

shp %>% coo_dxy %>% coo_plot(main="coo_dxy", cex.first.point = 2)

coo_force2close can be use to spread differences between the first and last point, and to force closing:

shp %>% coo_center %>% coo_up %>% coo_plot(main="half a cat")
shp %>% coo_center %>% coo_up %>% coo_force2close %>% coo_plot(main="stop animal cruelty now")
# for the sake of reproducibility
set.seed(123)
shp %>% coo_jitter(factor=10) %>% coo_plot(main="coo_jitter")

Other coo operations

# coo_descriptors along
coo_centdist
coo_perimcum
coo_perimpts



# coo_descriptors non-scalars
coo_boundingbox
coo_centpos # should be a df?
coo_chull
coo_chull_onion
coo_diffrange # coo_range_diff
coo_lw
coo_truss

coo_range
coo_range_enlarge

# coo_drawers
coo_arrows
coo_draw
coo_draw_rads
coo_listpanel
coo_lolli
coo_oscillo # deprecate for a proper oscillo
coo_plot
coo_ruban

# coo_others
coo_angle_edges
coo_angle_tangent

coo_intersect_angle
coo_intersect_direction
coo_intersect_segment

# coo_testers
coo_is_closed
coo_likely_anticlockwise
coo_likely_clockwise

# helpers
coo_check
coo_nb

coo_ldk


MomX/Momocs documentation built on Nov. 18, 2023, 10:53 p.m.