[Bf-blender-cvs] [3ca0870] master: Improvements to the Freestyle Python API (needed by the SVG Exporter)

Tamito KAJIYAMA rd6t-kjym at asahi-net.or.jp
Sun May 31 16:39:39 CEST 2015


For the record and a proper acknowledgement of contributions, the patch 
was authored by Folkert de Vries (flokkievids).  This information got 
lost somehow in the middle of doing 'arc patch' and 'git merge & commit'.

-- 
KAJIYAMA, Tamito <rd6t-kjym at asahi-net.or.jp>


On 31/05/2015 23:17, Tamito Kajiyama wrote:
> Commit: 3ca0870023bb71bc929925a8fc8d172c09df710f
> Author: Tamito Kajiyama
> Date:   Sun May 31 17:46:58 2015 +0900
> Branches: master
> https://developer.blender.org/rB3ca0870023bb71bc929925a8fc8d172c09df710f
>
> Improvements to the Freestyle Python API (needed by the SVG Exporter)
>
> 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
>
> ===================================================================
>
> M	release/scripts/freestyle/modules/freestyle/functions.py
> M	release/scripts/freestyle/modules/freestyle/predicates.py
> M	release/scripts/freestyle/modules/freestyle/shaders.py
> M	release/scripts/freestyle/modules/freestyle/utils.py
> M	source/blender/freestyle/intern/python/BPy_FrsMaterial.cpp
> M	source/blender/freestyle/intern/python/Interface0D/BPy_CurvePoint.cpp
>
> ===================================================================
>
> diff --git a/release/scripts/freestyle/modules/freestyle/functions.py b/release/scripts/freestyle/modules/freestyle/functions.py
> index 48d9b2e..426d344 100644
> --- a/release/scripts/freestyle/modules/freestyle/functions.py
> +++ b/release/scripts/freestyle/modules/freestyle/functions.py
> @@ -189,11 +189,13 @@ class CurveMaterialF0D(UnaryFunction0DMaterial):
>       priority is used to pick one of the two materials at material
>       boundaries.
>
> -    Note: expects instances of CurvePoint to be iterated over
> +    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
> -        assert(fe is not None), "CurveMaterialF0D: fe is None"
> +        if fe is None:
> +            return None
>           if fe.is_smooth:
>               return fe.material
>           else:
> diff --git a/release/scripts/freestyle/modules/freestyle/predicates.py b/release/scripts/freestyle/modules/freestyle/predicates.py
> index 2439cb0..5cbe577 100644
> --- a/release/scripts/freestyle/modules/freestyle/predicates.py
> +++ b/release/scripts/freestyle/modules/freestyle/predicates.py
> @@ -43,6 +43,7 @@ __all__ = (
>       "FalseUP0D",
>       "FalseUP1D",
>       "Length2DBP1D",
> +    "MaterialBP1D",
>       "NotBP1D",
>       "NotUP1D",
>       "ObjectNamesUP1D",
> @@ -150,12 +151,13 @@ from freestyle.functions import (
>       pyViewMapGradientNormF1D,
>       )
>
> +from freestyle.utils import material_from_fedge
> +
>   import random
>
>
>   # -- Unary predicates for 0D elements (vertices) -- #
>
> -
>   class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
>       def __init__(self, a):
>           UnaryPredicate0D.__init__(self)
> @@ -234,9 +236,10 @@ class AndUP1D(UnaryPredicate1D):
>       def __init__(self, *predicates):
>           UnaryPredicate1D.__init__(self)
>           self.predicates = predicates
> -        # there are cases in which only one predicate is supplied (in the parameter editor)
> -        if len(self.predicates) < 1:
> -            raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates))
> +        correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates)
> +        if not (correct_types and predicates):
> +            raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" %
> +                    (self.__class__.__name__, self.predicates))
>
>       def __call__(self, inter):
>           return all(pred(inter) for pred in self.predicates)
> @@ -246,9 +249,10 @@ class OrUP1D(UnaryPredicate1D):
>       def __init__(self, *predicates):
>           UnaryPredicate1D.__init__(self)
>           self.predicates = predicates
> -        # there are cases in which only one predicate is supplied (in the parameter editor)
> -        if len(self.predicates) < 1:
> -            raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates))
> +        correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates)
> +        if not (correct_types and predicates):
> +            raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" %
> +                    (self.__class__.__name__, self.predicates))
>
>       def __call__(self, inter):
>           return any(pred(inter) for pred in self.predicates)
> @@ -257,10 +261,10 @@ class OrUP1D(UnaryPredicate1D):
>   class NotUP1D(UnaryPredicate1D):
>       def __init__(self, pred):
>           UnaryPredicate1D.__init__(self)
> -        self.__pred = pred
> +        self.predicate = pred
>
>       def __call__(self, inter):
> -        return not self.__pred(inter)
> +        return not self.predicate(inter)
>
>
>   class ObjectNamesUP1D(UnaryPredicate1D):
> @@ -563,32 +567,36 @@ class pyClosedCurveUP1D(UnaryPredicate1D):
>   class AndBP1D(BinaryPredicate1D):
>       def __init__(self, *predicates):
>           BinaryPredicate1D.__init__(self)
> -        self._predicates = predicates
> -        if len(predicates) < 2:
> -            raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates))
> +        self.predicates = tuple(predicates)
> +        correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates)
> +        if not (correct_types and predicates):
> +            raise TypeError("%s: Expected one or more BinaryPredicate1D, got %r" %
> +                    (self.__class__.__name__, self.predicates))
>
>       def __call__(self, i1, i2):
> -        return all(pred(i1, i2) for pred in self._predicates)
> +        return all(pred(i1, i2) for pred in self.predicates)
>
>
>   class OrBP1D(BinaryPredicate1D):
>       def __init__(self, *predicates):
>           BinaryPredicate1D.__init__(self)
> -        self._predicates = predicates
> -        if len(predicates) < 2:
> -            raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates))
> +        self.predicates = tuple(predicates)
> +        correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates)
> +        if not (correct_types and predicates):
> +            raise TypeError("%s: Expected one or more BinaryPredicate1D, got %r" %
> +                    (self.__class__.__name__, self.predicates))
>
>       def __call__(self, i1, i2):
> -        return any(pred(i1, i2) for pred in self._predicates)
> +        return any(pred(i1, i2) for pred in self.predicates)
>
>
>   class NotBP1D(BinaryPredicate1D):
>       def __init__(self, predicate):
>           BinaryPredicate1D.__init__(self)
> -        self._predicate = predicate
> +        self.predicate = predicate
>
>       def __call__(self, i1, i2):
> -        return (not self._predicate(i1, i2))
> +        return (not self.predicate(i1, i2))
>
>
>   class pyZBP1D(BinaryPredicate1D):
> @@ -663,3 +671,10 @@ class pyShuffleBP1D(BinaryPredicate1D):
>
>       def __call__(self, inter1, inter2):
>           return (random.uniform(0, 1) < random.uniform(0, 1))
> +
> +class MaterialBP1D(BinaryPredicate1D):
> +    """Checks whether the two supplied ViewEdges have the same material."""
> +    def __call__(self, i1, i2):
> +        fedges = (fe for ve in (i1, i2) for fe in (ve.first_fedge, ve.last_fedge))
> +        materials = {material_from_fedge(fe) for fe in fedges}
> +        return len(materials) < 2
> diff --git a/release/scripts/freestyle/modules/freestyle/shaders.py b/release/scripts/freestyle/modules/freestyle/shaders.py
> index 61365e8..127db3f 100644
> --- a/release/scripts/freestyle/modules/freestyle/shaders.py
> +++ b/release/scripts/freestyle/modules/freestyle/shaders.py
> @@ -138,7 +138,7 @@ from freestyle.predicates import (
>
>   from freestyle.utils import (
>       bound,
> -    bounding_box,
> +    BoundingBox,
>       phase_to_direction,
>       )
>
> @@ -865,7 +865,7 @@ class pyBluePrintCirclesShader(StrokeShader):
>
>       def shade(self, stroke):
>           # get minimum and maximum coordinates
> -        p_min, p_max = bounding_box(stroke)
> +        p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners
>
>           stroke.resample(32 * self.__turns)
>           sv_nb = len(stroke) // self.__turns
> @@ -917,7 +917,7 @@ class pyBluePrintEllipsesShader(StrokeShader):
>           self.__random_radius = random_radius
>
>       def shade(self, stroke):
> -        p_min, p_max = bounding_box(stroke)
> +        p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners
>
>           stroke.resample(32 * self.__turns)
>           sv_nb = len(stroke) // self.__turns
> @@ -964,7 +964,7 @@ class pyBluePrintSquaresShader(StrokeShader):
>               return
>
>           # get minimum and maximum coordinates
> -        p_min, p_max = bounding_box(stroke)
> +        p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners
>
>           stroke.resample(32 * self.__turns)
>           num_segments = len(stroke) // self.__turns
> diff --git a/release/scripts/freestyle/modules/freestyle/utils.py b/release/scripts/freestyle/modules/freestyle/utils.py
> index 224734d..41d2297 100644
> --- a/release/scripts/freestyle/modules/freestyle/utils.py
> +++ b/release/scripts/freestyle/modules/freestyle/utils.py
> @@ -22,24 +22,29 @@ writing.
>   """
>
>   __all__ = (
> -    "ContextFunctions",
>       "bound",
> -    "bounding_box",
> +    "BoundingBox",
> +    "ContextFunctions",
>       "find_matching_vertex",
> -    "getCurrentScene",
>       "get_chain_length",
> +    "get_object_name",
> +    "get_strokes",
>       "get_test_stroke",
> +    "getCurrentScene",
>       "integrate",
> +    "is_poly_clockwise",
>       "iter_distance_along_stroke",
>       "iter_distance_from_camera",
>       "iter_distance_from_object",
>       "iter_material_value",
>       "iter_t2d_along_stroke",
> +    "material_from_fedge",
>       "pairwise",
>       "phase_to_direction",
>       "rgb_to_bw",
>       "stroke_curvature",
>       "stroke_normal",
> +    "StrokeCollector",
>       "tripplewise",
>       )
>
> @@ -55,6 +60,7 @@ from _freestyle import (
>   from freestyle.types import (
>       Interface0DIterator,
>       Stroke,
> +    StrokeShader,
>       StrokeVertexIterator,
>       )
>
> @@ -79,12 +85,38 @@ def bound(lower, x, higher):
>       return (lower if x <= lower else higher if x >= higher else x)
>
>
> -def bounding_box(stroke):
> -    """
> -    Returns the maximum and minimum coordinates (the bounding box) of the stroke's vertices
> -    """
> -    x, y = zip(*(svert.point for svert in stroke))
> -    return (Vector((min(x), min(y))), Vector((max(x), max(y))))
> +def get_strokes():
> +    """Get all strokes that are currently available"""
> +    return tuple(map(Operators().get_stroke_from_index, range(Operators().get_strokes_size())))
> +
> +
> +def is_poly_clockwise(stroke):
> +    """True if the stroke is orientated in a clockwise way, False otherwise"""
> +    v = sum((v2.point.x - v1.point.x) * (v1.point.y + v2.point.y) for v1, v2 in pairwise(stroke))
> +    v1, v2 = stroke[0], stroke[-1]
> +    if (v1.point - v2.point).length > 1e-3:
> +        v += (v2.point.x - v1.point.x) * (v1.point.y + v2.point.y)
> +    return v > 0
> +
> +
> +def get_object_name(stroke):
> +    """Returns the name of the object that this stroke is drawn on."""
> +    fedge = stroke[0].fedge
> +    if fedge is None:
> +        return None
> +    return fedge.viewedge.viewshape.name
> +
> +
> +def material_from_fedge(fe):
> +    "get the diffuse rgba color from an FEdge"
> +    if fe is None:
> +        return None
> +    if fe.is_smooth:
> +        material = fe.material
> +    else:
> +        right, left = fe.material_right, fe.material_left
> +        material = right if (right.priorit
>
> @@ Diff output truncated at 10240 characters. @@
>
> _______________________________________________
> Bf-blender-cvs mailing list
> Bf-blender-cvs at blender.org
> http://lists.blender.org/mailman/listinfo/bf-blender-cvs
>



More information about the Bf-blender-cvs mailing list