This patch adds some new functionality to the Freestyle Python API, notably: - MaterialBP1D, checks whether the supplied arguments have the same material - Fixes a potential crash in CurvePoint.fedge (due to NULL pointer) - Makes (error handling in) boolean predicates more robust - Adds a BoundingBox type, to make working with bounding boxes easier - Adds several new functions (get_object_name, get_strokes, is_poly_clockwise, material_from_fedge) - Adds a StrokeCollector StrokeShader, that collects all the strokes from a specific call to Operators.create() - Adds hashing and rich comparison to the FrsMaterial type These new features (most of them, anyway) are needed for making a more robust SVG exporter that supports holes in fills. Reviewers: kjym3, campbellbarton Subscribers: campbellbarton Projects: #bf_blender Differential Revision: https://developer.blender.org/D1245
319 lines
9.8 KiB
Python
319 lines
9.8 KiB
Python
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software Foundation,
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
"""
|
|
This module contains functions operating on vertices (0D elements) and
|
|
polylines (1D elements). The module is also intended to be a
|
|
collection of examples for function definition in Python.
|
|
|
|
User-defined functions inherit one of the following base classes,
|
|
depending on the object type (0D or 1D) to operate on and the return
|
|
value type:
|
|
|
|
- :class:`freestyle.types.UnaryFunction0DDouble`
|
|
- :class:`freestyle.types.UnaryFunction0DEdgeNature`
|
|
- :class:`freestyle.types.UnaryFunction0DFloat`
|
|
- :class:`freestyle.types.UnaryFunction0DId`
|
|
- :class:`freestyle.types.UnaryFunction0DMaterial`
|
|
- :class:`freestyle.types.UnaryFunction0DUnsigned`
|
|
- :class:`freestyle.types.UnaryFunction0DVec2f`
|
|
- :class:`freestyle.types.UnaryFunction0DVec3f`
|
|
- :class:`freestyle.types.UnaryFunction0DVectorViewShape`
|
|
- :class:`freestyle.types.UnaryFunction0DViewShape`
|
|
- :class:`freestyle.types.UnaryFunction1DDouble`
|
|
- :class:`freestyle.types.UnaryFunction1DEdgeNature`
|
|
- :class:`freestyle.types.UnaryFunction1DFloat`
|
|
- :class:`freestyle.types.UnaryFunction1DUnsigned`
|
|
- :class:`freestyle.types.UnaryFunction1DVec2f`
|
|
- :class:`freestyle.types.UnaryFunction1DVec3f`
|
|
- :class:`freestyle.types.UnaryFunction1DVectorViewShape`
|
|
- :class:`freestyle.types.UnaryFunction1DVoid`
|
|
"""
|
|
|
|
__all__ = (
|
|
"ChainingTimeStampF1D",
|
|
"Curvature2DAngleF0D",
|
|
"Curvature2DAngleF1D",
|
|
"CurveMaterialF0D",
|
|
"CurveNatureF0D",
|
|
"CurveNatureF1D",
|
|
"DensityF0D",
|
|
"DensityF1D",
|
|
"GetCompleteViewMapDensityF1D",
|
|
"GetCurvilinearAbscissaF0D",
|
|
"GetDirectionalViewMapDensityF1D",
|
|
"GetOccludeeF0D",
|
|
"GetOccludeeF1D",
|
|
"GetOccludersF0D",
|
|
"GetOccludersF1D",
|
|
"GetParameterF0D",
|
|
"GetProjectedXF0D",
|
|
"GetProjectedXF1D",
|
|
"GetProjectedYF0D",
|
|
"GetProjectedYF1D",
|
|
"GetProjectedZF0D",
|
|
"GetProjectedZF1D",
|
|
"GetShapeF0D",
|
|
"GetShapeF1D",
|
|
"GetSteerableViewMapDensityF1D",
|
|
"GetViewMapGradientNormF0D",
|
|
"GetViewMapGradientNormF1D",
|
|
"GetXF0D",
|
|
"GetXF1D",
|
|
"GetYF0D",
|
|
"GetYF1D",
|
|
"GetZF0D",
|
|
"GetZF1D",
|
|
"IncrementChainingTimeStampF1D",
|
|
"LocalAverageDepthF0D",
|
|
"LocalAverageDepthF1D",
|
|
"MaterialF0D",
|
|
"Normal2DF0D",
|
|
"Normal2DF1D",
|
|
"Orientation2DF1D",
|
|
"Orientation3DF1D",
|
|
"QuantitativeInvisibilityF0D",
|
|
"QuantitativeInvisibilityF1D",
|
|
"ReadCompleteViewMapPixelF0D",
|
|
"ReadMapPixelF0D",
|
|
"ReadSteerableViewMapPixelF0D",
|
|
"ShapeIdF0D",
|
|
"TimeStampF1D",
|
|
"VertexOrientation2DF0D",
|
|
"VertexOrientation3DF0D",
|
|
"ZDiscontinuityF0D",
|
|
"ZDiscontinuityF1D",
|
|
"pyCurvilinearLengthF0D",
|
|
"pyDensityAnisotropyF0D",
|
|
"pyDensityAnisotropyF1D",
|
|
"pyGetInverseProjectedZF1D",
|
|
"pyGetSquareInverseProjectedZF1D",
|
|
"pyInverseCurvature2DAngleF0D",
|
|
"pyViewMapGradientNormF0D",
|
|
"pyViewMapGradientNormF1D",
|
|
"pyViewMapGradientVectorF0D",
|
|
)
|
|
|
|
|
|
# module members
|
|
from _freestyle import (
|
|
ChainingTimeStampF1D,
|
|
Curvature2DAngleF0D,
|
|
Curvature2DAngleF1D,
|
|
CurveNatureF0D,
|
|
CurveNatureF1D,
|
|
DensityF0D,
|
|
DensityF1D,
|
|
GetCompleteViewMapDensityF1D,
|
|
GetCurvilinearAbscissaF0D,
|
|
GetDirectionalViewMapDensityF1D,
|
|
GetOccludeeF0D,
|
|
GetOccludeeF1D,
|
|
GetOccludersF0D,
|
|
GetOccludersF1D,
|
|
GetParameterF0D,
|
|
GetProjectedXF0D,
|
|
GetProjectedXF1D,
|
|
GetProjectedYF0D,
|
|
GetProjectedYF1D,
|
|
GetProjectedZF0D,
|
|
GetProjectedZF1D,
|
|
GetShapeF0D,
|
|
GetShapeF1D,
|
|
GetSteerableViewMapDensityF1D,
|
|
GetViewMapGradientNormF0D,
|
|
GetViewMapGradientNormF1D,
|
|
GetXF0D,
|
|
GetXF1D,
|
|
GetYF0D,
|
|
GetYF1D,
|
|
GetZF0D,
|
|
GetZF1D,
|
|
IncrementChainingTimeStampF1D,
|
|
LocalAverageDepthF0D,
|
|
LocalAverageDepthF1D,
|
|
MaterialF0D,
|
|
Normal2DF0D,
|
|
Normal2DF1D,
|
|
Orientation2DF1D,
|
|
Orientation3DF1D,
|
|
QuantitativeInvisibilityF0D,
|
|
QuantitativeInvisibilityF1D,
|
|
ReadCompleteViewMapPixelF0D,
|
|
ReadMapPixelF0D,
|
|
ReadSteerableViewMapPixelF0D,
|
|
ShapeIdF0D,
|
|
TimeStampF1D,
|
|
VertexOrientation2DF0D,
|
|
VertexOrientation3DF0D,
|
|
ZDiscontinuityF0D,
|
|
ZDiscontinuityF1D,
|
|
)
|
|
|
|
# constructs for function definition in Python
|
|
from freestyle.types import (
|
|
CurvePoint,
|
|
IntegrationType,
|
|
UnaryFunction0DDouble,
|
|
UnaryFunction0DMaterial,
|
|
UnaryFunction0DVec2f,
|
|
UnaryFunction1DDouble,
|
|
)
|
|
from freestyle.utils import ContextFunctions as CF
|
|
from freestyle.utils import integrate
|
|
|
|
from mathutils import Vector
|
|
|
|
# -- Functions for 0D elements (vertices) -- #
|
|
|
|
|
|
class CurveMaterialF0D(UnaryFunction0DMaterial):
|
|
"""
|
|
A replacement of the built-in MaterialF0D for stroke creation.
|
|
MaterialF0D does not work with Curves and Strokes. Line color
|
|
priority is used to pick one of the two materials at material
|
|
boundaries.
|
|
|
|
Notes: expects instances of CurvePoint to be iterated over
|
|
can return None if no fedge can be found
|
|
"""
|
|
def __call__(self, inter):
|
|
fe = inter.object.fedge
|
|
if fe is None:
|
|
return None
|
|
if fe.is_smooth:
|
|
return fe.material
|
|
else:
|
|
right, left = fe.material_right, fe.material_left
|
|
return right if (right.priority > left.priority) else left
|
|
|
|
|
|
class pyInverseCurvature2DAngleF0D(UnaryFunction0DDouble):
|
|
def __call__(self, inter):
|
|
func = Curvature2DAngleF0D()
|
|
c = func(inter)
|
|
return (3.1415 - c)
|
|
|
|
|
|
class pyCurvilinearLengthF0D(UnaryFunction0DDouble):
|
|
def __call__(self, inter):
|
|
cp = inter.object
|
|
assert(isinstance(cp, CurvePoint))
|
|
return cp.t2d
|
|
|
|
|
|
class pyDensityAnisotropyF0D(UnaryFunction0DDouble):
|
|
"""Estimates the anisotropy of density."""
|
|
def __init__(self, level):
|
|
UnaryFunction0DDouble.__init__(self)
|
|
self.IsoDensity = ReadCompleteViewMapPixelF0D(level)
|
|
self.d0Density = ReadSteerableViewMapPixelF0D(0, level)
|
|
self.d1Density = ReadSteerableViewMapPixelF0D(1, level)
|
|
self.d2Density = ReadSteerableViewMapPixelF0D(2, level)
|
|
self.d3Density = ReadSteerableViewMapPixelF0D(3, level)
|
|
|
|
def __call__(self, inter):
|
|
c_iso = self.IsoDensity(inter)
|
|
c_0 = self.d0Density(inter)
|
|
c_1 = self.d1Density(inter)
|
|
c_2 = self.d2Density(inter)
|
|
c_3 = self.d3Density(inter)
|
|
cMax = max(max(c_0, c_1), max(c_2, c_3))
|
|
cMin = min(min(c_0, c_1), min(c_2, c_3))
|
|
return 0 if (c_iso == 0) else (cMax - cMin) / c_iso
|
|
|
|
|
|
class pyViewMapGradientVectorF0D(UnaryFunction0DVec2f):
|
|
"""
|
|
Returns the gradient vector for a pixel.
|
|
|
|
.. method:: __init__(self, level)
|
|
|
|
Builds a pyViewMapGradientVectorF0D object.
|
|
|
|
:arg level: the level at which to compute the gradient
|
|
:type level: int
|
|
"""
|
|
def __init__(self, level):
|
|
UnaryFunction0DVec2f.__init__(self)
|
|
self._l = level
|
|
self._step = pow(2, self._l)
|
|
|
|
def __call__(self, iter):
|
|
p = iter.object.point_2d
|
|
gx = CF.read_complete_view_map_pixel(self._l, int(p.x + self._step), int(p.y)) - \
|
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
|
gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y + self._step)) - \
|
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
|
return Vector((gx, gy))
|
|
|
|
|
|
class pyViewMapGradientNormF0D(UnaryFunction0DDouble):
|
|
def __init__(self, l):
|
|
UnaryFunction0DDouble.__init__(self)
|
|
self._l = l
|
|
self._step = pow(2, self._l)
|
|
|
|
def __call__(self, iter):
|
|
p = iter.object.point_2d
|
|
gx = CF.read_complete_view_map_pixel(self._l, int(p.x + self._step), int(p.y)) - \
|
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
|
gy = CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y + self._step)) - \
|
|
CF.read_complete_view_map_pixel(self._l, int(p.x), int(p.y))
|
|
return Vector((gx, gy)).length
|
|
|
|
# -- Functions for 1D elements (curves) -- #
|
|
|
|
|
|
class pyGetInverseProjectedZF1D(UnaryFunction1DDouble):
|
|
def __call__(self, inter):
|
|
func = GetProjectedZF1D()
|
|
z = func(inter)
|
|
return (1.0 - z)
|
|
|
|
|
|
class pyGetSquareInverseProjectedZF1D(UnaryFunction1DDouble):
|
|
def __call__(self, inter):
|
|
func = GetProjectedZF1D()
|
|
z = func(inter)
|
|
return (1.0 - pow(z, 2))
|
|
|
|
|
|
class pyDensityAnisotropyF1D(UnaryFunction1DDouble):
|
|
def __init__(self, level, integrationType=IntegrationType.MEAN, sampling=2.0):
|
|
UnaryFunction1DDouble.__init__(self, integrationType)
|
|
self._func = pyDensityAnisotropyF0D(level)
|
|
self._integration = integrationType
|
|
self._sampling = sampling
|
|
|
|
def __call__(self, inter):
|
|
v = integrate(self._func, inter.points_begin(self._sampling), inter.points_end(self._sampling), self._integration)
|
|
return v
|
|
|
|
|
|
class pyViewMapGradientNormF1D(UnaryFunction1DDouble):
|
|
def __init__(self, l, integrationType, sampling=2.0):
|
|
UnaryFunction1DDouble.__init__(self, integrationType)
|
|
self._func = pyViewMapGradientNormF0D(l)
|
|
self._integration = integrationType
|
|
self._sampling = sampling
|
|
|
|
def __call__(self, inter):
|
|
v = integrate(self._func, inter.points_begin(self._sampling), inter.points_end(self._sampling), self._integration)
|
|
return v
|