Processing Geometry Suite

Processing Geometry Suite

Hi everyone.

I’m pleased to share something I’ve been working on the past few months – a 2D computational geometry that I’ve called Processing Geometry Suite.

The focus of the library is on visualisation rather than providing underlying data structures and so all methods in the library are static and most of them take in and return PShapes or PVectors.

I’ve made a real effort to make it easy to pick up – it’s well documented, the Github is replete with gifs illustrating the varied functionality, and there’s a handful of example sketches included too.

It certainly shares some functionality in common with existing computational geometry libraries for Processing but there’s a lot of novel functionality introduced too. Furthermore many of the existing libraries are poorly supported and documented. All of this hopefully makes PGS a very worthwhile addition to the Processing library ecosystem.

Overview

Library functionality is split over the following classes:

  • PGS_Construction
    • Construct uncommon 2D primitives
  • PGS_Contour
    • Methods that produce various contours from shapes: medial axes, straight skeletons, offset curves, etc.
  • PGS_Conversion
    • Conversion between PShapes and JTS Geometries
  • PGS_Morphology
    • Methods that affect the geometry or topology of shapes (buffering, simplification, smoothing, etc.)
  • PGS_Optimsation
    • Solve geometric optimisation problems, such as finding the maximum inscribed circle, or the closest vertex to a coordinate
  • PGS_Processing
    • Methods that process a shape in some way: compute hulls, partition, slice, etc.
  • PGS_ShapeBoolean
    • Boolean set-operations for 2D shapes
  • PGS_ShapePredicates
    • Various shape metrics (area, circularity, etc.) and predicates (“do these shapes intersect?”)
  • PGS_Transformation
    • Various geometric and affine transformations that affect vertex coordinates
  • PGS_Triangulation
    • Delaunay triangulation (constrained and refined) and earcut triangulation of shapes and point sets
  • PGS_Voronoi
    • Voronoi Diagrams of shapes and point sets

Installation

Processing IDE — Quick

Download the latest PGS.jar from releases and simply drag-and-drop it onto the Processing IDE.

Processing IDE — Permanently

Download the latest PGS.jar from releases and save it to Documents\Processing\libraries\PGS\library.

Result: Documents\Processing\libraries\PGS\library\PGS.jar.

(Note the .jar and the folder must be called PGS — rename the .jar if this is not the case).

Examples

A number of example Processing sketches are provided in examples.

Illustrations

A few illustrations below. There are many more on the Github repo

miteredInteriorvoronoishapeIntersectionpointsOnPerimeter2inscribedCirclegaussianSmooth

26 Likes

hi Micycle,
thanks for sharing this,
it’s amazing !!!

What a master work !
Thanks

@micycle looks to be an excellent library.
As an exercise I managed to translate the contour map example to JRubyArt with the use of this VectorList shim:-

java_import 'java.util.ArrayList'
java_import 'processing.core.PVector'

class VectorList
  attr_reader :array_list
  def initialize
    @array_list = ArrayList.new
  end

  def <<(val)
    array_list.add(PVector.new(val.x, val.y, val.z))
  end
end

Here’s the sketch in JRubyArt

require_relative './vector_list'
load_library :pgs
java_import 'micycle.pgs.PGS_Contour'

attr_reader  :heights, :max, :min
MAX = -1
MIN = 9999

def setup
  sketch_title 'Contour Map'
  @max = MAX
  @min = MIN
end

def draw
  background(0, 0, 40)
  populate_height_map
  isolines = PGS_Contour.isolines(heights.array_list, 0.08, min, max)
  isolines.to_hash.each do |isoline, value|
    isoline.set_stroke(
      color(
        map1d(value, min..max, 50..255),
        map1d(isoline.get_vertex(0).x, 0..width, 50..255),
        map1d(isoline.get_vertex(0).y, 0..height, 50..255)
      )
    )
    shape(isoline)
  end
end

def populate_height_map
  @heights = VectorList.new

  res = 16
  anim_speed = 0.005

  grid(width + res, height + res, res, res) do |x, y|
    z = norm(noise(x*0.01 + frame_count*anim_speed, y*0.01 + frame_count*anim_speed), -1.0, 1.0)
    h = Vec3D.new(x, y, 0)
    z += h.dist(Vec3D.new(mouse_x, mouse_y, 0))*0.005
    h.z = z
    heights << h
    @max = [max, z].max
    @min = [min, z].min
  end
end

def settings
  size(800, 800)
  smooth
end

The norm is used because JRubyArt now uses OpenSimplex2 noise (values -1.0…1.0)

2 Likes

Nice. I’m surprised Ruby has such interoperability with Java to the point where you can simply import Java libraries…

@micycle That’s down to the magic of JRuby, which is actively maintained cf Jython. The bit that is down to me is the load_library feature which can be used to load pure java libraries, pure ruby libraries and hybrid libraries.
PS: I tried using library on 64 bit arm (linux), and got unsupported architecture message any idea why that might be?

1 Like

Does it say which library?

My bad, it turns out there was an issue with the PiCrate library loader (should have recognised my own error message!!!). I tested library with vanilla processing-4.0 on Manjaro Arm64 and it worked. Here’s a snapshot of the sketch running using a patched PiCrate:-

2 Likes

Thanks very much @micycle - My post 2D lineart - culling hidden lines for SVG output / Plotting explains the problem I was facing.

I needed hidden line removal when overlaying shapes for plotter output (can’t use fills, limited to lines). I would have had no idea when it comes to geometry :slight_smile:

