Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 3 additions & 11 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,17 @@ name: CI
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Controls when the action will run. Triggers the workflow on push or pull request
# events but only for the development branch
on:
pull_request:
types: [assigned, opened, synchronize, reopened]

push:
branches:
- master
on: [push, pull_request, workflow_dispatch]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
smalltalk: [ Pharo64-11, Pharo64-10, Pharo64-9.0 ]
smalltalk: [ Pharo64-9.0, Pharo64-10, Pharo64-11, Pharo64-12, Pharo64-13, Pharo64-14 ]
name: ${{ matrix.smalltalk }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
- uses: hpi-swa/setup-smalltalkCI@v1
id: smalltalkci
with:
Expand Down
10 changes: 0 additions & 10 deletions .travis.yml

This file was deleted.

51 changes: 37 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,11 @@
# Geometry

[![Coverage Status](https://coveralls.io/repos/github/pharo-contributions/Geometry/badge.svg?branch=master)](https://coveralls.io/github/pharo-contributions/Geometry?branch=master) [![Build Status](https://travis-ci.org/pharo-contributions/Geometry.svg?branch=master)](https://travis-ci.org/pharo-contributions/Geometry)
[![Coverage Status](https://coveralls.io/repos/github/pharo-contributions/Geometry/badge.svg?branch=master)](https://coveralls.io/github/pharo-contributions/Geometry?branch=master) [![CI](https://github.com/pharo-contributions/Geometry/actions/workflows/build.yml/badge.svg)](https://github.com/pharo-contributions/Geometry/actions/workflows/build.yml)

A simple work-in-progress library for representing basic geometry shapes (line, circle, ellipse, ...) and doing some computations of top (mainly intersection).
A library for representing basic geometry shapes (line, circle, ellipse, ...) and doing some computations of top (mainly intersection).

The original repository is: http://smalltalkhub.com/#!/~NataliaTymchuk/Geometry

## Version management

This project use semantic versionning to define the releases. This mean that each stable release of the project will get associate a version number of the form `vX.Y.Z`.

- **X** define the major version number
- **Y** define the minor version number
- **Z** define the patch version number

When a release contains only bug fixes, the patch number increase. When the release contains new features backward compatibles, the minor version increase. When the release contains breaking changes, the major version increase.

Thus, it should be safe to depend on a fixed major version and moving minor version of this project.

## Install Geometry

To install Geometry on your Pharo image you can just execute the following script:
Expand All @@ -38,3 +26,38 @@ To add Geometry to your baseline just add this:
```

Note that you can replace the v2.x.x tag by a branch as #master or #development or a tag as #v1.0.0, #v1.? or #v1.0.x or a commit SHA.

## Quick start

Once installed, the shapes are regular Smalltalk objects. Everything below is runnable in a Playground.

```Smalltalk
"Create some shapes"
aPoint := GPoint x: 0 y: 0.
aSegment := GSegment with: (GPoint x: 0 y: 0) with: (GPoint x: 3 y: 4).
aCircle := GCircle center: (GPoint x: 0 y: 0) radius: 5.

"Compute their intersections (answer is a collection of points)"
aCircle intersectionsWith: aSegment. "a Set(a GPoint(3,4))"
aSegment intersectionsWith: aCircle. "same result"

"Test membership and query metrics"
aCircle includes: (GPoint x: 3 y: 4). "true: the point is on the circle"
aCircle perimeter. "31.41592653589793"
aSegment length. "5"
```

All elements share the same API (`includes:`, `intersectionsWith:`, `translateBy:`, `length`/`perimeter`/`area`, ...). The full guide, including a description of every shape, is in [resources/user_documentation.md](resources/user_documentation.md).

## Version management

This project use semantic versionning to define the releases. This mean that each stable release of the project will get associate a version number of the form `vX.Y.Z`.

- **X** define the major version number
- **Y** define the minor version number
- **Z** define the patch version number

When a release contains only bug fixes, the patch number increase. When the release contains new features backward compatibles, the minor version increase. When the release contains breaking changes, the major version increase.

Thus, it should be safe to depend on a fixed major version and moving minor version of this project.

232 changes: 231 additions & 1 deletion resources/user_documentation.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,233 @@
# User documentation

> TODO
## About

Geometry is a Pharo library for representing basic geometric shapes (point, line, segment, ray, arc, circle, ellipse, polygon, rectangle, triangle, ...) and computing properties of them.

## Core concepts

Two families of entities exist:

- **Points and vectors** live in `Geometry-Core`: `GPoint`, `GVector`, `GMatrix`, `GAngle`, with Euclidean coordinates handled by `GCoordinates`, `G2DCoordinates` and `G3DCoordinates`.
- **Elements** are subclasses of `GElement`. 1D elements are subclasses of `G1DElement` (`GLine`, `GSegment`, `GRay`, `GArc`, `GPoint`); 2D shapes are subclasses of `GShape` (`GEllipse` and its subclass `GCircle`, `GPolygon` and its subclasses `GRectangle` and `GTriangle`).

## Points and vectors

```Smalltalk
p := GPoint x: 3 y: 4. "a GPoint(3,4)"
q := GPoint x: 0 y: 0.
p distanceTo: q. "5"
p + (GVector x: 1 y: 1). "a GPoint(4,5) — translated copy"
p - q. "a GVector(3,4) — difference of two points"
(p x) -> (p y). "3 -> 4"
p asPoint. "3@4 — convert to a plain Pharo point"
(1 @ 2) asGPoint. "convert a plain Pharo point"
```

Points are also elements, so you can ask `p1 segment: p2` to build a segment, and compute `p1 intersectionsWith: p2`.

Vectors support the usual arithmetic (`+`, `-`, `*`, `/`), `length`, `dotProduct:`, `angleWith:` and `nonOrientedAngleWith:`.

```Smalltalk
v1 := GVector x: 3 y: 4.
v2 := GVector x: 1 y: 0.
v1 length. "5"
v1 dotProduct: v2. "3"
```

### Angles

`GAngle` wraps an angle in radians. Construction accepts degrees or radians (`rads` is used for radians; `radians` is deprecated to avoid a conflict with the `Units` package).

```Smalltalk
straight := GAngle degrees: 180.
right := GAngle radians: Float pi / 2.
right rads. "1.5707963267948966"
right isRight. "true"
straight isStraight. "true"
straight isAcute. isObtuse. isReflex.
right sin, right cos, right tan. "trigonometry"
```

### Matrices

`GMatrix` stores a 2D array of numbers.

```Smalltalk
m := GMatrix rows: { {1. 2}. {3. 4} }.
m at: 1 at: 1. "1"
m at: 2 at: 2. "4"
m determinant. "-2"
```

## 1D elements

`GLine`, `GSegment`, `GRay` and `GArc` are the 1D elements (points are covered above). Common API: `length`, `distanceTo:`, `translateBy:`, `intersectionsWith:`, `includes:`.

### Line

A line goes through two points and extends **infinitely** in both directions. `through:and:` only picks two points it passes through, the points do not bound it. Since it is infinite, `length` answers `Float infinity`.

```Smalltalk
line := GLine through: (GPoint x: 0 y: 0) and: (GPoint x: 3 y: 4).
vertical := GLine a: 1 b: 0 c: -1. "general form a*x + b*y + c = 0 : x - 1 = 0"
line isParallelTo: vertical.
line angleWith: vertical.
```

### Segment

A segment is the **finite** piece of a line between two points: it starts at its first point (`v1`) and stops at its second (`v2`). `length` is the distance between the two points.

```Smalltalk
s := GSegment with: (GPoint x: 0 y: 0) with: (GPoint x: 3 y: 4).
s length. "5"
s midPoint. "a GPoint(3/2, 2)"
s perpendicularBisector.
s distanceTo: (GPoint x: 0 y: 2).
```

### Ray

A ray starts at its origin point (`initialPoint`) and goes **infinitely** in the direction of its `directionPoint` (the direction from `initialPoint` towards `directionPoint`). `length` answers `Float infinity`; `flipped` answers the opposite ray (same origin, reversed direction).

```Smalltalk
r := GRay origin: (GPoint x: 0 y: 0) direction: (GPoint x: 1 y: 1).
r flipped. "the opposite ray"
r includes: (GPoint x: 2 y: 2). "true"
```

### Arc

An arc is a **finite** portion of a circle's circumference: it lies on the circle of `center` with the arc's `origin` point on it, and spans the given `angle` (the angle between the vectors `center-origin` and `center-endPoint`). `length` is `radius * centralAngle`.

```Smalltalk
a := GArc center: (GPoint x: 0 y: 0) origin: (GPoint x: 3 y: 4) angle: (GAngle degrees: 90).
a radius. "5"
a endPoint. "the point at the end of the arc"
a centralAngle.
a length. "arc length"
```

## 2D shapes

Shapes are closed 2D surfaces, so unlike 1D elements they have a `perimeter` and appear in an `encompassingRectangle`. Common `GShape` API: `perimeter`, `semiperimeter`, `center`, `extent`, `encompassingRectangle`, `fitInExtent:` and the inherited `translateBy:` / `intersectionsWith:`.

### Ellipse

An ellipse is the set of points whose sum of distances to two foci is constant. It is defined by a `center`, a `vertex` (an extremity of the major axis) and a `coVertex` (an extremity of the minor axis).

```Smalltalk
ellipse := GEllipse center: (GPoint x: 0 y: 0) vertex: (GPoint x: 5 y: 0) coVertex: (GPoint x: 0 y: 3).
ellipse area. "π * 5 * 3"
ellipse foci, fociLocation.
ellipse majorAxis, minorAxis.
ellipse semiMajorAxisLength, semiMinorAxisLength.
ellipse encompassingRectangle.
```

### Circle

A circle is the special case of an ellipse where both axes are equal: the set of points at a fixed `radius` from a `center`. `GCircle` is a subclass of `GEllipse`, so everything documented on the ellipse also works on a circle.

```Smalltalk
circle := GCircle center: (GPoint x: 0 y: 0) radius: 5.
circle perimeter. "31.41592653589793"
circle radius.
circle includes: (GPoint x: 3 y: 4). "true (boundaries included)"
```

### Polygon

A polygon is a closed chain of segments (the `edges`) between an ordered list of `vertices`: each edge goes from one vertex to the next, and the last vertex is joined back to the first. The vertices keep the order you give them with `GPolygon vertices: aCollection`.

```Smalltalk
polygon := GPolygon vertices: {
(GPoint x: 0 y: 0).
(GPoint x: 4 y: 0).
(GPoint x: 4 y: 3).
(GPoint x: 0 y: 3) }.
polygon edges. "an OrderedCollection of GSegment"
polygon perimeter. "14"
polygon encompassingRectangle.
```

Note:
- The convex hull of a collection of points is computed with `GPolygon convexHullOn: aCollection`.

### Rectangle

A rectangle is a polygon with four vertices whose edges are perpendicular and axis-aligned, defined by an origin and a corner (or its left/right/top/bottom bounds). It is a subclass of `GPolygon`.

```Smalltalk
rect := GRectangle origin: (GPoint x: 0 y: 0) corner: (GPoint x: 4 y: 3).
rect area. "12"
rect width, rect height, rect extent, rect center.
rect diagonals.
```

### Triangle

A triangle is a polygon with exactly three vertices. It is a subclass of `GPolygon`.

```Smalltalk
triangle := GTriangle with: (GPoint x: 0 y: 0) with: (GPoint x: 4 y: 0) with: (GPoint x: 2 y: 3).
triangle area. "6"
triangle circumscribedCircle.
triangle isDegenerate.
```

## Intersections

Every element can compute its intersection points with any other element:

```Smalltalk
segment := GSegment with: (GPoint x: 0 y: 0) with: (GPoint x: 3 y: 4).
circle intersectionsWith: segment. "a Set(a GPoint(3,4))"
segment intersectionsWith: circle. "same result: double dispatch"
line intersectionsWith: line.
ellipse intersectionsWith: (GCircle center: (GPoint x: 0 y: 0) radius: 5).
```

- The message is symmetric: `a intersectionsWith: b` and `b intersectionsWith: a` answer the same collection of points.
- When there is no intersection the answer is an **empty collection** `{}` (not `nil`).
- Perform a membership test instead when you only need to know if a *point* lies on an element: `element includes: aPoint`.

## Membership tests

All `GElement`s answer the following point queries:

```Smalltalk
element includes: aPoint. "true if the point is on the element, boundaries included"
element boundaryContains: aPoint. "true if the point is on the boundary"
element contains: aPoint. "true if included but NOT on the boundary"
```

`boundaryContainsAny:`, `boundaryContainsWhichOf:` accept collections of points. Note that for 1D elements (lines, segments, rays, arcs, points) `boundaryContains:` is equivalent to `includes:`, so `contains:` answers `false` always.

## Transformations

Be careful: `translateBy:` and `scaleBy:` **modify the receiver** in place. If you need to keep the original, work on a copy (`element copy translateBy: ...`). The exception is point arithmetic and rotation: `GPoint >> +` and `rotatedBy:` / `rotatedBy:about:` return a new point, while `rotateBy:` / `rotateBy:about:` change the receiver.

```Smalltalk
element translateBy: (GPoint x: 1 y: 1). "or a GVector: any object with coordinates"
point rotatedBy: (GAngle degrees: 90). "rotates around the origin; see rotatedBy:about: for an arbitrary center"
point rotateBy: (GAngle degrees: 90). "same rotation, but changes the point"
polygon scaleBy: 2. "polygons only (rect, triangle); scales about center"
polygon fitInExtent: (10 @ 10). "polygons and ellipses: rescale to fit inside a given extent"
```

`fitInExtent:` also modifies the receiver. Note `scaleBy:` exists only on `GPolygon`/`GRectangle`/`GTriangle`; ellipses expose no `scaleBy:`.

## Converting to and from plain Pharo objects

- `Point` is extended with `asGPoint` and `asGVector`, `Number` with the `=~` close-to operator.
- `GPoint >> asPoint` and `GCoordinates >> asGPoint` / `asGVector` convert back.

## A note on equality and floating point

Do not compare coordinates with `=`. Because results come from float arithmetic, equality is answered with the `=~` operator (a close-to comparison extended on `Number` and reused by `GPoint`, `GVector`, `GAngle`...):

```Smalltalk
(result x =~ expectedX) and: [ result y =~ expectedY ].
```
2 changes: 1 addition & 1 deletion src/BaselineOfGeometry/BaselineOfGeometry.class.st
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A baseline for Geometrypackage
Class {
#name : #BaselineOfGeometry,
#superclass : #BaselineOf,
#category : 'BaselineOfGeometry'
#category : #BaselineOfGeometry
}

{ #category : #baseline }
Expand Down