Pts.guides

We are all in the gutter, but some of us are looking at the stars, said Oscar Wilde. To him, and to many of us, a sky full of stars moves us to look into that vast darkness and, with imagination, connect those scattered and flickering dots. What would a sky be without stars, and what would a starry night be without us looking and imagining!

A group of points is never just a mathematical concept. We actively connect the dots and find strange forms and humanistic meanings in them.

In ** Pts**, these acts of imagination are known as "Op". They transform a static point into an active one, a noun into a verb, a vector space into an expressive canvas.

Let's begin with an example. Suppose there are 100 points randomly placed on a canvas, and a pointer moves randomly about. A simple (indeed boring) act of imagination might be to ask: which point is closest to the pointer?

We can draw this whole scene a few lines of code:

```
// make 100 pts and pointer
var pts = Create.distributeRandom( space.innerBound, 100 );
let t = space.pointer;
// sort the pts
pts.sort( (a,b) =>
a.$subtract(t).magnitudeSq() - b.$subtract(t).magnitudeSq()
);
// draw the pts
form.fillOnly("#123").points( pts, 2, "circle" );
form.fill("#f03").point( pts[0], 5, "circle" );
form.strokeOnly("#f03", 2).line( [pts[0], space.pointer] );
```

Notice we just used the javascript `sort`

function to rearrange the `pts`

group by comparing two points' distance to `space.pointer`

. Then we draw the first point in the group (the closest) in red.

The humble `sort`

function is essentially an "Op" by our definition. It transforms a group of points and makes it meaningful. From here on, it's easy to imagine what other structures we can derive from this simple sketch. For example, what if we visualize all points' distances to the pointer in different ways:

```
pts.forEach( (p, i) =>
form.point( p, 20 - 20*i/pts.length, "circle" ) )
```

The group of points becomes active. It's somewhat interesting and kind of a mess -- a starting point for further experimentation.

** Pts** includes many different Ops to help you make the points meaningful. Next we will look at them in more details.

The Op module includes various static functions that deal with specific forms such as `Rectangle`

and `Curve`

, and Num module includes utility classes like `Num`

and `Geom`

for numeric and geometric calculations. Most of them are straightforward and easy to use.

It's time to let our imaginary forces work on these functions. Keeping most of the code from above, let's try the `perpendicularFromPt`

function. Given a line and a point, this Op finds a perpendicular line (ie, shortest distance) between the point and the line.

```
let path = [new Pt(), space.pointer];
let perpends = pts.map( (p) => [p, Line.perpendicularFromPt(path, p)] );
```

First we create a line by joining the pointer and the top-left corner at (0,0), and then we convert the set of random points on canvas to perpendicular lines. Also note that, since we don't need additional features from `Group`

, we can just use Array to store the Pts for drawing. Pretty fun and simple, right?

Ops can also construct shapes out of points. Creating a rectangle or a line from 2 points is obvious, so let's get a bit more elaborate.

```
// create a group of 4 Pts from rectangle
let c = space.center;
let corners = Rectangle.corners( Rectangle.fromCenter( c, space.height ) );
// interpolate with time to make them move
let cycle = (t, i) => Num.cycle( (t+i*500)%3000/3000 );
let pts = corners.map( (p, i) => Geom.interpolate( p, c, cycle(time, i)) );
// close the B-spline by adding first 3 anchors at the end
pts.push( space.pointer );
pts = pts.concat( pts.slice(0, 3) );
// draw the B-spline curve
let curve = Curve.bspline( pts );
form.fill("#f03").stroke("#fff", 3).polygon( curve );
```

What's going on here? First, we use `Rectangle.fromCenter`

to make a rectangle and then get its 4 corners as a group.

Then, we define a function called `cycle`

to get a value between 0 to 1 for interpolation. The function takes two parameters `t`

(for time) and `i`

(for index). And we map the 4 corners into 4 interpolated points, making use of `Geom.interpolate`

op.

Next, we add the first 3 points again to the end of the `pts`

group, which is a quick way to close a b-spline curve. Finally, we get the curve from `Curve.bspline`

and just draw it.

