{"id":22647916,"url":"https://github.com/juliageometry/geometricalpredicates.jl","last_synced_at":"2025-07-25T20:43:25.279Z","repository":{"id":21663738,"uuid":"24984662","full_name":"JuliaGeometry/GeometricalPredicates.jl","owner":"JuliaGeometry","description":"Fast and robust 2D \u0026 3D incircle/intriangle/etc. for Julia","archived":false,"fork":false,"pushed_at":"2022-07-05T15:33:18.000Z","size":113,"stargazers_count":57,"open_issues_count":6,"forks_count":12,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-05T02:09:34.507Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JuliaGeometry.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-10-09T10:38:31.000Z","updated_at":"2024-06-03T10:01:51.000Z","dependencies_parsed_at":"2022-07-08T04:17:50.481Z","dependency_job_id":null,"html_url":"https://github.com/JuliaGeometry/GeometricalPredicates.jl","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaGeometry%2FGeometricalPredicates.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaGeometry%2FGeometricalPredicates.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaGeometry%2FGeometricalPredicates.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaGeometry%2FGeometricalPredicates.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JuliaGeometry","download_url":"https://codeload.github.com/JuliaGeometry/GeometricalPredicates.jl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246150409,"owners_count":20731419,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-09T07:35:46.167Z","updated_at":"2025-03-29T06:48:30.162Z","avatar_url":"https://github.com/JuliaGeometry.png","language":"Julia","funding_links":[],"categories":[],"sub_categories":[],"readme":"# GeometricalPredicates.jl\n\n[![Build Status](https://travis-ci.org/JuliaGeometry/GeometricalPredicates.jl.svg?branch=master)](https://travis-ci.org/JuliaGeometry/GeometricalPredicates.jl)\n[![Coverage Status](https://coveralls.io/repos/JuliaGeometry/GeometricalPredicates.jl/badge.svg?branch=master)](https://coveralls.io/r/JuliaGeometry/GeometricalPredicates.jl?branch=master)\n\nFast, robust 2D and 3D geometrical predicates on generic point types.\nImplementation follows algorithms described in the [Arepo paper](http://arxiv.org/abs/0901.4107)\nand used (for e.g.) in the [Illustris Simulation](http://www.illustris-project.org/). License: MIT. Bug reports welcome!\n\nHow does it work?\n--------------------\nCalculations are initially performed on `Float64` while bounding max\nabsolute errors. If unable to determine result, fall back to exact\ncalculation using `BigInt`s. This is a form of floating point filtering.\nMost calculations are cached for fast repeated testing of\nincircle/intriangle predicates.\n\nCurrent limitations\n--------------------\n* Due to the numerical methods used, all coordinates are internally represented as `Float64`. In addition all must reside in the range `1.0\u003c=x\u003c2.0`. In this range, according to IEEE754, `Float64`s have a constant exponent, hence their mantissa can be used for a one to one mapping to integers, which in turn are used for exact calculations using `BigInt`s.\n* It is assumed that primitive vertices don't overlap. It is currently the responsibility of the user to make sure this is the case.\n* It is assumed tetrahedron vertices don't all lie in the same line. It is the user's responsibility to make sure it is so.\n* Testing points are assumed not to overlap any vertices. As usual, it is the user's responsibility to make sure this is the case.\nExcept for the 1st restriction, all others could be easily implemented. Currently these features are not needed by me. If you need missing features, please open an issue I may develop it!\n\nHow to use?\n--------------\n### Installation\n```julia\n]add GeometricalPredicates\n```\nFor Julia 0.6 and older\n```julia\nPkg.add(\"GeometricalPredicates\")\n```\n\n### Points\n```julia\nusing GeometricalPredicates\n\n# create a 2D point in (x, y) = (1.1, 1.9)\nmypoint = Point(1.1, 1.9)\ntypeof(mypoint) # -\u003e Point2D\n\n# create a 3D point in (x, y, z) = (1.1, 1.9, 1.5)\nmypoint = Point(1.1, 1.9, 1.5)\ntypeof(mypoint) # -\u003e Point3D\n\n# getting coordinates:\ngetx(mypoint) # -\u003e 1.1\ngety(mypoint) # -\u003e 1.9\ngetz(mypoint) # -\u003e 1.5\n```\n`Point2D` is a subtype of `AbstractPoint2D`, and `Point3D` is a subtype of `AbstractPoint3D`.\nYou can implement custom point types by subtyping these abstract types. These\ncustom point types can be used with the rest of the package:\n```julia\ntype MyCustomPointType \u003c: AbstractPoint2D\n    _x::FLoat64\n    _y::Float64\n    _mass::Float64\nend\n\ngetx(p::MyCustomPointType) = p._x\ngety(p::MyCustomPointType) = p._y\n```\nimplementing `getx`, `gety`, and `getz` for 3D points is necessary\nas this is the interface the package is expecting. These function should return `Float64`.\nPoints can be either immutables or types. Default `Point2D` and `Point3D` are immutables.\n\nThe point coordinates must reside in a region `1.0 \u003c= x \u003c 2.0`. Read above on\nwhy this limitation is necessary. For convenience there are 2 constants defined,\n`min_coord` and `max coord` representing the minimal and maximal feasible values\nof coordinates.\n\n### Triangles and Tetrahedrons (..aka Primitives)\nA triangle is the 2D primitive, and a tetrahedron is the 3D primitive.\n```julia\n# create a triangle using 3 points\na = Point(1.1, 1.1)\nb = Point(1.9, 1.1)\nc = Point(1.1, 1.9)\nmytriangle = Primitive(a, b, c)\ntypeof(mytriangle) # -\u003e UnOrientedTriangle{Point2D}\n```\nThe triangle is unoriented in the sense that orientation is not-known in advance,\nit is not immutable and it could change if points in the triangle are updated.\nThe orientation needs to be calculated when the triangle is created and when\npoints within are updated. Read below for the definition of orientation. The\ntriangle could be created using any points inheriting from `AbstractPoint2D`\nwhich implement `getx` and `gety`, or using coordinates directly:\n```julia\nmytriangle = Primitive(1.1, 1.1, 1.9, 1.1, 1.1, 1.9)\n\n# Getting point `a` in the triangle\ngeta(mytriangle) # -\u003e Point2D(1.1, 1.1)\ngetb(mytriangle) # -\u003e Point2D(1.9, 1.1)\ngetc(mytriangle) # -\u003e Point2D(1.1, 1.9)\n```\nThe same goes for tetrahedrons, except we now use 4 3D points instead of 3 2D ones:\n```julia\n# create a tetrahedron using 4 points\na = Point(1.1, 1.1, 1.1)\nb = Point(1.9, 1.1, 1.1)\nc = Point(1.1, 1.9, 1.1)\nd = Point(1.1, 1.1, 1.9)\nmytetraedron = Primitive(a, b, c, d)\ntypeof(mytetrahedron) # -\u003e UnOrientedTetrahedron{Point3D}\n```\nFor certain applications we use primitives with known orientation. In those cases\nthere should be no need to calculate it. This is achieved by passing an `orientation`\nflag to the `Primitive` creation function:\n```julia\nmytetrahedron = Primitive(a, b, c, d, positivelyoriented)\ntypeof(mytetrahedron) # -\u003e PositivelyOrientedTetrahedron{Point3D}\norientation(mytetrahedron) # -\u003e constant 1, not calculated\nmytetrahedron = Primitive(a, b, c, d, negativelyoriented)\ntypeof(mytetrahedron) # -\u003e NegativelyOrientedTetrahedron{Point3D}\norientation(mytetrahedron) # -\u003e constant -1, not calculated\n```\nNote that when the primitive is oriented the real orientation is never calculated.\nIt is assumed that the user knows what he's doing. If in doubt, just use unoriented\nprimitives at the cost of actual calculation.\n\nUpdating points in primitives can be done with `seta`, `setb`, etc. methods:\n```julia\nseta(mytriangle, Point(1.7, 1.7))\n```\nUpdating a point in a primitive will fire all relevant pre-calculations. i.e. if the triangle\nis unoriented then orientation will be calculated. If it is oriented then still a few other\npre-calculations will be done, but a few less. If there is need to update a number of points\nit is thus more efficient to do so in a group update:\n```julia\nsetab(mytriangle, Point(1.7, 1.7), Point(1.3, 1.1))\nsetbcd(mytetrahedron, Point(1.1, 1.1, 1.2), Point(1.2,1.1,1.3), Point(1.4,1.1,1.2))\n```\ncombinations for all points exist. The name always contains the point names\nin alphabetical order. As long as inner primitive data is not changed manually, it will\nkeep giving correct results for all functions in this package.\n\n### Predicates\n`incircle` is the popular name to test whether a point lies inside of the sphere\ndefined by the primitive points:\n```julia\na = Point(1.1, 1.1)\nb = Point(1.5, 1.1)\nc = Point(1.1, 1.5)\nmytriangle = Primitive(a, b, c)\nincircle(mytriangle, Point(1.9, 1.9)) # -\u003e -1, i.e. outside\nincircle(mytriangle, Point(1.2, 1.2)) # -\u003e +1, i.e. inside\nincircle(mytriangle, Point(1.5, 1.5)) # -\u003e  0, i.e. point is exactly on circle\n```\nThere is one more possibility. If the circle defined by our primitive has infinite radius\nthen it is impossible to tell whether the point is inside or outside:\n```julia\na = Point(1.1, 1.1)\nb = Point(1.2, 1.2)\nc = Point(1.3, 1.3)\nmytriangle = Primitive(a, b, c)\nincircle(mytriangle, Point(1.3, 1.4)) # -\u003e +2, i.e. cannot tell\n```\n\n`intriangle` is a popular name to test whether a point lies inside of the primitive:\n```julia\na = Point(1.1, 1.1)\nb = Point(1.5, 1.1)\nc = Point(1.1, 1.5)\nmytriangle = Primitive(a, b, c)\nintriangle(mytriangle, Point(1.2, 1.2)) # -\u003e +1, i.e. inside\nintriangle(mytriangle, Point(1.6, 1.6)) # -\u003e -1, i.e. outside\nintriangle(mytriangle, Point(1.3, 1.1)) # -\u003e  4, i.e. exactly on ab\nintriangle(mytriangle, Point(1.1, 1.3)) # -\u003e  3, i.e. exactly on ac\nintriangle(mytriangle, Point(1.3, 1.3)) # -\u003e  2, i.e. exactly on bc\n\n```\nHere any negative number means outside. The exact value gives some information regarding\nthe direction in which the point lies outside:\n* `-1` means the test point is in front of a, and outside of the triangle\n* `-2` means the test point is in front of b, and outside of the triangle\n* `-3` means the test point is in front of c, and outside of the triangle\nsame goes for tetrahedrons. Note that the point could be both in front of a and b. In\ncases as this one is arbitrarily chosen, all in name of performance.\n\n`1` means test point is inside. But there are other possible positive values:\n* `1 + 1 = 2` means the test point is in front of a, exactly on the triangle\n* `1 + 2 = 3` means the test point is in front of b, exactly on the triangle\n* `1 + 3 = 4` means the test point is in front of c, exactly on the triangle\n\nsame extends for tetrahedrons.\n\n### Lines and Polygons\n\n`orientation` tests for the primitive orientation. In 2D this means:\n* ` 1` --\u003e point `c` is to the left of the line defined by `ab` (with directionality from `a` to `b`)\n* `-1` --\u003e point `c` is to the right\n* ` 0` --\u003e point `c` is exactly on the line\n\nin 3D it means:\n* ` 1` --\u003e point `d` is above the plane defined by `abc` (note \"above\" here means the direction of the plane normal, which depends on its orientation)\n* `-1` --\u003e point `d` is below the plane\n* ` 0` --\u003e point `c` is exactly on the plane\n\nAnother convenience API to test for orientation in 2D is using a line. It has some performance advantages over creating a triangle:\n```julia\na = Point(1.1, 1.1)\nb = Point(1.5, 1.5)\n\nl = Line(a, b)\nprintln(orientation(l, Point(1.4, 1.6))) # --\u003e  1\nprintln(orientation(l, Point(1.4,1.05))) # --\u003e -1\nprintln(orientation(l, Point(1.4,1.40))) # --\u003e  0\n```\n\nOne can also create simple 2D polygons:\n```julia\nll = Point(1.0,1.0)\nlr = Point(1.2,1.0)\nur = Point(1.2,1.2)\nul = Point(1.0,1.2)\npoly = Polygon(ll, lr, ur, ul)\ninpolygon(poly, Point(1.1,1.1))  # assumes it is convex\n```\n\n\n### Basic geometrical properties\n`orientation` gives the primitive orientation. `area`, `volume`, `centroid`, `circumcenter`, `circumradius2` are all exported and I hope self descriptive.\n\n### Spatial ordering\nScale and scale-free Peano-Hilbert ordering is available. Look [here](http://doc.cgal.org/latest/Spatial_sorting/index.html) for a nice explanation on Hilbert sorting and [here](http://doc.cgal.org/latest/Spatial_sorting/classCGAL_1_1Multiscale__sort.html) for a nice explanation of multiscale sort. Both are implemented here:\n\n```julia\np = Point(1.1, 1.2)\npeanokey(p, 4) # -\u003e 6, the peano key in a regular grid of 2^4 X 2^4 cells\n\np = Point(1.1, 1.2, 1.3)\npeanokey(p, 4) # -\u003e 94, the peano key in a regular grid of 2^4 X 2^4 X 2^4 cells\n```\n\nThe number of cells doesn't need to be specified. The default for 2D is `2^31 X 2^31` and for 3D `2^21 X 2^21 X 2^21`.\nYou can also do the inverse, and get a point from a key:\n```julia\nPoint2D(6, 4) # -\u003e Point2D(1.0625,1.1875)\n```\nin a finer grid we would get back something more accurate.\n\nSo that was scale-dependent Hilbert stuff, which is good to say balance workload between computing nodes.\nWhen you need to order particles spatially it is better to use a scale independent method, like the Hilbert ordering here:\n\n```julia\na = [Point(1.0+rand(), 1.0+rand() for i in 1:1e6]\nhilbertsort!(a)\n```\n\nHere keys are never calculated, and there is no grid, it uses a median policy, adjusting to the actual\npoint distribution. There are a few parameters with good defaults, see links above to understand what they mean.\nFor an algorithm such a Delaunay tesselation it is sometimes better to use a multi-scale sort with a Hilbert sort, like this:\n```julia\nmssort!(a)\n```\nof course this adds a few more default parameters, again with decent defaults, read above links to understand.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliageometry%2Fgeometricalpredicates.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjuliageometry%2Fgeometricalpredicates.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjuliageometry%2Fgeometricalpredicates.jl/lists"}