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@blender.org
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to