Knowing how bspline works, we can easily apply it to our 100 random points. The following sketch also uses `Polygon.convexHull`

: think of it like a rubber band that wraps around a group of points.

That was quick! By combining different ops together, you can quickly try out and compare different options in forms and interactions.

If you think of code like a narrative, then the static ops are like monologues — telling the story in a dull way.

```
// dull
Polygon.convexHull( pts )
// meh
pts.convexHull()
// fun
makeRubberBand()
```

The `op`

function in both Pt and Group enables you to turn your dull code into an expressive one. Let's see how it works:

```
let makeRubberBand = pts.op( Polygon.convexHull );
makeRubberBand();
```

When you supply `op`

with a function, it applies the Pt or Group as a parameter to that function, and returns a new function with one less parameter. That's all. So here `Polygon.convexHull(group)`

becomes `makeRubberBand()`

since `pts`

is applied as the first parameter `group`

. If it's still confusing, think of it as a noun ("the `pts`

") turning into a verb ("make rubber band using the `pts`

").

Let's illustrate this with a concrete example. Suppose we want to make 50 lines by pairing the 100 random points, and then find out which lines intersect with another line drawn by the pointer. What's the code?

It only takes 3 lines:

```
let pairs = pts.segments(2, 2);
let hit = new Group(space.center, space.pointer).op( Line.intersectLine2D );
let hitPts = pairs.map( (pa) => hit( pa ) );
```

First, we take every 2 points in `pts`

to make 50 lines. Next, we make a line from space's center to pointer, and immediately turn it into an op of `Line.intersectLine2D`

. Lastly we just apply the `hit`

function to each pair and get its intersection point.

This approach works best if the op will be re-used in different scenarios, or if it can make the code easier to read. Of course, you can always use the static intersectLine2D function inside the `map(...)`

, or even create a custom function and call it `hit`

. Just like there're many ways to tell a story, there're many ways to write code.

`Num`

from "Num" module includes helper functions to simplify numeric calculations.

```
Num.cycle( 0.3 ); // cycle between 0...1...0
Num.mapToRange( 5, 1,100, 0, 2 ); // map a value to new range
Num.lerp( 1, 100, 0.2 ); // linear interpolation
```

`Geom`

from "Num" module includes helper functions to simplify geometric calculations.

```
Geom.boundAngle( 361 ); // bound between 0 to 360
Geom.withinBound( p1, top_left, bottom_right );
Geom.interpolate( p1, p2, 0.3 );
```

`Line`

from "Op" module helps you create and work with lines.

```
Line.fromAngle( p1, Math.PI/3, 10 ); // create with angle and distance
Line.collinear( p1, p2, p3 );
Line.intersectRay2D( ln1, ln2 );
Line.subpoints( 5 ); // get 5 evenly distributed pts on the line
```

`Rectangle`

from "Op" module helps you create and work with rectangles.

```
Rectangle.fromCenter( center, 100, 50 );
Rectangle.corners();
Rectangle.sides();
Rectangle.quadrants(); // get 4 inner rectangles
Rectangle.intersectRect2D( rect1, rect2 );
```

`Circle`

from "Op" module helps you create and work with circles.

```
Circle.fromCenter( center, 10 );
Circle.fromRect( rect );
Circle.toRect();
Circle.intersectCircle2D( c1, c2 );
```

`Triangle`

from "Op" module helps you create and work with triangles.

```
Triangle.fromCircle( c ); // equilateral triangle
Triangle.fromRect( rect );
Triangle.incircle( tri );
Triangle.orthocenter( tri );
Triangle.medial( tri );
```

`Polygon`

from "Op" module helps you create and work with polygons.

```
Polygon.centroid( poly );
Polygon.convexHull( poly );
Polygon.lines( poly ); // get line segments
Polygon.intersectPolygon2D( poly, lines );
```

`Curve`

from "Op" module helps you create and work with curves.

```
Curve.catmullRom( pts );
Curve.cardinal( pts );
Curve.cardinal( pts, 20, 0.3 ); // step and tension parameters
Curve.bezier( pts );
Curve.bspline( pts );
```

Check out the full documentation too.