Compare commits
	
		
			30 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6e66cba64c | |||
| afe4b74eda | |||
| 7766a7c43c | |||
| 379abb5e82 | |||
| 1799faeddd | |||
| 9749cecef8 | |||
| a12ef190fa | |||
| 89d0611cfc | |||
| 6e32eda1c7 | |||
| 0a81c97af1 | |||
| 56989009e0 | |||
| 6ee12d8db9 | |||
| 38dc51aa7e | |||
| 81e34520aa | |||
| 6d861d8a9c | |||
| f8d3a6600e | |||
| e5e30a9414 | |||
| 522b610209 | |||
| ecefdff781 | |||
| 085bb79ed7 | |||
| 94ebf9fa18 | |||
| 99b35a1561 | |||
| 50e3822eac | |||
| f68ab6bedb | |||
| 1ab4b17247 | |||
| 76f6c68472 | |||
| c10fd556e2 | |||
| 219e4b9926 | |||
| 756c015b87 | |||
| 3ca8afb3ef | 
							
								
								
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +1,10 @@ | ||||
| *.pyc | ||||
| __pycache__ | ||||
| 
 | ||||
| *.idea | ||||
| 
 | ||||
| build/ | ||||
| dist/ | ||||
| *.egg-info/ | ||||
| 
 | ||||
| .mypy_cache | ||||
|  | ||||
							
								
								
									
										15
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								README.md
									
									
									
									
									
								
							| @ -6,13 +6,22 @@ float_raster calculates pixel values with float64 precision and is capable of dr | ||||
| with variable pixel widths and heights. | ||||
| 
 | ||||
| 
 | ||||
