# 2D operations and groups

[Start here](https://pegmatite.dev/guide/start.md). Fetch this topic when a profile must be grown, shrunk, rounded, filled, turned, mirrored or resized before its sweep, or built from several shapes in braces.

## 2D modifiers

`offset`, `round` and `fill` treat any 2D shape before its sweep. A pipeline reads left to right: steps before the sweep treat the profile, steps after it the solid. `round` here rounds the outline's corners, which become upright edges; `fillet` then rounds the solid's top edges:

```pegm
rect 40 30 | round 5 | extrude 10 | fillet 1 at #top
```

A 2D modifier after the sweep, or after a solid that never was a shape, is an error:

```pegm
/* Expected error: round treats a flat shape, not the swept solid. */
rect 40 30 | extrude 10 | fillet 1 at #top | round 5
```

Steps run in written order, each on the outline the one before left. Rounding then growing gives larger corners than growing then rounding:

```pegm
!@right 6
rect 40 30 | round 2 | offset 2 | extrude 4
rect 40 30 | offset 2 | round 2 | extrude 4
```

A modifier treats the innermost shape written before it. When a shape ends another, as a [tiling](https://pegmatite.dev/guide/tilings.md)'s area does, the modifier belongs to it: here the area is rounded and the cells are cut back to its corners. Braces end the tiling, so a modifier after them treats the cells:

```pegm
honeycomb 8 1.5 in rect 40 40 | round 8 | extrude 3
@right 6 { honeycomb 8 1.5 in rect 40 40 } | round 0.5 | extrude 3
```

A treated shape stays in its frame. A measured shape stays centred on its origin, and a path or a polygon of written points keeps its coordinates, so an [`apex`](https://pegmatite.dev/guide/extrude.md) or a [`revolve`](https://pegmatite.dev/guide/extrude.md) reads it as it reads the untreated shape. A [face](https://pegmatite.dev/guide/shapes.md) or a tiling still claims the box it claimed, grown or shrunk by an offset. The output is filled, so an open path is closed first, as `fill` closes it.

### `offset`

**Syntax:** **`| offset`** _distance_ [**`round`**]

Positive _distance_ expands the outline and narrows holes; negative shrinks it. Corners stay sharp, so `rect 40 30 | offset 2` is `rect 44 34`; very sharp corners are squared off, as on a mitred [wall](https://pegmatite.dev/guide/extrude.md). With `round`, every point of the new outline is the same distance from the old one, which rounds the corners the outline moves away from: outer ones as it grows, inward-turning ones as it shrinks.

```pegm
rect 40 30 | offset 2 round | extrude 4
```

An inward offset that leaves nothing is an error. Offset makes fits with a clearance: this lid's lip is the box outline shrunk by the wall and 0.2 more:

```pegm
def %outline rect 60 40 | round 4

{
  %outline | extrude 2
  %outline | extrude 28 wall 2 inside
}
@right 10 {
  %outline | extrude 2
  %outline | offset -2.2 | extrude 4
}
```

A [`sweep`](https://pegmatite.dev/guide/sweeps.md) trajectory takes no modifiers, so an offset trajectory goes in braces, which also end the offset: the `round` after them is the sweep's option.

```pegm
circle 3 | sweep along { rect 40 40 | offset 2 } round
```

### `round`

**Syntax:** **`| round`** _radius_

Rounds outer and inner corners to _radius_, the L's inner corner included; curves gentler than the radius stay. Extruded, it rounds the upright edges a [fillet](https://pegmatite.dev/guide/modifiers.md) would.

```pegm
polygon points [0 0] [40 0] [40 10] [10 10] [10 40] [0 40] | round 3 | extrude 4
```

A bar exactly twice the radius wide keeps its width and gets round ends: `rect 40 6 | round 3` is a slot. A narrower part, such as a spike or thin bar, is lost, and a gap narrower than twice the radius closes, such as the strut between tiling cells. A radius that leaves nothing is an error; `round 0` changes nothing. Only after `|` is `round` this modifier: `round(2.5)` is the [function](https://pegmatite.dev/guide/expressions.md), and after a sweep trajectory it is the sweep's option.

### `fill`

**Syntax:** **`| fill`**

Fills the holes in every outline, islands within holes included. Filling a hollow box's top face, a rim, makes a lid:

```pegm
box: block 40 30 20 | shell 2
on box #top box #top | fill | extrude 2
```

### `rotate`, `flip`, `scale`

**Syntax:** **`| rotate`** _angle_

**Syntax:** **`| flip`** _axis_

**Syntax:** **`| scale`** _factor_ [_y-factor_]

Before its sweep, a profile or a shape in a group is turned, mirrored or resized about its own origin, in its own plane. `rotate` turns counter-clockwise, `flip x` or `flip y` negates that axis, `scale` takes one factor for both axes or one per axis. They act before placement, which uses the transformed box, and interleave freely with outline modifiers, each acting on the result before it.

```pegm
{ rect 40 10  rect 40 10 | rotate 90 } | extrude 2
```

A wedge and its mirror image side by side make a triangle:

```pegm
def %half polygon points [0 0] [20 0] [0 10]
{ %half @left %half | flip x } | extrude 2
```

After the sweep they treat the solid instead: `rotate` and `flip` name an axis of space and `scale` reaches the height, see [modifiers](https://pegmatite.dev/guide/modifiers.md). Here the first is a plate 20 × 20 × 5, the second the whole block grown to 10 tall:

```pegm
!@right 6
rect 10 10 | scale 2 | extrude 5
rect 10 10 | extrude 5 | scale 2
```

## Groups

**Syntax:** **`{`** _shape_ … **`}`**

A group combines 2D shapes into one, as braces combine solids. It stands wherever a shape does; inside, shapes are placed and combined by the rules for solids, on the two axes a flat shape has.

### Placing shapes

Each shape is placed against the one before it, centred on it unless told otherwise, so a washer or a frame needs no coordinates. [`x` and `y`](https://pegmatite.dev/guide/placement.md) align and offset against the previous shape; an anchor lays it beside, with an optional gap:

```pegm
{ rect 60 10 @right 2 circle 10 @right 2 circle 10 } | extrude 2
```

`@left` and `@right` travel along X, `@front` and `@back` along Y, front towards −Y. `@top`, `@bottom`, `z` and `color` are errors inside a group; color belongs to the swept solid. `!` makes a setter persistent to the end of the group:

```pegm
{ !@right 2 rect 10 10 rect 10 10 rect 10 10 } | extrude 2
```

Shapes are placed as they stand upright, Y up, so `@back` points the same way after a [`path`](https://pegmatite.dev/guide/shapes.md), which draws Y down the page, as after a `circle`.

### The first shape

The first shape is placed against the group's origin and otherwise stays where its coordinates put it. A group of one shape is that shape: `{ path "M0,0 H20 L10,-20 Z" }` sweeps as the path does, apex and revolve axis included. An offset moves the first shape within its coordinates; an alignment puts that point of its box on the origin:

```pegm
{ x left rect 10 10 } | revolve
```

The square's left side is on the axis, so it revolves into a solid cylinder 20 across. `{ x 5 rect 10 10 }` is the same square and the same cylinder.

### Signs

`-`, `&` and `^` cut, keep and cancel as for [solids](https://pegmatite.dev/guide/booleans.md), each against everything before it in the group, so the first shape cannot carry one. A signed shape is placed by the same rules, but on an anchor's axis it reaches back into the previous shape instead of standing beside it, so this notch stands inside the right edge:

```pegm
{ rect 40 20 @right -rect 10 10 } | extrude 2
```

A second cut after a cut steps along beside it, as a row of holes does.

### Lists

A list builds each item from the shape it opened after, and the shape after the list is placed against that shape again. A persistent setter written in a list ends with it.

```pegm
{ rect 40 20 [ x left 4 -circle 6  x right -4 -circle 6 ] } | extrude 2
```

### What a group leaves

A group is one shape. It takes 2D modifiers as a whole, and is swept, used as a tiling's area or named by a [shape macro](https://pegmatite.dev/guide/macros.md) like any shape:

```pegm
def %washer $d { circle $d - circle ($d / 2) }

%washer 20 | offset -1 | extrude 2
```

Braces holding only shape macro calls are a group too: `def %pair { %a @right %b }` is a shape when `%a` and `%b` are.

A group claims the box of every shape that adds to the outline; a shape written with `-` or `&` claims none. A [`slot`](https://pegmatite.dev/guide/booleans.md) shape takes room and adds nothing, so a small shape can be swept into a larger box. The cube stands beside the 40 × 40 box, not the circle:

```pegm
{ slot rect 40 40 circle 10 } | extrude 2
@right cube 4
```