Time to dump my own solutions in favour of your library. Thank-you very much for packaging it so beautifully.

1 Like

Wow! How the hell did I not know this project existed? Wonderful project @micycle

1 Like

1.3.0 Update

I have recently released PGS 1.3.0, which has been in the making for 10 months. Since I originally shared the library here, its functionality has grown considerably so I thought it fitting to share the library again with this update.

New classes (since 1.0):

  • PGS_CirclePacking — a class for circle packings of shapes, subject to varying constraints and patterns of tangencies.
  • PGS_PointSet — a class that generates sets of 2D points having a variety of different distributions and constraints.
  • PGS_Coloring — a class for intelligent coloring of meshes (or mesh-like shapes) such that no two adjacent faces have the same color, while minimising the number of colors used.
  • PGS_Tiling — a class for tiling, tessellation and subdivision of the plane using periodic or non-periodic geometric shapes.
  • PGS_Meshing - a class to host mesh generation methods (excluding triangulation).
  • PGS_Hull — a dedicated class for convex and concave hulls of polygons and point sets.
  • PGS_SegmentSet — a class that generates random sets of non-intersecting line segments.

Just a few examples:
morphtangentPackspannervoroprecisioncoverage

13 Likes

@micycle

Thank you. It’s a good resource.

OMG! I can’t go near this library… It’s exactly what I find myself painstackingly coding every time… on P5 :confused:

Congrats, though, awesome addition. I’ll probably go through your code trying to understand your solutions and maybe reuse on P5. It looks tidy, fresh, thanks a lot!

2 Likes

Hello micycle and THANK YOU for your amazing work !

Like wageDu said, there are a numbers of things I’m trying to code myself and I feel almost like cheating to use your library :wink:
It’s a shame that processing doesn’t include it in the tools menu …

1 Like

The type string is ambiguous, what am I doing wrong?

image

1 Like

I’m having this error too and I’m wondering if anyone has solutions.

Ok, can you post your code?

This is from the examples directory, and PGS.jar is in the sketch’s code folder.

// This example shows concave hull, constrained voronoi, shape subtract & intersection, slicing, pointsOnExterior, shape intersection points
import processing.javafx.*;
import micycle.pgs.*;
import java.util.List;

PShape polygon;
List<PShape> subPartitions;
PVector center;

void setup() {
  size(800, 800, FX2D);
  smooth();
  colorMode(HSB, 1);
  stroke(color(324f/360f, 0.79, 0.93));
  strokeWeight(8);
  center = new PVector(width/2, height/2);

  List<PVector> randomPoints = PGS_PointSet.poisson(30, 30, width - 30, height - 30, 15,1337);
  polygon = PGS_Hull.concaveHullBFS2(randomPoints, 0);

  polygon.setFill(false);
  polygon.setStroke(color(1));
  polygon.setStrokeWeight(3);

  PShape partitions = PGS_Processing.convexPartition(polygon);
  subPartitions = new ArrayList<PShape>();
  for (PShape p : partitions.getChildren()) {
    PShape split = PGS_Processing.split(p);
    subPartitions.add(split.getChild(0));
    subPartitions.add(split.getChild(1));
  }
}

void draw() {
  background(0, 0, 0.075);

  PShape star = PGS_Construction.createStar(width/2, height/2, 11, mouseX*0.4, mouseX*0.4+100, 1);
  star = PGS_Transformation.rotateAroundCenter(star, frameCount*0.01f);
  star = PGS_Morphology.simplify(star, 1);
  PShape smallStar = PGS_Transformation.scale(star, 0.5);

  PShape outer =  PGS_ShapeBoolean.subtract(polygon, star);
  PShape inner = PGS_ShapeBoolean.subtract(star, polygon);
  inner = PGS_ShapeBoolean.subtract(inner, smallStar);
  PGS_Conversion.setAllFillColor(inner, color(.45, .5, .9));
  shape(inner);

  for (int i = 0; i < outer.getChildCount(); i++) {
    try {
      PShape p = PGS_Voronoi.innerVoronoi(outer.getChild(i), true);
      PGS_Conversion.setAllStrokeColor(p, color(i / ((float) outer.getChildCount() - 1), 1, 1), 2);
      shape(p);
    }
    catch (Exception e) {
    }
  }

  PShape polygon2 = PGS_ShapeBoolean.subtract(polygon, smallStar);

  PGS_Conversion.disableAllFill(polygon2);
  PGS_Conversion.setAllStrokeColor(polygon2, color(1), 3);
  shape(polygon2);

  PShape innerInner = PGS_ShapeBoolean.intersect(polygon, smallStar);
  List<PVector> ps = PGS_Processing.pointsOnExterior(star, 2, 0);
  innerInner = PGS_Processing.slice(innerInner, ps.get(0), ps.get(1)).getChild(0);
  PGS_Conversion.disableAllStroke(innerInner);
  shape(innerInner);

  List<PVector> intersections = PGS_Processing.shapeIntersection(polygon, inner);
  for (PVector x : intersections) {
    point(x.x, x.y);
  }
}

PGS.jar is in the sketch’s code folder

There seems to be a problem using the library when it’s hosted with the sketch (potentially a bug with Processing itself).

Install it as a permanent Processing library (in the Processing libraries folder) and things work fine.
Probably at: Documents\Processing\libraries\PGS\library\PGS.jar

@paulgoux ^^