git: 62fd95493e22 - main - math/py-isosurfaces: New port: Construct isolines/isosurfaces over a 2D/3D scalar field
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 20 Jan 2023 18:43:35 UTC
The branch main has been updated by yuri: URL: https://cgit.FreeBSD.org/ports/commit/?id=62fd95493e22bd6cb7dc9884e92a479cd76ea8d6 commit 62fd95493e22bd6cb7dc9884e92a479cd76ea8d6 Author: Yuri Victorovich <yuri@FreeBSD.org> AuthorDate: 2023-01-20 16:09:03 +0000 Commit: Yuri Victorovich <yuri@FreeBSD.org> CommitDate: 2023-01-20 18:43:32 +0000 math/py-isosurfaces: New port: Construct isolines/isosurfaces over a 2D/3D scalar field --- math/Makefile | 1 + math/py-isosurfaces/Makefile | 30 +++++++ math/py-isosurfaces/distinfo | 3 + math/py-isosurfaces/files/isoline_demo.py | 133 ++++++++++++++++++++++++++++++ math/py-isosurfaces/pkg-descr | 5 ++ 5 files changed, 172 insertions(+) diff --git a/math/Makefile b/math/Makefile index 6e6adc82c966..902b5e115691 100644 --- a/math/Makefile +++ b/math/Makefile @@ -925,6 +925,7 @@ SUBDIR += py-intspan SUBDIR += py-iohexperimenter SUBDIR += py-ipyopt + SUBDIR += py-isosurfaces SUBDIR += py-jax SUBDIR += py-kahip SUBDIR += py-keras diff --git a/math/py-isosurfaces/Makefile b/math/py-isosurfaces/Makefile new file mode 100644 index 000000000000..63ef30cdb9c4 --- /dev/null +++ b/math/py-isosurfaces/Makefile @@ -0,0 +1,30 @@ +PORTNAME= isosurfaces +DISTVERSION= 0.1.0 +CATEGORIES= math +MASTER_SITES= PYPI +PKGNAMEPREFIX= ${PYTHON_PKGNAMEPREFIX} + +MAINTAINER= yuri@FreeBSD.org +COMMENT= Construct isolines/isosurfaces over a 2D/3D scalar field +WWW= https://github.com/jared-hughes/isosurfaces + +LICENSE= MIT + +RUN_DEPENDS= ${PYNUMPY} +TEST_DEPENDS= ${PYTHON_PKGNAMEPREFIX}cairo>0:graphics/py-cairo@${PY_FLAVOR} \ + ${PYNUMPY} \ + xdg-open:devel/xdg-utils + +USES= python +USE_PYTHON= distutils autoplist + +NO_ARCH= yes + +TEST_ENV= ${MAKE_ENV} PYTHONPATH=${STAGEDIR}${PYTHONPREFIX_SITELIBDIR} + +do-test: + @cd ${TEST_WRKSRC} && \ + ${SETENV} ${TEST_ENV} ${PYTHON_CMD} ${FILESDIR}/isoline_demo.py && \ + xdg-open ${TEST_WRKSRC}/demo.svg + +.include <bsd.port.mk> diff --git a/math/py-isosurfaces/distinfo b/math/py-isosurfaces/distinfo new file mode 100644 index 000000000000..f4588b9ff108 --- /dev/null +++ b/math/py-isosurfaces/distinfo @@ -0,0 +1,3 @@ +TIMESTAMP = 1674236137 +SHA256 (isosurfaces-0.1.0.tar.gz) = fa1b44e5e59d2f429add49289ab89e36f8dcda49b7badd99e0beea273be331f4 +SIZE (isosurfaces-0.1.0.tar.gz) = 10122 diff --git a/math/py-isosurfaces/files/isoline_demo.py b/math/py-isosurfaces/files/isoline_demo.py new file mode 100644 index 000000000000..53bd1a51da61 --- /dev/null +++ b/math/py-isosurfaces/files/isoline_demo.py @@ -0,0 +1,133 @@ +# from examples/isoline_demo.py + +""" Code for demo-ing and experimentation. Prepare for a mess """ +from isosurfaces import plot_isoline +from isosurfaces.isoline import ( + Cell, + build_tree, + Triangulator, + CurveTracer, +) +import numpy as np +import cairo + +min_depth = 5 +pmin = np.array([-8, -6]) +pmax = np.array([8, 6]) + + +def f(x, y): + return y * (x - y) ** 2 - 4 * x - 8 + + +# Here we directly use plot_implicit internals in order to see the quadtree +fn = lambda u: f(u[0], u[1]) +tol = (pmax - pmin) / 1000 +quadtree = build_tree(2, fn, pmin, pmax, min_depth, 5000, tol) +triangles = Triangulator(quadtree, fn).triangulate() +curves = CurveTracer(triangles, fn, tol).trace() + + +def g(x, y): + return x ** 3 - x - y ** 2 + + +# Typical usage +curves1 = plot_isoline( + lambda u: g(u[0], u[1]), + pmin, + pmax, + min_depth=4, + max_quads=1000, +) + + +def h(x, y): + return x ** 4 + y ** 4 - np.sin(x) - np.sin(4 * y) + + +curves2 = plot_isoline(lambda u: h(u[0], u[1]), pmin, pmax, 4, 1000) + + +WIDTH = 640 +HEIGHT = 480 + + +def setup_context(c): + # reflection to change math units to screen units + scale = min(WIDTH / (pmax[0] - pmin[0]), HEIGHT / (pmax[1] - pmin[1])) + c.scale(scale, -scale) + c.translate(WIDTH / scale / 2, -HEIGHT / scale / 2) + c.set_line_join(cairo.LINE_JOIN_BEVEL) + + +def draw_axes(c): + c.save() + c.set_line_width(0.1) + c.move_to(0, -100) + c.line_to(0, 100) + c.stroke() + c.move_to(-100, 0) + c.line_to(100, 0) + c.stroke() + c.restore() + + +def draw_quad(c, quad: Cell): + width = 0 + if quad.depth <= min_depth: + width = 0.02 + elif quad.depth == min_depth + 1: + width = 0.01 + else: + width = 0.005 + c.set_line_width(0.5 * width) + + if quad.children: + c.move_to(*((quad.vertices[0].pos + quad.vertices[1].pos) / 2)) + c.line_to(*((quad.vertices[2].pos + quad.vertices[3].pos) / 2)) + c.move_to(*((quad.vertices[0].pos + quad.vertices[2].pos) / 2)) + c.line_to(*((quad.vertices[1].pos + quad.vertices[3].pos) / 2)) + c.stroke() + for child in quad.children: + draw_quad(c, child) + + +def draw_quads(c): + c.save() + draw_quad(c, quadtree) + c.restore() + + +def draw_bg(c): + c.save() + c.set_source_rgb(1, 1, 1) + c.paint() + c.restore() + + +def draw_curves(c, curves_list, rgb): + print( + "drawing", sum(map(len, curves_list)), "segments in", len(curves_list), "curves" + ) + c.set_source_rgb(*rgb) + # draw curves + c.save() + c.set_line_width(0.03) + for curve in curves_list: + c.move_to(*curve[0]) + for v in curve: + c.line_to(*v) + c.stroke() + c.restore() + + +with cairo.SVGSurface("demo.svg", WIDTH, HEIGHT) as surface: + c = cairo.Context(surface) + setup_context(c) + draw_bg(c) + draw_axes(c) + # draw_quads(c) + draw_curves(c, curves, [0.1, 0.1, 0.8]) + draw_curves(c, curves1, [0.8, 0.1, 0.1]) + draw_curves(c, curves2, [0.1, 0.6, 0.1]) diff --git a/math/py-isosurfaces/pkg-descr b/math/py-isosurfaces/pkg-descr new file mode 100644 index 000000000000..d1ca20c3de04 --- /dev/null +++ b/math/py-isosurfaces/pkg-descr @@ -0,0 +1,5 @@ +isosurfaces allows to construct isolines/isosurfaces of a 2D/3D scalar field +defined by a function, i.e. curves over which f(x,y)=0 or surfaces over which +f(x,y,z)=0. Most similar libraries use marching squares or similar over a +uniform grid, but this uses a quadtree to avoid wasting time sampling many far +from the implicit surface.