| - [Source repository](https://mpxd.net/code/jan/float_raster) | ||||
| - [PyPi](https://pypi.org/project/float_raster) | ||||
| 
 | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| Requirements: | ||||
| * python 3 (written and tested with 3.5) | ||||
| * python >=3.11 | ||||
| * numpy | ||||
| 
 | ||||
| Install with pip, via git: | ||||
| Install with pip: | ||||
| ```bash | ||||
| pip install git+https://mpxd.net/gogs/jan/float_raster.git@release | ||||
| pip3 install float_raster | ||||
| ``` | ||||
| 
 | ||||
| Alternatively, install via git | ||||
| ```bash | ||||
| pip3 install git+https://mpxd.net/code/jan/float_raster.git@release | ||||
| ``` | ||||
|  | ||||
							
								
								
									
										1
									
								
								float_raster/LICENSE.md
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								float_raster/LICENSE.md
									
									
									
									
									
										Symbolic link
									
								
							| @ -0,0 +1 @@ | ||||
| ../LICENSE.md | ||||
							
								
								
									
										1
									
								
								float_raster/README.md
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								float_raster/README.md
									
									
									
									
									
										Symbolic link
									
								
							| @ -0,0 +1 @@ | ||||
| ../README.md | ||||
							
								
								
									
										18
									
								
								float_raster/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								float_raster/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | ||||
| """ | ||||
| Module for rasterizing polygons, with float-precision anti-aliasing on | ||||
|  a non-uniform rectangular grid. | ||||
| 
 | ||||
| See the documentation for float_raster.raster(...) for details. | ||||
| """ | ||||
| 
 | ||||
| from .float_raster import ( | ||||
|     raster as raster, | ||||
|     find_intersections as find_intersections, | ||||
|     create_vertices as create_vertices, | ||||
|     clip_vertices_to_window as clip_vertices_to_window, | ||||
|     get_raster_parts as get_raster_parts, | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| __author__ = 'Jan Petykiewicz' | ||||
| __version__ = '0.8' | ||||
| @ -1,22 +1,19 @@ | ||||
| """ | ||||
| Module for rasterizing polygons, with float-precision anti-aliasing on | ||||
|  a non-uniform rectangular grid. | ||||
| 
 | ||||
| See the documentation for raster(...) for details. | ||||
| """ | ||||
| 
 | ||||
| from typing import Tuple | ||||
| from numpy.typing import ArrayLike, NDArray | ||||
| import numpy | ||||
| from numpy import logical_and, diff, floor, ceil, ones, zeros, hstack, full_like, newaxis | ||||
| from scipy import sparse | ||||
| 
 | ||||
| __author__ = 'Jan Petykiewicz' | ||||
| 
 | ||||
| class FloatRasterError(Exception): | ||||
|     """ Custom exception for float_raster """ | ||||
|     pass | ||||
| 
 | ||||
| 
 | ||||
| def raster(vertices: numpy.ndarray, | ||||
|            grid_x: numpy.ndarray, | ||||
|            grid_y: numpy.ndarray | ||||
|            ) -> numpy.ndarray: | ||||
| def raster( | ||||
|         vertices: ArrayLike, | ||||
|         grid_x: ArrayLike, | ||||
|         grid_y: ArrayLike, | ||||
|         ) -> NDArray[numpy.float64]: | ||||
|     """ | ||||
|     Draws a polygon onto a 2D grid of pixels, setting pixel values equal to the fraction of the | ||||
|      pixel area covered by the polygon. This implementation is written for accuracy and works with | ||||
| @ -27,11 +24,14 @@ def raster(vertices: numpy.ndarray, | ||||
|     Polygons are assumed to have clockwise vertex order; reversing the vertex order is equivalent | ||||
|      to multiplying the result by -1. | ||||
| 
 | ||||
|     :param vertices: 2xN ndarray containing x,y coordinates for each vertex of the polygon | ||||
|     :param grid_x: x-coordinates for the edges of each pixel (ie, the leftmost two columns span | ||||
|         x=grid_x[0] to x=grid_x[1] and x=grid_x[1] to x=grid_x[2]) | ||||
|     :param grid_y: y-coordinates for the edges of each pixel (see grid_x) | ||||
|     :return: 2D ndarray with pixel values in the range [0, 1] containing the anti-aliased polygon | ||||
|     Args: | ||||
|         vertices: 2xN ndarray containing `x,y` coordinates for each vertex of the polygon | ||||
|         grid_x: x-coordinates for the edges of each pixel (ie, the leftmost two columns span | ||||
|                 `x=grid_x[0]` to `x=grid_x[1]` and `x=grid_x[1]` to `x=grid_x[2]`) | ||||
|         grid_y: y-coordinates for the edges of each pixel (see `grid_x`) | ||||
| 
 | ||||
|     Returns: | ||||
|         2D ndarray with pixel values in the range [0, 1] containing the anti-aliased polygon | ||||
|     """ | ||||
|     vertices = numpy.array(vertices) | ||||
|     grid_x = numpy.array(grid_x) | ||||
| @ -55,15 +55,15 @@ def raster(vertices: numpy.ndarray, | ||||
| 
 | ||||
| 
 | ||||
| def find_intersections( | ||||
|         vertices: numpy.ndarray, | ||||
|         grid_x: numpy.ndarray, | ||||
|         grid_y: numpy.ndarray | ||||
|         ) -> Tuple[numpy.ndarray]: | ||||
|         vertices: NDArray[numpy.floating], | ||||
|         grid_x: NDArray[numpy.floating], | ||||
|         grid_y: NDArray[numpy.floating], | ||||
|         ) -> tuple[NDArray[numpy.float64], NDArray[numpy.float64], NDArray[numpy.float64]]: | ||||
|     """ | ||||
|     Find intersections between a polygon and grid lines | ||||
|     """ | ||||
|     if vertices.shape[0] != 2: | ||||
|         raise Exception('vertices must be 2xN') | ||||
|         raise FloatRasterError('vertices must be 2xN') | ||||
| 
 | ||||
|     min_bounds = floor(vertices.min(axis=1)) | ||||
|     max_bounds = ceil(vertices.max(axis=1)) | ||||
| @ -132,18 +132,18 @@ def find_intersections( | ||||
| 
 | ||||
| 
 | ||||
| def create_vertices( | ||||
|         vertices: numpy.ndarray, | ||||
|         grid_x: numpy.ndarray, | ||||
|         grid_y: numpy.ndarray, | ||||
|         new_vertex_data: Tuple[numpy.ndarray] = None | ||||
|         vertices: NDArray[numpy.floating], | ||||
|         grid_x: NDArray[numpy.floating], | ||||
|         grid_y: NDArray[numpy.floating], | ||||
|         new_vertex_data: tuple[NDArray[numpy.float64], NDArray[numpy.float64], NDArray[numpy.float64]] | None = None | ||||
|         ) -> sparse.coo_matrix: | ||||
|     """ | ||||
|     Create additional vertices where a polygon crosses gridlines | ||||
|     """ | ||||
|     if vertices.shape[0] != 2: | ||||
|         raise Exception('vertices must be 2xN') | ||||
|         raise FloatRasterError('vertices must be 2xN') | ||||
|     if grid_x.size < 1 or grid_y.size < 1: | ||||
|         raise Exception('Grid must contain at least one line in each direction?') | ||||
|         raise FloatRasterError('Grid must contain at least one line in each direction?') | ||||
| 
 | ||||
|     num_poly_vertices = vertices.shape[1] | ||||
| 
 | ||||
| @ -180,13 +180,14 @@ def create_vertices( | ||||
| 
 | ||||
|     return vertices | ||||
| 
 | ||||
| 
 | ||||
| def clip_vertices_to_window( | ||||
|         vertices: numpy.ndarray, | ||||
|         vertices: NDArray[numpy.float64], | ||||
|         min_x: float = -numpy.inf, | ||||
|         max_x: float = numpy.inf, | ||||
|         min_y: float = -numpy.inf, | ||||
|         max_y: float = numpy.inf | ||||
|         ) -> numpy.ndarray: | ||||
|         ) -> NDArray[numpy.float64]: | ||||
|     """ | ||||
|     """ | ||||
|     # Remove points outside the window (these will only be original points) | ||||
| @ -205,40 +206,49 @@ def clip_vertices_to_window( | ||||
| 
 | ||||
| 
 | ||||
| def get_raster_parts( | ||||
|         vertices: numpy.ndarray, | ||||
|         grid_x: numpy.ndarray, | ||||
|         grid_y: numpy.ndarray | ||||
|         vertices: ArrayLike, | ||||
|         grid_x: ArrayLike, | ||||
|         grid_y: ArrayLike, | ||||
|         ) -> sparse.coo_matrix: | ||||
|     """ | ||||
|     This function performs the same task as raster(...), but instead of returning a dense array | ||||
|     This function performs the same task as `raster(...)`, but instead of returning a dense array | ||||
|     of pixel values, it returns a sparse array containing the value | ||||
|         (-area + 1j * cover) | ||||
|         `(-area + 1j * cover)` | ||||
|     for each pixel which contains a line segment, where | ||||
|         cover   is the fraction of the pixel's y-length that is traversed by the segment, | ||||
|                  multiplied by the sign of (y_final - y_initial) | ||||
|         area    is the fraction of the pixel's area covered by the trapezoid formed by | ||||
|                  the line segment's endpoints (clipped to the cell edges) and their projections | ||||
|                  onto the pixel's left (i.e., lowest-x) edge, again multiplied by | ||||
|                  the sign of (y_final - y_initial) | ||||
|         `cover`   is the fraction of the pixel's y-length that is traversed by the segment, | ||||
|                    multiplied by the sign of `(y_final - y_initial)` | ||||
|         `area`    is the fraction of the pixel's area covered by the trapezoid formed by | ||||
|                    the line segment's endpoints (clipped to the cell edges) and their projections | ||||
|                    onto the pixel's left (i.e., lowest-x) edge, again multiplied by | ||||
|                   the sign of `(y_final - y_initial)` | ||||
|         Note that polygons are assumed to be wound clockwise. | ||||
| 
 | ||||
|     The result from raster(...) can be obtained with | ||||
|         raster_result = numpy.real(lines_result) + numpy.imag(lines_result).cumsum(axis=0) | ||||
|     The result from `raster(...)` can be obtained with | ||||
|         `raster_result = numpy.real(lines_result) + numpy.imag(lines_result).cumsum(axis=0)` | ||||
| 
 | ||||
|     :param vertices: 2xN ndarray containing x,y coordinates for each point in the polygon | ||||
|     :param grid_x: x-coordinates for the edges of each pixel (ie, the leftmost two columns span | ||||
|         x=grid_x[0] to x=grid_x[1] and x=grid_x[1] to x=grid_x[2]) | ||||
|     :param grid_y: y-coordinates for the edges of each pixel (see grid_x) | ||||
|     :return: Complex sparse COO matrix containing area and cover information | ||||
|     Args: | ||||
|         vertices: 2xN ndarray containing `x, y` coordinates for each point in the polygon | ||||
|         grid_x: x-coordinates for the edges of each pixel (ie, the leftmost two columns span | ||||
|                 `x=grid_x[0]` to `x=grid_x[1]` and `x=grid_x[1]` to `x=grid_x[2]`) | ||||
|         grid_y: y-coordinates for the edges of each pixel (see `grid_x`) | ||||
| 
 | ||||
|     Returns: | ||||
|         Complex sparse COO matrix containing area and cover information | ||||
|     """ | ||||
|     vertices = numpy.array(vertices) | ||||
|     grid_x = numpy.array(grid_x) | ||||
|     grid_y = numpy.array(grid_y) | ||||
| 
 | ||||
|     if grid_x.size < 2 or grid_y.size < 2: | ||||
|         raise Exception('Grid must contain at least one full pixel') | ||||
|         raise FloatRasterError('Grid must contain at least one full pixel') | ||||
| 
 | ||||
|     num_xy_px = numpy.array([grid_x.size, grid_y.size]) - 1 | ||||
| 
 | ||||
|     vertices = clip_vertices_to_window(vertices, | ||||
|                                        grid_x[0], grid_x[-1], | ||||
|                                        grid_y[0], grid_y[-1]) | ||||
|     vertices = clip_vertices_to_window( | ||||
|         vertices, | ||||
|         grid_x[0], grid_x[-1], | ||||
|         grid_y[0], grid_y[-1], | ||||
|         ) | ||||
| 
 | ||||
|     # If the shape fell completely outside our area, just return a blank grid | ||||
|     if vertices.size == 0: | ||||
							
								
								
									
										0
									
								
								float_raster/py.typed
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								float_raster/py.typed
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										82
									
								
								pyproject.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								pyproject.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | ||||
| [build-system] | ||||
| requires = ["hatchling"] | ||||
| build-backend = "hatchling.build" | ||||
| 
 | ||||
| [project] | ||||
| name = "float_raster" | ||||
| description = "High-precision anti-aliasing polygon rasterizer" | ||||
| readme = "README.md" | ||||
| license = { file = "LICENSE.md" } | ||||
| authors = [ | ||||
|     { name="Jan Petykiewicz", email="jan@mpxd.net" }, | ||||
|     ] | ||||
| homepage = "https://mpxd.net/code/jan/float_raster" | ||||
| repository = "https://mpxd.net/code/jan/float_raster" | ||||
| keywords = [ | ||||
|     "coverage", | ||||
|     "raster", | ||||
|     "anti-alias", | ||||
|     "polygon", | ||||
|     ] | ||||
| classifiers = [ | ||||
|     "Programming Language :: Python :: 3", | ||||
|     "Development Status :: 4 - Beta", | ||||
|     "Intended Audience :: Developers", | ||||
|     "Intended Audience :: Information Technology", | ||||
|     "Intended Audience :: Manufacturing", | ||||
|     "Intended Audience :: Science/Research", | ||||
|     "License :: OSI Approved :: GNU Affero General Public License v3", | ||||
|     "Topic :: Scientific/Engineering", | ||||
|     "Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)", | ||||
|     "Topic :: Multimedia :: Graphics :: Graphics Conversion", | ||||
|     ] | ||||
| requires-python = ">=3.11" | ||||
| dynamic = ["version"] | ||||
| dependencies = [ | ||||
|     "numpy>=1.26", | ||||
|     "scipy~=1.14", | ||||
|     ] | ||||
| 
 | ||||
| [tool.hatch.version] | ||||
| path = "float_raster/__init__.py" | ||||
| 
 | ||||
| 
 | ||||
| [tool.ruff] | ||||
| exclude = [ | ||||
|     ".git", | ||||
|     "dist", | ||||
|     ] | ||||
| line-length = 145 | ||||
| indent-width = 4 | ||||
| lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" | ||||
| lint.select = [ | ||||
|     "NPY", "E", "F", "W", "B", "ANN", "UP", "SLOT", "SIM", "LOG", | ||||
|     "C4", "ISC", "PIE", "PT", "RET", "TCH", "PTH", "INT", | ||||
|     "ARG", "PL", "R", "TRY", | ||||
|     "G010", "G101", "G201", "G202", | ||||
|     "Q002", "Q003", "Q004", | ||||
|     ] | ||||
| lint.ignore = [ | ||||
|     #"ANN001",   # No annotation | ||||
|     "ANN002",   # *args | ||||
|     "ANN003",   # **kwargs | ||||
|     "ANN401",   # Any | ||||
|     "ANN101",   # self: Self | ||||
|     "SIM108",   # single-line if / else assignment | ||||
|     "RET504",   # x=y+z; return x | ||||
|     "PIE790",   # unnecessary pass | ||||
|     "ISC003",   # non-implicit string concatenation | ||||
|     "C408",     # dict(x=y) instead of {'x': y} | ||||
|     "PLR09",    # Too many xxx | ||||
|     "PLR2004",  # magic number | ||||
|     "PLC0414",  # import x as x | ||||
|     "TRY003",   # Long exception message | ||||
|     ] | ||||
| 
 | ||||
| 
 | ||||
| [[tool.mypy.overrides]] | ||||
| module = [ | ||||
|     "scipy", | ||||
|     "scipy.sparse", | ||||
|     ] | ||||
| ignore_missing_imports = true | ||||
							
								
								
									
										16
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								setup.py
									
									
									
									
									
								
							| @ -1,16 +0,0 @@ | ||||
| #!/usr/bin/env python | ||||
| 
 | ||||
| from setuptools import setup | ||||
| 
 | ||||
| setup(name='float_raster', | ||||
|       version='0.4', | ||||
|       description='High-precision anti-aliasing polygon rasterizer', | ||||
|       author='Jan Petykiewicz', | ||||
|       author_email='anewusername@gmail.com', | ||||
|       url='https://mpxd.net/gogs/jan/float_raster', | ||||
|       py_modules=['float_raster'], | ||||
|       install_requires=[ | ||||
|             'numpy', | ||||
|             'scipy', | ||||
|       ], | ||||
|       ) | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user