deform_polygon: Deform polygon

Description Usage Arguments Value Note Author(s) Examples

View source: R/deform-polygon.R

Description

This function deforms a polygon by moving each vertex a specified distance. If you want a smooth deformation of a polygon with few vertices (e.g. a square), then interpolate_polygon can be used to create an interpolated version of the polygon (see example below).

Usage

1
deform_polygon(vertex.df, deform.distance, method = "bisector")

Arguments

vertex.df

A data frame where each row corresponds to the vertex of a polygon. It must contain the columns x, y, and optionally group (must be integer). x and y specify the coordinates of the vertex, and group is used to indicate which polygon the vertex belongs to. If vertex.df contains only a single polygon, group can be omitted.

deform.distance

The distance that each vertex is to be moved. Can either be a single value or a vector containing one value per vertex. If the value is positive, the vertex is moved "outwards", while a negative value moves the vertex "inwards".

method

Must be one of the following values:

  • "bisector", where the vertex is moved along the bisector of the two adjacent edges

  • "centroid", where the vertex is moved along the line between the vertex and the centroid of the polygon

Value

The same data frame that is given in vertex.df, the only difference being that the x- and y-columns are changed due to the deformation.

Note

This function is not robust, and can lead to strange results. While method = "bisector" generally looks better, it struggles in certain situations (see example #3 below), and should only be used for expanding convex polygons. If the polygon is concave and deform.distance contains both positive and negative values, method = "centroid" might be a better choice.

Author(s)

Mathias Isaksen mathiasleanderi@gmail.com

Examples

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# Example 1: Deformation of rounded square
# Start with a square
square.df = data.frame(x = c(-1, 1, 1, -1), y = c(1, 1, -1, -1))
# Round the corners of the square
rounded.df = round_polygon_corners(square.df, corner.radius = 0.3)
# The rounded polygon has many vertices along the corners, but none along the
# straight edges. We need to resample!
# Create function that allows us to resample vertices along the boundary of the polygon
interpolation.function = interpolate_polygon(rounded.df)
# How many vertices should resampled polygon contain?
num.vertices = 1000
# Create vector with n values evenly spaced between 0 and 1
# (0 and 1 correspond to the same point, so the latter is removed)
time = seq(0, 1, length.out = num.vertices + 1)[-(num.vertices + 1)]

# Polygon that consists of 1000 vertices placed evenly along the boundary of
# the rounded polygon
resampled.df = interpolation.function(time)

# Function that gives the amount of deformation for time between 0 and 1
deform.function = function(t, freq) 0.1 + (0.35 - 0.1)*(1 + cos(freq*2*pi*t)) / 2
# Vector that gives the amount of deformation for each vertex in resampled.df
deform.amount = deform.function(time, 4)

# Deformed polygon
deform.df = deform_polygon(resampled.df, deform.amount, method = "bisector")

library(ggplot2)
ggplot()+
  geom_polygon(data = square.df, aes(x = x, y = y, color = "1. Original"), fill = NA)+
  geom_polygon(data = rounded.df, aes(x = x, y = y, color = "2. Rounded"), fill = NA)+
  geom_polygon(data = deform.df, aes(x = x, y = y, color = "3. Deformed"), fill = NA)+
  scale_color_manual(values = c("black", "blue", "red"))+
  coord_fixed()

# Example 2: Comparison of "bisector" and "centroid" methods
# Start with a 2 x 10 rectangle
rectangle.df = data.frame(x = c(-1, 1, 1, -1), y = c(5, 5, -5, -5))

# Resample polygon, this time without rounding
interpolation.function = interpolate_polygon(rectangle.df)
resampled.df = interpolation.function(time)

# Deformation that oscillates faster than in example 1
deform.amount = deform.function(time, 20)

# Deformed polygons
bisector.df = deform_polygon(resampled.df, deform.amount, method = "bisector")
centroid.df = deform_polygon(resampled.df, deform.amount, method = "centroid")

# From left to right, the plot shows the original rectangle, and the deformed polygons
# from the "bisector" and "centroid" methods, respectively.
# The result from "bisector" looks better overall, but struggles at sharp corners,
# where the deformed polygon ends up having large gaps between the vertices.
# This is avoided by rounding the polygon before deformation.
# The deformed polygon from the "centroid" method handles the corners without problem,
# but the amount of deformation is not consistent along the vertical sides.
ggplot()+
  geom_polygon(data = rectangle.df, aes(x = x, y = y), fill = "red")+
  geom_polygon(data = bisector.df, aes(x = x + 3, y = y), fill = "red")+
  geom_polygon(data = centroid.df, aes(x = x + 6, y = y), fill = "red")+
  geom_point(data = bisector.df, aes(x = x + 3, y = y), color = "black", size = 0.5)+
  geom_point(data = centroid.df, aes(x = x + 6, y = y), color = "black", size = 0.5)+
  coord_fixed()

# Example 3: Situation where "bisector" method fails
set.seed(123)
# Generate random polygon using polar coordinates
num.vertices = 10
theta = seq(0, 2*pi, length.out = num.vertices + 1)[-(num.vertices + 1)]
radius = runif(num.vertices, 0.5, 1)
vertex.df = data.frame(x = radius*cos(theta), y = radius*sin(theta))
# How the original polygon looks:
ggplot()+
  geom_polygon(data = vertex.df, aes(x = x, y = y), fill = NA, color = "black")+
  coord_fixed()

# Resample polygon before deforming
interpolation.function = interpolate_polygon(vertex.df)
resampled.df = interpolation.function(time)

# Deform function goes from -0.2 to 0.2, and back to -0.2
deform.function = function(t) -0.2*cos(2*pi*t)
deform.amount = deform.function(time)

# Deform resampled polygon
deform.df = deform_polygon(resampled.df, deform.amount)

# How the deformed polygon looks:
ggplot()+
  geom_polygon(data = deform.df, aes(x = x, y = y), fill = NA, color = "black")+
  coord_fixed()
# The combinations convex corner/negative deformation (right side of plot)
# and concave corner/positive deformation (left side) leads to a shape that is
# self-intersecting.

mathiasisaksen/artKIT documentation built on Dec. 21, 2021, 2:52 p.m.