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
This commit is contained in:
@@ -189,11 +189,13 @@ class CurveMaterialF0D(UnaryFunction0DMaterial):
|
|||||||
priority is used to pick one of the two materials at material
|
priority is used to pick one of the two materials at material
|
||||||
boundaries.
|
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):
|
def __call__(self, inter):
|
||||||
fe = inter.object.fedge
|
fe = inter.object.fedge
|
||||||
assert(fe is not None), "CurveMaterialF0D: fe is None"
|
if fe is None:
|
||||||
|
return None
|
||||||
if fe.is_smooth:
|
if fe.is_smooth:
|
||||||
return fe.material
|
return fe.material
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ __all__ = (
|
|||||||
"FalseUP0D",
|
"FalseUP0D",
|
||||||
"FalseUP1D",
|
"FalseUP1D",
|
||||||
"Length2DBP1D",
|
"Length2DBP1D",
|
||||||
|
"MaterialBP1D",
|
||||||
"NotBP1D",
|
"NotBP1D",
|
||||||
"NotUP1D",
|
"NotUP1D",
|
||||||
"ObjectNamesUP1D",
|
"ObjectNamesUP1D",
|
||||||
@@ -150,12 +151,13 @@ from freestyle.functions import (
|
|||||||
pyViewMapGradientNormF1D,
|
pyViewMapGradientNormF1D,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from freestyle.utils import material_from_fedge
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
|
|
||||||
# -- Unary predicates for 0D elements (vertices) -- #
|
# -- Unary predicates for 0D elements (vertices) -- #
|
||||||
|
|
||||||
|
|
||||||
class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
|
class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D):
|
||||||
def __init__(self, a):
|
def __init__(self, a):
|
||||||
UnaryPredicate0D.__init__(self)
|
UnaryPredicate0D.__init__(self)
|
||||||
@@ -234,9 +236,10 @@ class AndUP1D(UnaryPredicate1D):
|
|||||||
def __init__(self, *predicates):
|
def __init__(self, *predicates):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self.predicates = predicates
|
self.predicates = predicates
|
||||||
# there are cases in which only one predicate is supplied (in the parameter editor)
|
correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates)
|
||||||
if len(self.predicates) < 1:
|
if not (correct_types and predicates):
|
||||||
raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates))
|
raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" %
|
||||||
|
(self.__class__.__name__, self.predicates))
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
return all(pred(inter) for pred in self.predicates)
|
return all(pred(inter) for pred in self.predicates)
|
||||||
@@ -246,9 +249,10 @@ class OrUP1D(UnaryPredicate1D):
|
|||||||
def __init__(self, *predicates):
|
def __init__(self, *predicates):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self.predicates = predicates
|
self.predicates = predicates
|
||||||
# there are cases in which only one predicate is supplied (in the parameter editor)
|
correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates)
|
||||||
if len(self.predicates) < 1:
|
if not (correct_types and predicates):
|
||||||
raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates))
|
raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" %
|
||||||
|
(self.__class__.__name__, self.predicates))
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
return any(pred(inter) for pred in self.predicates)
|
return any(pred(inter) for pred in self.predicates)
|
||||||
@@ -257,10 +261,10 @@ class OrUP1D(UnaryPredicate1D):
|
|||||||
class NotUP1D(UnaryPredicate1D):
|
class NotUP1D(UnaryPredicate1D):
|
||||||
def __init__(self, pred):
|
def __init__(self, pred):
|
||||||
UnaryPredicate1D.__init__(self)
|
UnaryPredicate1D.__init__(self)
|
||||||
self.__pred = pred
|
self.predicate = pred
|
||||||
|
|
||||||
def __call__(self, inter):
|
def __call__(self, inter):
|
||||||
return not self.__pred(inter)
|
return not self.predicate(inter)
|
||||||
|
|
||||||
|
|
||||||
class ObjectNamesUP1D(UnaryPredicate1D):
|
class ObjectNamesUP1D(UnaryPredicate1D):
|
||||||
@@ -563,32 +567,36 @@ class pyClosedCurveUP1D(UnaryPredicate1D):
|
|||||||
class AndBP1D(BinaryPredicate1D):
|
class AndBP1D(BinaryPredicate1D):
|
||||||
def __init__(self, *predicates):
|
def __init__(self, *predicates):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
self._predicates = predicates
|
self.predicates = tuple(predicates)
|
||||||
if len(predicates) < 2:
|
correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates)
|
||||||
raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates))
|
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):
|
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):
|
class OrBP1D(BinaryPredicate1D):
|
||||||
def __init__(self, *predicates):
|
def __init__(self, *predicates):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
self._predicates = predicates
|
self.predicates = tuple(predicates)
|
||||||
if len(predicates) < 2:
|
correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates)
|
||||||
raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates))
|
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):
|
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):
|
class NotBP1D(BinaryPredicate1D):
|
||||||
def __init__(self, predicate):
|
def __init__(self, predicate):
|
||||||
BinaryPredicate1D.__init__(self)
|
BinaryPredicate1D.__init__(self)
|
||||||
self._predicate = predicate
|
self.predicate = predicate
|
||||||
|
|
||||||
def __call__(self, i1, i2):
|
def __call__(self, i1, i2):
|
||||||
return (not self._predicate(i1, i2))
|
return (not self.predicate(i1, i2))
|
||||||
|
|
||||||
|
|
||||||
class pyZBP1D(BinaryPredicate1D):
|
class pyZBP1D(BinaryPredicate1D):
|
||||||
@@ -663,3 +671,10 @@ class pyShuffleBP1D(BinaryPredicate1D):
|
|||||||
|
|
||||||
def __call__(self, inter1, inter2):
|
def __call__(self, inter1, inter2):
|
||||||
return (random.uniform(0, 1) < random.uniform(0, 1))
|
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
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ from freestyle.predicates import (
|
|||||||
|
|
||||||
from freestyle.utils import (
|
from freestyle.utils import (
|
||||||
bound,
|
bound,
|
||||||
bounding_box,
|
BoundingBox,
|
||||||
phase_to_direction,
|
phase_to_direction,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -865,7 +865,7 @@ class pyBluePrintCirclesShader(StrokeShader):
|
|||||||
|
|
||||||
def shade(self, stroke):
|
def shade(self, stroke):
|
||||||
# get minimum and maximum coordinates
|
# 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)
|
stroke.resample(32 * self.__turns)
|
||||||
sv_nb = len(stroke) // self.__turns
|
sv_nb = len(stroke) // self.__turns
|
||||||
@@ -917,7 +917,7 @@ class pyBluePrintEllipsesShader(StrokeShader):
|
|||||||
self.__random_radius = random_radius
|
self.__random_radius = random_radius
|
||||||
|
|
||||||
def shade(self, stroke):
|
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)
|
stroke.resample(32 * self.__turns)
|
||||||
sv_nb = len(stroke) // self.__turns
|
sv_nb = len(stroke) // self.__turns
|
||||||
@@ -964,7 +964,7 @@ class pyBluePrintSquaresShader(StrokeShader):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# get minimum and maximum coordinates
|
# 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)
|
stroke.resample(32 * self.__turns)
|
||||||
num_segments = len(stroke) // self.__turns
|
num_segments = len(stroke) // self.__turns
|
||||||
|
|||||||
@@ -22,24 +22,29 @@ writing.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
"ContextFunctions",
|
|
||||||
"bound",
|
"bound",
|
||||||
"bounding_box",
|
"BoundingBox",
|
||||||
|
"ContextFunctions",
|
||||||
"find_matching_vertex",
|
"find_matching_vertex",
|
||||||
"getCurrentScene",
|
|
||||||
"get_chain_length",
|
"get_chain_length",
|
||||||
|
"get_object_name",
|
||||||
|
"get_strokes",
|
||||||
"get_test_stroke",
|
"get_test_stroke",
|
||||||
|
"getCurrentScene",
|
||||||
"integrate",
|
"integrate",
|
||||||
|
"is_poly_clockwise",
|
||||||
"iter_distance_along_stroke",
|
"iter_distance_along_stroke",
|
||||||
"iter_distance_from_camera",
|
"iter_distance_from_camera",
|
||||||
"iter_distance_from_object",
|
"iter_distance_from_object",
|
||||||
"iter_material_value",
|
"iter_material_value",
|
||||||
"iter_t2d_along_stroke",
|
"iter_t2d_along_stroke",
|
||||||
|
"material_from_fedge",
|
||||||
"pairwise",
|
"pairwise",
|
||||||
"phase_to_direction",
|
"phase_to_direction",
|
||||||
"rgb_to_bw",
|
"rgb_to_bw",
|
||||||
"stroke_curvature",
|
"stroke_curvature",
|
||||||
"stroke_normal",
|
"stroke_normal",
|
||||||
|
"StrokeCollector",
|
||||||
"tripplewise",
|
"tripplewise",
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,6 +60,7 @@ from _freestyle import (
|
|||||||
from freestyle.types import (
|
from freestyle.types import (
|
||||||
Interface0DIterator,
|
Interface0DIterator,
|
||||||
Stroke,
|
Stroke,
|
||||||
|
StrokeShader,
|
||||||
StrokeVertexIterator,
|
StrokeVertexIterator,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -79,12 +85,38 @@ def bound(lower, x, higher):
|
|||||||
return (lower if x <= lower else higher if x >= higher else x)
|
return (lower if x <= lower else higher if x >= higher else x)
|
||||||
|
|
||||||
|
|
||||||
def bounding_box(stroke):
|
def get_strokes():
|
||||||
"""
|
"""Get all strokes that are currently available"""
|
||||||
Returns the maximum and minimum coordinates (the bounding box) of the stroke's vertices
|
return tuple(map(Operators().get_stroke_from_index, range(Operators().get_strokes_size())))
|
||||||
"""
|
|
||||||
x, y = zip(*(svert.point for svert in stroke))
|
|
||||||
return (Vector((min(x), min(y))), Vector((max(x), max(y))))
|
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.priority > left.priority) else left
|
||||||
|
return material
|
||||||
|
|
||||||
# -- General helper functions -- #
|
# -- General helper functions -- #
|
||||||
|
|
||||||
@@ -106,6 +138,54 @@ def phase_to_direction(length):
|
|||||||
# lower bound (e.g., thickness, range and certain values)
|
# lower bound (e.g., thickness, range and certain values)
|
||||||
BoundedProperty = namedtuple("BoundedProperty", ["min", "max", "delta"])
|
BoundedProperty = namedtuple("BoundedProperty", ["min", "max", "delta"])
|
||||||
|
|
||||||
|
|
||||||
|
class BoundingBox:
|
||||||
|
"""Object representing a bounding box consisting out of 2 2D vectors"""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
"minimum",
|
||||||
|
"maximum",
|
||||||
|
"size",
|
||||||
|
"corners",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, minimum: Vector, maximum: Vector):
|
||||||
|
self.minimum = minimum
|
||||||
|
self.maximum = maximum
|
||||||
|
if len(minimum) != len(maximum):
|
||||||
|
raise TypeError("Expected two vectors of size 2, got", minimum, maximum)
|
||||||
|
self.size = len(minimum)
|
||||||
|
self.corners = (minimum, maximum)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "BoundingBox(%r, %r)" % (self.minimum, self.maximum)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_sequence(cls, sequence):
|
||||||
|
"""BoundingBox from sequence of 2D or 3D Vector objects"""
|
||||||
|
x, y = zip(*sequence)
|
||||||
|
mini = Vector((min(x), min(y)))
|
||||||
|
maxi = Vector((max(x), max(y)))
|
||||||
|
return cls(mini, maxi)
|
||||||
|
|
||||||
|
def inside(self, other):
|
||||||
|
"""True if self inside other, False otherwise"""
|
||||||
|
if self.size != other.size:
|
||||||
|
raise TypeError("Expected two BoundingBox of the same size, got", self, other)
|
||||||
|
return (self.minimum.x >= other.minimum.x and self.minimum.y >= other.minimum.y and
|
||||||
|
self.maximum.x <= other.maximum.x and self.maximum.y <= other.maximum.y)
|
||||||
|
|
||||||
|
|
||||||
|
class StrokeCollector(StrokeShader):
|
||||||
|
"Collects and Stores stroke objects"
|
||||||
|
def __init__(self):
|
||||||
|
StrokeShader.__init__(self)
|
||||||
|
self.strokes = []
|
||||||
|
|
||||||
|
def shade(self, stroke):
|
||||||
|
self.strokes.append(stroke)
|
||||||
|
|
||||||
|
|
||||||
# -- helper functions for chaining -- #
|
# -- helper functions for chaining -- #
|
||||||
|
|
||||||
def get_chain_length(ve, orientation):
|
def get_chain_length(ve, orientation):
|
||||||
@@ -147,6 +227,7 @@ def find_matching_vertex(id, it):
|
|||||||
"""Finds the matching vertex, or returns None."""
|
"""Finds the matching vertex, or returns None."""
|
||||||
return next((ve for ve in it if ve.id == id), None)
|
return next((ve for ve in it if ve.id == id), None)
|
||||||
|
|
||||||
|
|
||||||
# -- helper functions for iterating -- #
|
# -- helper functions for iterating -- #
|
||||||
|
|
||||||
def pairwise(iterable, types={Stroke, StrokeVertexIterator}):
|
def pairwise(iterable, types={Stroke, StrokeVertexIterator}):
|
||||||
@@ -210,7 +291,7 @@ def iter_distance_from_object(stroke, location, range_min, range_max, normfac):
|
|||||||
|
|
||||||
|
|
||||||
def iter_material_value(stroke, func, attribute):
|
def iter_material_value(stroke, func, attribute):
|
||||||
"Yields a specific material attribute from the vertex' underlying material."
|
"""Yields a specific material attribute from the vertex' underlying material."""
|
||||||
it = Interface0DIterator(stroke)
|
it = Interface0DIterator(stroke)
|
||||||
for svert in it:
|
for svert in it:
|
||||||
material = func(it)
|
material = func(it)
|
||||||
@@ -252,8 +333,9 @@ def iter_material_value(stroke, func, attribute):
|
|||||||
raise ValueError("unexpected material attribute: " + attribute)
|
raise ValueError("unexpected material attribute: " + attribute)
|
||||||
yield (svert, value)
|
yield (svert, value)
|
||||||
|
|
||||||
|
|
||||||
def iter_distance_along_stroke(stroke):
|
def iter_distance_along_stroke(stroke):
|
||||||
"Yields the absolute distance along the stroke up to the current vertex."
|
"""Yields the absolute distance along the stroke up to the current vertex."""
|
||||||
distance = 0.0
|
distance = 0.0
|
||||||
# the positions need to be copied, because they are changed in the calling function
|
# the positions need to be copied, because they are changed in the calling function
|
||||||
points = tuple(svert.point.copy() for svert in stroke)
|
points = tuple(svert.point.copy() for svert in stroke)
|
||||||
@@ -295,6 +377,7 @@ def stroke_curvature(it):
|
|||||||
|
|
||||||
yield abs(K)
|
yield abs(K)
|
||||||
|
|
||||||
|
|
||||||
def stroke_normal(stroke):
|
def stroke_normal(stroke):
|
||||||
"""
|
"""
|
||||||
Compute the 2D normal at the stroke vertex pointed by the iterator
|
Compute the 2D normal at the stroke vertex pointed by the iterator
|
||||||
@@ -323,6 +406,7 @@ def stroke_normal(stroke):
|
|||||||
n2 = Vector((e2[1], -e2[0])).normalized()
|
n2 = Vector((e2[1], -e2[0])).normalized()
|
||||||
yield (n1 + n2).normalized()
|
yield (n1 + n2).normalized()
|
||||||
|
|
||||||
|
|
||||||
def get_test_stroke():
|
def get_test_stroke():
|
||||||
"""Returns a static stroke object for testing """
|
"""Returns a static stroke object for testing """
|
||||||
from freestyle.types import Stroke, Interface0DIterator, StrokeVertexIterator, SVertex, Id, StrokeVertex
|
from freestyle.types import Stroke, Interface0DIterator, StrokeVertexIterator, SVertex, Id, StrokeVertex
|
||||||
|
|||||||
@@ -30,6 +30,9 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "BLI_hash_mm2a.h"
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
//-------------------MODULE INITIALIZATION--------------------------------
|
//-------------------MODULE INITIALIZATION--------------------------------
|
||||||
@@ -478,6 +481,48 @@ static PyGetSetDef BPy_FrsMaterial_getseters[] = {
|
|||||||
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
|
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static PyObject *BPy_FrsMaterial_richcmpr(PyObject *objectA, PyObject *objectB, int comparison_type)
|
||||||
|
{
|
||||||
|
const BPy_FrsMaterial *matA = NULL, *matB = NULL;
|
||||||
|
bool result = 0;
|
||||||
|
|
||||||
|
if (!BPy_FrsMaterial_Check(objectA) || !BPy_FrsMaterial_Check(objectB)) {
|
||||||
|
if (comparison_type == Py_NE) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matA = (BPy_FrsMaterial *)objectA;
|
||||||
|
matB = (BPy_FrsMaterial *)objectB;
|
||||||
|
|
||||||
|
switch (comparison_type) {
|
||||||
|
case Py_NE:
|
||||||
|
result = (*matA->m) != (*matB->m);
|
||||||
|
break;
|
||||||
|
case Py_EQ:
|
||||||
|
result = (*matA->m) == (*matB->m);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_SetString(PyExc_TypeError, "Material does not support this comparison type");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result == true) {
|
||||||
|
Py_RETURN_TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Py_RETURN_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Py_hash_t FrsMaterial_hash(PyObject *self)
|
||||||
|
{
|
||||||
|
return (Py_uhash_t)BLI_hash_mm2((const unsigned char *)self, sizeof(*self), 0);
|
||||||
|
}
|
||||||
/*-----------------------BPy_FrsMaterial type definition ------------------------------*/
|
/*-----------------------BPy_FrsMaterial type definition ------------------------------*/
|
||||||
|
|
||||||
PyTypeObject FrsMaterial_Type = {
|
PyTypeObject FrsMaterial_Type = {
|
||||||
@@ -494,7 +539,7 @@ PyTypeObject FrsMaterial_Type = {
|
|||||||
0, /* tp_as_number */
|
0, /* tp_as_number */
|
||||||
0, /* tp_as_sequence */
|
0, /* tp_as_sequence */
|
||||||
0, /* tp_as_mapping */
|
0, /* tp_as_mapping */
|
||||||
0, /* tp_hash */
|
(hashfunc)FrsMaterial_hash, /* tp_hash */
|
||||||
0, /* tp_call */
|
0, /* tp_call */
|
||||||
0, /* tp_str */
|
0, /* tp_str */
|
||||||
0, /* tp_getattro */
|
0, /* tp_getattro */
|
||||||
@@ -504,7 +549,7 @@ PyTypeObject FrsMaterial_Type = {
|
|||||||
FrsMaterial_doc, /* tp_doc */
|
FrsMaterial_doc, /* tp_doc */
|
||||||
0, /* tp_traverse */
|
0, /* tp_traverse */
|
||||||
0, /* tp_clear */
|
0, /* tp_clear */
|
||||||
0, /* tp_richcompare */
|
(richcmpfunc)BPy_FrsMaterial_richcmpr, /* tp_richcompare */
|
||||||
0, /* tp_weaklistoffset */
|
0, /* tp_weaklistoffset */
|
||||||
0, /* tp_iter */
|
0, /* tp_iter */
|
||||||
0, /* tp_iternext */
|
0, /* tp_iternext */
|
||||||
|
|||||||
@@ -188,7 +188,10 @@ static PyObject *CurvePoint_fedge_get(BPy_CurvePoint *self, void *UNUSED(closure
|
|||||||
{
|
{
|
||||||
SVertex *A = self->cp->A();
|
SVertex *A = self->cp->A();
|
||||||
Interface0D *B = (Interface0D *)self->cp->B();
|
Interface0D *B = (Interface0D *)self->cp->B();
|
||||||
return Any_BPy_Interface1D_from_Interface1D(*(A->getFEdge(*B)));
|
// B can be NULL under certain circumstances
|
||||||
|
if (B)
|
||||||
|
return Any_BPy_Interface1D_from_Interface1D(*(A->getFEdge(*B)));
|
||||||
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(CurvePoint_t2d_doc,
|
PyDoc_STRVAR(CurvePoint_t2d_doc,
|
||||||
|
|||||||
Reference in New Issue
Block a user